Skip to content

Commit 7a520a4

Browse files
authored
D1とDurable Objectを導入 (#111)
* seed local d1 * set up durable object * format * migrate to durable object * format
1 parent 63dc297 commit 7a520a4

29 files changed

+738
-382
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,5 @@ cp .env.example .env
66
docker compose up
77
bun dev
88
```
9+
10+
After running `bunx drizzle-kit generate --config=drizzle-do.config.ts`, you need to add `?raw` to SQL file names in drizzle-do/migraions.js`.

app/lib/auth.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ import { anonymous } from "better-auth/plugins";
44
import * as schema from "../lib/db/schema";
55
import { getDB } from "./db";
66

7-
export function getAuth(env?: Env) {
7+
export function getAuth(env: Env) {
88
const auth = betterAuth({
99
database: drizzleAdapter(getDB(env), {
10-
provider: "pg",
10+
provider: "sqlite",
1111
schema: schema,
1212
}),
1313
emailAndPassword: {
@@ -28,5 +28,3 @@ export function getAuth(env?: Env) {
2828
});
2929
return auth;
3030
}
31-
// This is for @better-auth/cli
32-
// export const auth = getAuth();

app/lib/db/index.ts

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,6 @@
1-
import { drizzle as drizzleNeon } from "drizzle-orm/neon-http";
2-
import { drizzle as drizzlePg } from "drizzle-orm/node-postgres";
1+
import { drizzle } from "drizzle-orm/d1";
32

4-
export function getDB(env?: Env) {
5-
// better-auth/cli を実行するとき
6-
if (!env) {
7-
const db = drizzlePg(process.env.DATABASE_URL);
8-
return db;
9-
}
10-
const db =
11-
env.NODE_ENV === "development"
12-
? drizzlePg(env.DATABASE_URL)
13-
: drizzleNeon(env.DATABASE_URL);
3+
export function getDB(env: Env) {
4+
const db = drizzle(env.DB);
145
return db;
156
}

app/lib/db/schema-do.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { customType, int, sqliteTable } from "drizzle-orm/sqlite-core";
2+
import type { Hai } from "../hai/utils";
3+
import { haiArray } from "./schema";
4+
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"),
26+
});

app/lib/db/schema.ts

Lines changed: 66 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,37 @@
11
import { sql } from "drizzle-orm";
22
import {
3-
boolean,
43
check,
4+
customType,
55
index,
66
integer,
7-
pgEnum,
8-
pgTable,
97
primaryKey,
10-
serial,
8+
sqliteTable,
119
text,
12-
timestamp,
13-
} from "drizzle-orm/pg-core";
10+
} from "drizzle-orm/sqlite-core";
11+
import type { Hai } from "../hai/utils";
1412

15-
export const haiyama = pgTable("haiyama", {
16-
id: text("id").primaryKey(),
13+
export const haiArray = customType<{ data: Hai[]; driverData: string }>({
14+
dataType() {
15+
return "text";
16+
},
17+
toDriver(value: Hai[]) {
18+
return JSON.stringify(value);
19+
},
20+
fromDriver(value: string) {
21+
return JSON.parse(value) as Hai[];
22+
},
1723
});
1824

19-
export const haiKindEnum = pgEnum("hai_kind", [
20-
"manzu",
21-
"pinzu",
22-
"souzu",
23-
"jihai",
24-
]);
25-
26-
export const hai = pgTable("hai", {
27-
id: serial("id").primaryKey(),
28-
haiyamaId: text("haiyama_id")
29-
.notNull()
30-
.references(() => haiyama.id, { onDelete: "cascade" }),
31-
kind: haiKindEnum("kind").notNull(), // "manzu" | "pinzu" | "souzu" | "jihai"
32-
value: text("value").notNull(), // 1~9 or "ton" ~ "tyun"
33-
order: integer("order").notNull(), // 0~17
34-
index: integer("index").notNull(), // haiToIndex
25+
export const haiyama = sqliteTable("haiyama", {
26+
id: text("id")
27+
.primaryKey()
28+
.$defaultFn(() => crypto.randomUUID()),
29+
// D1だと1クエリあたり100パラメータまでなので、あえて正規化していない
30+
tiles: haiArray("tiles").notNull(),
3531
});
3632

3733
// relation between user and haiyama
38-
export const kyoku = pgTable(
34+
export const kyoku = sqliteTable(
3935
"kyoku",
4036
{
4137
userId: text("user_id")
@@ -44,7 +40,7 @@ export const kyoku = pgTable(
4440
haiyamaId: text("haiyama_id")
4541
.notNull()
4642
.references(() => haiyama.id, { onDelete: "cascade" }),
47-
didAgari: boolean("did_agari").notNull(),
43+
didAgari: integer("did_agari", { mode: "boolean" }).notNull(),
4844
agariJunme: integer("agari_junme"),
4945
},
5046
(table) => [
@@ -59,36 +55,43 @@ export const kyoku = pgTable(
5955
);
6056

6157
// better-auth
62-
export const user = pgTable("user", {
58+
export const user = sqliteTable("user", {
6359
id: text("id").primaryKey(),
6460
name: text("name").notNull(),
6561
email: text("email").notNull().unique(),
66-
emailVerified: boolean("email_verified").default(false).notNull(),
67-
image: text("image"),
68-
createdAt: timestamp("created_at").defaultNow().notNull(),
69-
updatedAt: timestamp("updated_at")
70-
.defaultNow()
71-
.$onUpdate(() => /* @__PURE__ */ new Date())
62+
emailVerified: integer("email_verified", { mode: "boolean" })
63+
.default(false)
7264
.notNull(),
73-
isAnonymous: boolean("is_anonymous"),
65+
image: text("image"),
66+
createdAt: integer("created_at", { mode: "timestamp" })
67+
.notNull()
68+
.$defaultFn(() => new Date()),
69+
updatedAt: integer("updated_at", { mode: "timestamp" })
70+
.notNull()
71+
.$defaultFn(() => new Date())
72+
.$onUpdate(() => new Date()),
73+
isAnonymous: integer("is_anonymous", { mode: "boolean" }),
7474
});
7575

76-
export const session = pgTable("session", {
76+
export const session = sqliteTable("session", {
7777
id: text("id").primaryKey(),
78-
expiresAt: timestamp("expires_at").notNull(),
78+
expiresAt: integer("expires_at", { mode: "timestamp" }).notNull(),
7979
token: text("token").notNull().unique(),
80-
createdAt: timestamp("created_at").defaultNow().notNull(),
81-
updatedAt: timestamp("updated_at")
82-
.$onUpdate(() => /* @__PURE__ */ new Date())
83-
.notNull(),
80+
createdAt: integer("created_at", { mode: "timestamp" })
81+
.notNull()
82+
.$defaultFn(() => new Date()),
83+
updatedAt: integer("updated_at", { mode: "timestamp" })
84+
.notNull()
85+
.$defaultFn(() => new Date())
86+
.$onUpdate(() => new Date()),
8487
ipAddress: text("ip_address"),
8588
userAgent: text("user_agent"),
8689
userId: text("user_id")
8790
.notNull()
8891
.references(() => user.id, { onDelete: "cascade" }),
8992
});
9093

91-
export const account = pgTable("account", {
94+
export const account = sqliteTable("account", {
9295
id: text("id").primaryKey(),
9396
accountId: text("account_id").notNull(),
9497
providerId: text("provider_id").notNull(),
@@ -98,24 +101,33 @@ export const account = pgTable("account", {
98101
accessToken: text("access_token"),
99102
refreshToken: text("refresh_token"),
100103
idToken: text("id_token"),
101-
accessTokenExpiresAt: timestamp("access_token_expires_at"),
102-
refreshTokenExpiresAt: timestamp("refresh_token_expires_at"),
104+
accessTokenExpiresAt: integer("access_token_expires_at", {
105+
mode: "timestamp",
106+
}),
107+
refreshTokenExpiresAt: integer("refresh_token_expires_at", {
108+
mode: "timestamp",
109+
}),
103110
scope: text("scope"),
104111
password: text("password"),
105-
createdAt: timestamp("created_at").defaultNow().notNull(),
106-
updatedAt: timestamp("updated_at")
107-
.$onUpdate(() => /* @__PURE__ */ new Date())
108-
.notNull(),
112+
createdAt: integer("created_at", { mode: "timestamp" })
113+
.notNull()
114+
.$defaultFn(() => new Date()),
115+
updatedAt: integer("updated_at", { mode: "timestamp" })
116+
.notNull()
117+
.$defaultFn(() => new Date())
118+
.$onUpdate(() => new Date()),
109119
});
110120

111-
export const verification = pgTable("verification", {
121+
export const verification = sqliteTable("verification", {
112122
id: text("id").primaryKey(),
113123
identifier: text("identifier").notNull(),
114124
value: text("value").notNull(),
115-
expiresAt: timestamp("expires_at").notNull(),
116-
createdAt: timestamp("created_at").defaultNow().notNull(),
117-
updatedAt: timestamp("updated_at")
118-
.defaultNow()
119-
.$onUpdate(() => /* @__PURE__ */ new Date())
120-
.notNull(),
125+
expiresAt: integer("expires_at", { mode: "timestamp" }).notNull(),
126+
createdAt: integer("created_at", { mode: "timestamp" })
127+
.notNull()
128+
.$defaultFn(() => new Date()),
129+
updatedAt: integer("updated_at", { mode: "timestamp" })
130+
.notNull()
131+
.$defaultFn(() => new Date())
132+
.$onUpdate(() => new Date()),
121133
});

app/lib/db/seed.ts

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

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/hai/utils.ts

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -78,36 +78,3 @@ export function constructHai(kind: HaiKind, value: number | JihaiValue): Hai {
7878
export function sortTehai(haiArray: Hai[]): Hai[] {
7979
return haiArray.sort((a, b) => haiToIndex(a) - haiToIndex(b));
8080
}
81-
82-
// To store hai in DB
83-
export type DBHai = {
84-
haiyamaId: string;
85-
kind: HaiKind;
86-
value: string;
87-
order: number;
88-
index: number;
89-
};
90-
91-
export function haiToDBHai(hai: Hai, haiyamaId: string, order: number): DBHai {
92-
return {
93-
haiyamaId,
94-
kind: hai.kind,
95-
value: String(hai.value),
96-
order,
97-
index: haiToIndex(hai),
98-
};
99-
}
100-
101-
export function dbHaiToHai(dbHai: DBHai): Hai {
102-
if (dbHai.kind === "jihai") {
103-
return {
104-
kind: dbHai.kind,
105-
value: dbHai.value as JihaiValue,
106-
};
107-
} else {
108-
return {
109-
kind: dbHai.kind as SuhaiKind,
110-
value: Number(dbHai.value),
111-
};
112-
}
113-
}

0 commit comments

Comments
 (0)