diff --git a/lefthook.yml b/lefthook.yml index 0c728e4..85dc788 100644 --- a/lefthook.yml +++ b/lefthook.yml @@ -2,4 +2,4 @@ pre-commit: commands: check: glob: "*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc}" - run: bunx biome format --write {staged_files} \ No newline at end of file + run: bunx @biomejs/biome check --write {staged_files} && git add {staged_files} diff --git a/package.json b/package.json index bfa6fd5..7a7fc7f 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "build": "bun vite build", "lint": "eslint .", "preview": "bun vite preview", - "format": "bunx biome check . --write" + "format": "bunx @biomejs/biome check . --write" }, "dependencies": {}, "devDependencies": { diff --git a/rr/.env.example b/rr/.env.example index 89bb7a8..a55391f 100644 --- a/rr/.env.example +++ b/rr/.env.example @@ -1,4 +1,4 @@ NODE_ENV=development DATABASE_URL=postgres://user:postgres@localhost:5432/ BETTER_AUTH_SECRET= -BETTER_AUTH_URL=http://localhost:3000 +BETTER_AUTH_URL=http://localhost:5173 diff --git a/rr/app/lib/auth-client.ts b/rr/app/lib/auth-client.ts index 9ccd525..b51b765 100644 --- a/rr/app/lib/auth-client.ts +++ b/rr/app/lib/auth-client.ts @@ -1,2 +1,5 @@ +import { anonymousClient } from "better-auth/client/plugins"; import { createAuthClient } from "better-auth/react"; -export const authClient = createAuthClient({}); +export const authClient = createAuthClient({ + plugins: [anonymousClient()], +}); diff --git a/rr/app/lib/auth.ts b/rr/app/lib/auth.ts index 56aa10c..69be51b 100644 --- a/rr/app/lib/auth.ts +++ b/rr/app/lib/auth.ts @@ -1,5 +1,7 @@ import { betterAuth } from "better-auth"; import { drizzleAdapter } from "better-auth/adapters/drizzle"; +import { anonymous } from "better-auth/plugins"; +import { eq } from "drizzle-orm"; import { getDB } from "./db"; export function getAuth(env?: Env) { @@ -10,6 +12,18 @@ export function getAuth(env?: Env) { emailAndPassword: { enabled: true, }, + plugins: [ + anonymous({ + onLinkAccount: async ({ anonymousUser, newUser }) => { + const db = getDB(env); + // migrate like this + // await db + // .update(playHistory) + // .set({ userId: newUser.user.id }) + // .where(eq(playHistory.userId, anonymousUser.user.id)); + }, + }), + ], }); return auth; } diff --git a/rr/app/lib/db/schema.ts b/rr/app/lib/db/schema.ts index 1f962ed..c169b9f 100644 --- a/rr/app/lib/db/schema.ts +++ b/rr/app/lib/db/schema.ts @@ -1,19 +1,64 @@ +import { sql } from "drizzle-orm"; import { boolean, + check, + index, integer, + pgEnum, pgTable, + primaryKey, + serial, text, timestamp, - varchar, } from "drizzle-orm/pg-core"; -export const usersTable = pgTable("users", { - id: integer().primaryKey().generatedAlwaysAsIdentity(), - name: varchar({ length: 255 }).notNull(), - age: integer().notNull(), - email: varchar({ length: 255 }).notNull().unique(), +export const haiyama = pgTable("haiyama", { + id: text("id").primaryKey(), +}); + +export const haiKindEnum = pgEnum("hai_kind", [ + "manzu", + "pinzu", + "souzu", + "jihai", +]); + +export const hai = pgTable("hai", { + id: serial("id").primaryKey(), + haiyamaId: text("haiyama_id") + .notNull() + .references(() => haiyama.id, { onDelete: "cascade" }), + kind: haiKindEnum("kind").notNull(), // "manzu" | "pinzu" | "souzu" | "jihai" + value: text("value").notNull(), // 1~9 or "ton" ~ "tyun" + order: integer("order").notNull(), // 0~17 + index: integer("index").notNull(), // haiToIndex }); +// relation between user and haiyama +// TODO: index +export const kyoku = pgTable( + "kyoku", + { + userId: text("user_id") + .notNull() + .references(() => user.id, { onDelete: "cascade" }), + haiyamaId: text("haiyama_id") + .notNull() + .references(() => haiyama.id, { onDelete: "cascade" }), + didAgari: boolean("did_agari").notNull(), + agariJunme: integer("agari_junme"), + }, + (table) => [ + primaryKey({ columns: [table.userId, table.haiyamaId] }), + index("kyoku_user_id_idx").on(table.userId), + index("kyoku_haiyama_id_idx").on(table.haiyamaId), + check( + "agari_consistency", + sql`(${table.didAgari} = false) OR (${table.didAgari} = true AND ${table.agariJunme} IS NOT NULL)`, + ), + ], +); + // better-auth export const user = pgTable("user", { id: text("id").primaryKey(), @@ -26,6 +71,7 @@ export const user = pgTable("user", { .defaultNow() .$onUpdate(() => /* @__PURE__ */ new Date()) .notNull(), + isAnonymous: boolean("is_anonymous"), }); export const session = pgTable("session", { diff --git a/apps/shared/hai.ts b/rr/app/lib/hai.ts similarity index 82% rename from apps/shared/hai.ts rename to rr/app/lib/hai.ts index 0b17199..8e0026c 100644 --- a/apps/shared/hai.ts +++ b/rr/app/lib/hai.ts @@ -78,3 +78,22 @@ export function constructHai(kind: HaiKind, value: number | JihaiValue): Hai { export function sortTehai(haiArray: Hai[]): Hai[] { return haiArray.sort((a, b) => haiToIndex(a) - haiToIndex(b)); } + +// To store hai in DB +export type DBHai = { + haiyamaId: string; + kind: HaiKind; + value: string; + order: number; + index: number; +}; + +export function haiToDBHai(hai: Hai, haiyamaId: string, order: number): DBHai { + return { + haiyamaId, + kind: hai.kind, + value: String(hai.value), + order, + index: haiToIndex(hai), + }; +}