PraxaLing / lib /db /schema.ts
Reubencf's picture
feat: phrases page, level picker, furigana, HF inference fix, more scenarios
a0a94bd
Raw
History Blame Contribute Delete
4.34 kB
import { relations, sql } from "drizzle-orm";
import {
jsonb,
pgTable,
primaryKey,
text,
timestamp,
uuid,
varchar,
} from "drizzle-orm/pg-core";
export const users = pgTable("users", {
id: uuid("id").primaryKey().defaultRandom(),
hfId: varchar("hf_id", { length: 64 }).notNull().unique(),
hfUsername: varchar("hf_username", { length: 128 }).notNull(),
email: varchar("email", { length: 256 }),
avatarUrl: text("avatar_url"),
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
});
export const profiles = pgTable("profiles", {
userId: uuid("user_id")
.primaryKey()
.references(() => users.id, { onDelete: "cascade" }),
nativeLang: varchar("native_lang", { length: 8 }).notNull(),
targetLang: varchar("target_lang", { length: 8 }).notNull(),
level: varchar("level", { length: 4 }).notNull(),
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
});
export const stories = pgTable("stories", {
id: uuid("id").primaryKey().defaultRandom(),
targetLang: varchar("target_lang", { length: 8 }).notNull(),
level: varchar("level", { length: 4 }).notNull(),
title: text("title").notNull(),
content: text("content").notNull(),
vocab: jsonb("vocab").$type<Array<{ word: string; gloss: string }>>().default(sql`'[]'::jsonb`),
createdBy: uuid("created_by").references(() => users.id, { onDelete: "set null" }),
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
});
export const storyReads = pgTable(
"story_reads",
{
userId: uuid("user_id")
.notNull()
.references(() => users.id, { onDelete: "cascade" }),
storyId: uuid("story_id")
.notNull()
.references(() => stories.id, { onDelete: "cascade" }),
completedAt: timestamp("completed_at", { withTimezone: true }).notNull().defaultNow(),
},
(t) => [primaryKey({ columns: [t.userId, t.storyId] })],
);
export const visionJobs = pgTable("vision_jobs", {
id: uuid("id").primaryKey().defaultRandom(),
userId: uuid("user_id")
.notNull()
.references(() => users.id, { onDelete: "cascade" }),
imageUrl: text("image_url").notNull(),
caption: text("caption"),
objects: jsonb("objects").$type<
Array<{ label: string; translation: string; box: [number, number, number, number]; score: number }>
>(),
sentences: jsonb("sentences").$type<Array<{ target: string; gloss: string }>>(),
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
});
export type DialogueOption = {
text: string;
isCorrect: boolean;
feedback: string;
};
export type DialogueTurn = {
type: "narration" | "character" | "user_choice";
text: string;
translation?: string;
speakerName?: string;
options?: DialogueOption[];
};
export const dialogues = pgTable("dialogues", {
id: uuid("id").primaryKey().defaultRandom(),
targetLang: varchar("target_lang", { length: 8 }).notNull(),
level: varchar("level", { length: 4 }).notNull(),
title: text("title").notNull(),
scenario: text("scenario").notNull(),
turns: jsonb("turns").$type<DialogueTurn[]>().notNull().default(sql`'[]'::jsonb`),
createdBy: uuid("created_by").references(() => users.id, { onDelete: "set null" }),
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
});
export const usersRelations = relations(users, ({ one, many }) => ({
profile: one(profiles, { fields: [users.id], references: [profiles.userId] }),
stories: many(stories),
reads: many(storyReads),
visionJobs: many(visionJobs),
dialogues: many(dialogues),
}));
export const profilesRelations = relations(profiles, ({ one }) => ({
user: one(users, { fields: [profiles.userId], references: [users.id] }),
}));
export const storiesRelations = relations(stories, ({ one, many }) => ({
author: one(users, { fields: [stories.createdBy], references: [users.id] }),
reads: many(storyReads),
}));
export const storyReadsRelations = relations(storyReads, ({ one }) => ({
user: one(users, { fields: [storyReads.userId], references: [users.id] }),
story: one(stories, { fields: [storyReads.storyId], references: [stories.id] }),
}));
export const dialoguesRelations = relations(dialogues, ({ one }) => ({
author: one(users, { fields: [dialogues.createdBy], references: [users.id] }),
}));