Skip to content

Commit 681fddf

Browse files
committed
migrate to durable object
1 parent 178ed56 commit 681fddf

File tree

14 files changed

+292
-210
lines changed

14 files changed

+292
-210
lines changed

app/lib/db/schema-do.ts

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,26 @@
1-
import { int, sqliteTable, text } from "drizzle-orm/sqlite-core";
1+
import { customType, int, sqliteTable } from "drizzle-orm/sqlite-core";
2+
import type { Hai } from "../hai/utils";
3+
import { haiArray } from "./schema";
24

3-
export const usersTable = sqliteTable("users_table", {
4-
id: int().primaryKey({ autoIncrement: true }),
5-
name: text().notNull(),
6-
age: int().notNull(),
7-
email: text().notNull().unique(),
8-
gender: text().notNull(),
5+
export const hai = customType<{ data: Hai; driverData: string }>({
6+
dataType() {
7+
return "text";
8+
},
9+
toDriver(value: Hai) {
10+
return JSON.stringify(value);
11+
},
12+
fromDriver(value: string) {
13+
return JSON.parse(value) as Hai;
14+
},
15+
});
16+
17+
export const gameState = sqliteTable("game_state", {
18+
// 1行しか使わないので、IDを固定
19+
id: int("id").primaryKey().default(1),
20+
kyoku: int("kyoku").notNull(),
21+
junme: int("junme").notNull(),
22+
haiyama: haiArray("haiyama").notNull(),
23+
sutehai: haiArray("sutehai").notNull(),
24+
tehai: haiArray("tehai").notNull(),
25+
tsumohai: hai("tsumohai"),
926
});

app/lib/db/schema.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import type { string } from "better-auth";
21
import { sql } from "drizzle-orm";
32
import {
43
check,
@@ -11,7 +10,7 @@ import {
1110
} from "drizzle-orm/sqlite-core";
1211
import type { Hai } from "../hai/utils";
1312

14-
const haiArray = customType<{ data: Hai[]; driverData: string }>({
13+
export const haiArray = customType<{ data: Hai[]; driverData: string }>({
1514
dataType() {
1615
return "text";
1716
},

app/lib/do.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import type { Hai } from "./hai/utils";
2+
3+
export interface GameState {
4+
kyoku: number;
5+
junme: number;
6+
haiyama: Hai[];
7+
sutehai: Hai[];
8+
tehai: Hai[];
9+
tsumohai: Hai | null;
10+
}
11+
12+
export default function getDOStub(env: Env, userId: string) {
13+
const id = env.DO.idFromName(userId);
14+
const stub = env.DO.get(id);
15+
return stub;
16+
}

app/lib/redis.ts

Lines changed: 0 additions & 140 deletions
This file was deleted.

app/routes/api.do.ts

Lines changed: 0 additions & 17 deletions
This file was deleted.

app/routes/play.tedashi.ts

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { redirect } from "react-router";
22
import { getAuth } from "~/lib/auth";
3-
import { type GameState, getRedisClient, tedashi } from "~/lib/redis";
3+
import getDOStub from "~/lib/do";
44
import type { Route } from "./+types/play.tedashi";
55

66
export async function action({ context, request }: Route.ActionArgs) {
@@ -20,18 +20,13 @@ export async function action({ context, request }: Route.ActionArgs) {
2020
throw new Response("Invalid index", { status: 400 });
2121
}
2222

23-
const redisClient = getRedisClient(env);
24-
await redisClient.connect();
23+
const stub = getDOStub(env, userId);
2524

2625
try {
27-
await tedashi(redisClient, userId, index);
28-
const gameStateJSON = await redisClient.get(`user:${userId}:game`);
29-
const gameState = gameStateJSON ? JSON.parse(gameStateJSON) : null;
26+
await stub.tedashi(index);
3027

31-
await redisClient.quit();
3228
return redirect("/play");
3329
} catch (error) {
34-
await redisClient.quit();
3530
const errorMessage = error instanceof Error ? error.message : String(error);
3631
throw new Response(errorMessage, { status: 400 });
3732
}

app/routes/play.tsumogiri.ts

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { redirect } from "react-router";
22
import { getAuth } from "~/lib/auth";
3-
import { type GameState, getRedisClient, tsumogiri } from "~/lib/redis";
3+
import getDOStub from "~/lib/do";
44
import type { Route } from "./+types/play.tsumogiri";
55

66
export async function action({ context, request }: Route.ActionArgs) {
@@ -13,18 +13,13 @@ export async function action({ context, request }: Route.ActionArgs) {
1313
}
1414
const userId = session.user.id;
1515

16-
const redisClient = getRedisClient(env);
17-
await redisClient.connect();
16+
const stub = getDOStub(env, userId);
1817

1918
try {
20-
await tsumogiri(redisClient, userId);
21-
const gameStateJSON = await redisClient.get(`user:${userId}:game`);
22-
const gameState = gameStateJSON ? JSON.parse(gameStateJSON) : null;
19+
await stub.tsumogiri();
2320

24-
await redisClient.quit();
2521
return redirect("/play");
2622
} catch (error) {
27-
await redisClient.quit();
2823
const errorMessage = error instanceof Error ? error.message : String(error);
2924
throw new Response(errorMessage, { status: 400 });
3025
}

app/routes/play.tsx

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@ import { Form } from "react-router";
33
import { getAuth } from "~/lib/auth";
44
import { getDB } from "~/lib/db";
55
import { haiyama } from "~/lib/db/schema";
6+
import type { GameState } from "~/lib/do";
7+
import getDOStub from "~/lib/do";
68
import judgeAgari from "~/lib/hai/judgeAgari";
79
import { sortTehai } from "~/lib/hai/utils";
8-
import { type GameState, getRedisClient, init } from "~/lib/redis";
910
import type { Route } from "./+types/play";
1011

1112
export async function loader({
@@ -23,16 +24,14 @@ export async function loader({
2324
const userId = session.user.id;
2425

2526
// Check if game state already exists in Redis
26-
const redisClient = getRedisClient(env);
27-
await redisClient.connect();
27+
const stub = getDOStub(env, userId);
2828

2929
try {
30-
const existingGameState = await redisClient.get(`user:${userId}:game`);
30+
const existingGameState = await stub.getCurrentGameState();
3131

3232
if (existingGameState) {
3333
// Return existing game state from Redis
34-
await redisClient.quit();
35-
return JSON.parse(existingGameState);
34+
return existingGameState;
3635
}
3736

3837
// No existing game state, so initialize from PostgreSQL
@@ -43,22 +42,20 @@ export async function loader({
4342
.limit(1);
4443

4544
if (randomHaiyama.length === 0) {
46-
await redisClient.quit();
4745
throw new Response("No haiyama found", { status: 404 });
4846
}
4947

5048
const haiData = randomHaiyama[0].tiles;
5149
// Initialize game state in Redis
52-
await init(redisClient, userId, haiData);
50+
await stub.init(haiData);
5351

5452
// Get the initialized game state to return
55-
const gameStateJSON = await redisClient.get(`user:${userId}:game`);
56-
const gameState = gameStateJSON ? JSON.parse(gameStateJSON) : null;
57-
58-
await redisClient.quit();
53+
const gameState = await stub.getCurrentGameState();
54+
if (!gameState) {
55+
throw new Error("Failed to get current game state");
56+
}
5957
return gameState;
6058
} catch (error) {
61-
await redisClient.quit();
6259
throw error instanceof Error ? error : new Error(String(error));
6360
}
6461
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
CREATE TABLE `game_state` (
2+
`id` integer PRIMARY KEY DEFAULT 1 NOT NULL,
3+
`kyoku` integer NOT NULL,
4+
`junme` integer NOT NULL,
5+
`haiyama` text NOT NULL,
6+
`sutehai` text NOT NULL,
7+
`tehai` text NOT NULL,
8+
`tsumohai` text
9+
);
10+
--> statement-breakpoint
11+
DROP TABLE `users_table`;

0 commit comments

Comments
 (0)