From 42de613b6c5a24b629cf0af423a18ffc8253f0f8 Mon Sep 17 00:00:00 2001 From: Pontus Abrahamsson Date: Wed, 8 Jan 2025 13:47:16 +0100 Subject: [PATCH] tRPC (#55) * wip * regions * Add emails --- .github/workflows/production-api.yml | 40 - apps/api/.dev.vars-example | 8 - apps/api/drizzle.config.ts | 42 - apps/api/locales/en.json | 18 - apps/api/package.json | 48 - apps/api/src/bindings.d.ts | 16 - apps/api/src/db/index.ts | 4 - apps/api/src/index.ts | 77 -- apps/api/src/lib/app.ts | 4 - apps/api/src/lib/auth.ts | 122 --- apps/api/src/lib/envs.ts | 14 - apps/api/src/middleware.ts | 40 - apps/api/src/routes/feedback/index.ts | 27 - apps/api/src/routes/feedback/schema.ts | 31 - apps/api/src/routes/fine-tune/index.ts | 56 -- apps/api/src/routes/fine-tune/schema.ts | 21 - apps/api/src/routes/projects/index.ts | 223 ----- apps/api/src/routes/projects/schema.ts | 65 -- apps/api/src/routes/teams/index.ts | 262 ----- apps/api/src/routes/teams/schema.ts | 58 -- apps/api/src/routes/telemetry/index.ts | 29 - apps/api/src/routes/telemetry/schema.ts | 10 - apps/api/src/routes/translate/index.ts | 121 --- apps/api/src/routes/translate/schema.ts | 41 - apps/api/src/routes/users/index.ts | 139 --- apps/api/src/routes/users/schema.ts | 44 - apps/api/tsconfig.json | 22 - apps/api/wrangler.json | 50 - apps/web/.env-example | 3 + apps/web/drizzle.config.ts | 13 + .../drizzle/0000_busy_mandrill.sql | 0 .../drizzle/0001_steep_scrambler.sql | 0 .../drizzle/meta/0000_snapshot.json | 0 .../drizzle/meta/0001_snapshot.json | 0 apps/{api => web}/drizzle/meta/_journal.json | 0 apps/web/migrations/0000_sad_red_shift.sql | 129 +++ apps/web/migrations/meta/0000_snapshot.json | 895 ++++++++++++++++++ apps/web/migrations/meta/_journal.json | 13 + apps/web/package.json | 22 +- .../app/[locale]/(dashboard)/login/page.tsx | 8 - apps/web/src/app/[locale]/layout.tsx | 11 +- apps/web/src/app/api/auth/[...all]/route.ts | 4 + apps/web/src/app/api/trpc/[trpc] /route.ts | 13 + apps/web/src/components/dashboard/header.tsx | 6 +- apps/web/src/components/github-sign-in.tsx | 4 +- apps/web/src/components/google-sign-in.tsx | 4 +- apps/web/src/components/login.tsx | 6 +- apps/web/src/components/sign-in.tsx | 4 +- .../src/components/team-selector.server.tsx | 14 +- apps/web/src/components/team-selector.tsx | 24 +- apps/web/src/components/user-menu.tsx | 6 +- apps/web/src/db/index.ts | 9 + .../src/db/queries/delete.ts} | 0 apps/web/src/db/queries/insert.ts | 42 + apps/web/src/db/queries/select.ts | 34 + apps/web/src/db/queries/update.ts | 0 apps/{api => web}/src/db/schema.ts | 0 .../src/emails/components/footer.tsx | 15 +- .../src/emails/components/logo.tsx | 6 +- .../src/emails/components/outline-button.tsx | 0 .../src/emails/templates/welcome.tsx | 6 +- apps/web/src/lib/api.ts | 18 - apps/web/src/lib/auth.ts | 26 - apps/web/src/lib/auth/client.ts | 14 + apps/web/src/lib/auth/middleware.ts | 24 + apps/web/src/lib/auth/server.ts | 71 ++ apps/web/src/lib/cookies.ts | 13 - apps/web/src/lib/queries.ts | 49 - apps/web/src/lib/resend.ts | 3 + apps/web/src/lib/session.ts | 9 +- apps/web/src/lib/url.ts | 7 + apps/web/src/middleware.ts | 25 +- apps/web/src/trpc/client.tsx | 81 ++ apps/web/src/trpc/init.ts | 32 + apps/web/src/trpc/query-client.ts | 24 + apps/web/src/trpc/routers/_app.ts | 12 + apps/web/src/trpc/routers/organization.ts | 121 +++ apps/web/src/trpc/server.ts | 16 + apps/web/vercel.json | 2 +- bun.lockb | Bin 840296 -> 785056 bytes 80 files changed, 1666 insertions(+), 1804 deletions(-) delete mode 100644 .github/workflows/production-api.yml delete mode 100644 apps/api/.dev.vars-example delete mode 100644 apps/api/drizzle.config.ts delete mode 100644 apps/api/locales/en.json delete mode 100644 apps/api/package.json delete mode 100644 apps/api/src/bindings.d.ts delete mode 100644 apps/api/src/db/index.ts delete mode 100644 apps/api/src/index.ts delete mode 100644 apps/api/src/lib/app.ts delete mode 100644 apps/api/src/lib/auth.ts delete mode 100644 apps/api/src/lib/envs.ts delete mode 100644 apps/api/src/middleware.ts delete mode 100644 apps/api/src/routes/feedback/index.ts delete mode 100644 apps/api/src/routes/feedback/schema.ts delete mode 100644 apps/api/src/routes/fine-tune/index.ts delete mode 100644 apps/api/src/routes/fine-tune/schema.ts delete mode 100644 apps/api/src/routes/projects/index.ts delete mode 100644 apps/api/src/routes/projects/schema.ts delete mode 100644 apps/api/src/routes/teams/index.ts delete mode 100644 apps/api/src/routes/teams/schema.ts delete mode 100644 apps/api/src/routes/telemetry/index.ts delete mode 100644 apps/api/src/routes/telemetry/schema.ts delete mode 100644 apps/api/src/routes/translate/index.ts delete mode 100644 apps/api/src/routes/translate/schema.ts delete mode 100644 apps/api/src/routes/users/index.ts delete mode 100644 apps/api/src/routes/users/schema.ts delete mode 100644 apps/api/tsconfig.json delete mode 100644 apps/api/wrangler.json create mode 100644 apps/web/drizzle.config.ts rename apps/{api => web}/drizzle/0000_busy_mandrill.sql (100%) rename apps/{api => web}/drizzle/0001_steep_scrambler.sql (100%) rename apps/{api => web}/drizzle/meta/0000_snapshot.json (100%) rename apps/{api => web}/drizzle/meta/0001_snapshot.json (100%) rename apps/{api => web}/drizzle/meta/_journal.json (100%) create mode 100644 apps/web/migrations/0000_sad_red_shift.sql create mode 100644 apps/web/migrations/meta/0000_snapshot.json create mode 100644 apps/web/migrations/meta/_journal.json create mode 100644 apps/web/src/app/api/auth/[...all]/route.ts create mode 100644 apps/web/src/app/api/trpc/[trpc] /route.ts create mode 100644 apps/web/src/db/index.ts rename apps/{api/README.md => web/src/db/queries/delete.ts} (100%) create mode 100644 apps/web/src/db/queries/insert.ts create mode 100644 apps/web/src/db/queries/select.ts create mode 100644 apps/web/src/db/queries/update.ts rename apps/{api => web}/src/db/schema.ts (100%) rename apps/{api => web}/src/emails/components/footer.tsx (81%) rename apps/{api => web}/src/emails/components/logo.tsx (68%) rename apps/{api => web}/src/emails/components/outline-button.tsx (100%) rename apps/{api => web}/src/emails/templates/welcome.tsx (94%) delete mode 100644 apps/web/src/lib/api.ts delete mode 100644 apps/web/src/lib/auth.ts create mode 100644 apps/web/src/lib/auth/client.ts create mode 100644 apps/web/src/lib/auth/middleware.ts create mode 100644 apps/web/src/lib/auth/server.ts delete mode 100644 apps/web/src/lib/cookies.ts delete mode 100644 apps/web/src/lib/queries.ts create mode 100644 apps/web/src/lib/resend.ts create mode 100644 apps/web/src/lib/url.ts create mode 100644 apps/web/src/trpc/client.tsx create mode 100644 apps/web/src/trpc/init.ts create mode 100644 apps/web/src/trpc/query-client.ts create mode 100644 apps/web/src/trpc/routers/_app.ts create mode 100644 apps/web/src/trpc/routers/organization.ts create mode 100644 apps/web/src/trpc/server.ts diff --git a/.github/workflows/production-api.yml b/.github/workflows/production-api.yml deleted file mode 100644 index 848e411..0000000 --- a/.github/workflows/production-api.yml +++ /dev/null @@ -1,40 +0,0 @@ -name: 🌟 Production Deployment - API - -on: - push: - branches: - - main - paths: - - apps/api/** -jobs: - deploy-production: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: oven-sh/setup-bun@v1 - with: - bun-version: latest - - name: πŸ“¦ Install dependencies - run: bun install - - name: πŸ”¦ Run type checking - run: bun turbo typecheck --filter=@languine/api - - name: πŸ”¬ Run linting - run: bun turbo lint --filter=@languine/api - - name: πŸ§ͺ Run unit tests - run: bun turbo test --filter=@languine/api - - name: πŸ—ƒοΈ Apply database migrations - uses: cloudflare/wrangler-action@v3 - with: - packageManager: bun - apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} - workingDirectory: "apps/api" - wranglerVersion: "latest" - command: d1 migrations apply languine-production --env production --remote - - name: πŸš€ Deploy Project Artifacts to Cloudflare - uses: cloudflare/wrangler-action@v3 - with: - packageManager: bun - apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} - workingDirectory: "apps/api" - wranglerVersion: "latest" - command: deploy --minify src/index.ts --env production \ No newline at end of file diff --git a/apps/api/.dev.vars-example b/apps/api/.dev.vars-example deleted file mode 100644 index 5120501..0000000 --- a/apps/api/.dev.vars-example +++ /dev/null @@ -1,8 +0,0 @@ -RESEND_API_KEY= -BETTER_AUTH_SECRET="" -BETTER_AUTH_TRUSTED_ORIGINS="," -BETTER_AUTH_BASE_URL=http://localhost:3000 -GITHUB_CLIENT_ID= -GITHUB_CLIENT_SECRET= -GOOGLE_CLIENT_ID= -GOOGLE_CLIENT_SECRET= \ No newline at end of file diff --git a/apps/api/drizzle.config.ts b/apps/api/drizzle.config.ts deleted file mode 100644 index b60fea5..0000000 --- a/apps/api/drizzle.config.ts +++ /dev/null @@ -1,42 +0,0 @@ -import fs from "node:fs"; -import path from "node:path"; -import type { Config } from "drizzle-kit"; - -const getLocalD1 = (): string => { - const wranglerDir = path.resolve(".wrangler"); - - try { - const files = fs.readdirSync(wranglerDir, { - encoding: "utf-8", - recursive: true, - }); - - const dbFile = files.find((f) => f.endsWith(".sqlite")); - - if (!dbFile) { - throw new Error(`No SQLite database found in ${wranglerDir}`); - } - - return path.resolve(wranglerDir, dbFile); - } catch { - return path.resolve(wranglerDir, "default.sqlite"); - } -}; - -export default { - dialect: "sqlite", - schema: "./src/db/schema.ts", - out: "./drizzle", - ...(!process.env.DEV_MODE - ? { - driver: "d1-http", - } - : {}), - ...(process.env.DEV_MODE - ? { - dbCredentials: { - url: getLocalD1(), - }, - } - : {}), -} satisfies Config; diff --git a/apps/api/locales/en.json b/apps/api/locales/en.json deleted file mode 100644 index 2d1dddd..0000000 --- a/apps/api/locales/en.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "email": { - "welcome": { - "preview": "Welcome to Languine - Your Automated Localization Solution", - "greeting": "Hi %{name},", - "intro": "Thank you for signing up for Languine! We're excited to help you automate your application's localization.", - "feature1": "Seamless integration with your existing codebase", - "feature2": "Automated translation workflows", - "feature3": "Context-aware translations", - "feature4": "Translation management dashboard", - "support": "If you have any questions, feel free to reach out to us at %{email}", - "cta": { - "automate": "Start Automating", - "docs": "Read the Docs" - } - } - } -} \ No newline at end of file diff --git a/apps/api/package.json b/apps/api/package.json deleted file mode 100644 index 7443c1e..0000000 --- a/apps/api/package.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "name": "@languine/api", - "main": "src/index.ts", - "types": "src/index.ts", - "scripts": { - "dev": "DEV_MODE=true wrangler dev --port 3002", - "email:dev": "email dev --port 3003 --dir src/emails/templates", - "deploy": "wrangler deploy --minify", - "typecheck": "tsc --noEmit", - "clean": "rm -rf .turbo node_modules", - "lint": "biome check .", - "format": "biome format --write .", - "generate": "drizzle-kit generate", - "drizzle:up": "drizzle-kit up", - "amend": "drizzle-kit drop && drizzle-kit generate", - "studio": "DEV_MODE=true drizzle-kit studio" - }, - "dependencies": { - "@hono/zod-validator": "^0.4.2", - "@paralleldrive/cuid2": "^2.2.2", - "@react-email/components": "0.0.31", - "@react-email/font": "^0.0.9", - "@scalar/hono-api-reference": "^0.5.165", - "better-auth": "^1.1.10", - "drizzle-orm": "^0.38.3", - "hono": "4.6.15", - "hono-openapi": "^0.3.1", - "kysely": "^0.27.5", - "kysely-d1": "^0.3.0", - "react": "19.0.0", - "react-dom": "19.0.0", - "resend": "^4.0.1", - "slugify": "^1.6.6", - "zod": "^3.24.1", - "zod-openapi": "^4.2.2" - }, - "devDependencies": { - "@cloudflare/workers-types": "^4.20241230.0", - "@libsql/client": "^0.14.0", - "@types/react": "19.0.3", - "@types/react-dom": "19.0.2", - "add": "2.0.6", - "bun": "1.1.42", - "drizzle-kit": "^0.30.1", - "react-email": "3.0.4", - "wrangler": "^3.99.0" - } -} \ No newline at end of file diff --git a/apps/api/src/bindings.d.ts b/apps/api/src/bindings.d.ts deleted file mode 100644 index ce0cc92..0000000 --- a/apps/api/src/bindings.d.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type { auth } from "@/auth"; -import type { Env } from "hono"; - -type Environment = Env & { - Bindings: { - DB: D1Database; - ENV_TYPE: "dev" | "prod" | "stage"; - RESEND_API_KEY: string; - BETTER_AUTH_SECRET: string; - BETTER_AUTH_TRUSTED_ORIGINS: string; - }; - Variables: { - user: typeof auth.$Infer.Session.user | null; - session: typeof auth.$Infer.Session.session | null; - }; -}; diff --git a/apps/api/src/db/index.ts b/apps/api/src/db/index.ts deleted file mode 100644 index 839897b..0000000 --- a/apps/api/src/db/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { drizzle } from "drizzle-orm/d1"; -import * as schema from "./schema"; - -export const db = (env: D1Database) => drizzle(env, { schema }); diff --git a/apps/api/src/index.ts b/apps/api/src/index.ts deleted file mode 100644 index 93ff572..0000000 --- a/apps/api/src/index.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { Hono } from "@/lib/app"; -import { apiReference } from "@scalar/hono-api-reference"; -import { openAPISpecs } from "hono-openapi"; -import { cors } from "hono/cors"; -import { setupAuth } from "./lib/auth"; -import { sessionMiddleware } from "./middleware"; -import feedbackRouter from "./routes/feedback"; -import fineTuneRouter from "./routes/fine-tune"; -import projectsRouter from "./routes/projects"; -import teamsRouter from "./routes/teams"; -import telemetryRouter from "./routes/telemetry"; -import translateRouter from "./routes/translate"; -import usersRouter from "./routes/users"; - -const app = new Hono(); - -app.use( - "*", - cors({ - origin: ["http://localhost:3000", "https://languine.ai"], - allowHeaders: ["Content-Type", "Authorization", "credentials"], - allowMethods: ["POST", "GET", "OPTIONS"], - exposeHeaders: ["Content-Length"], - maxAge: 600, - credentials: true, - }), -); - -app.on(["POST", "GET"], "/api/auth/**", (c) => { - return setupAuth(c).handler(c.req.raw); -}); - -app.use("*", sessionMiddleware); - -const appRoutes = app - .route("/telemetry", telemetryRouter) - .route("/fine-tune", fineTuneRouter) - .route("/feedback", feedbackRouter) - .route("/translate", translateRouter) - .route("/users", usersRouter) - .route("/projects", projectsRouter) - .route("/teams", teamsRouter); - -app.get( - "/openapi", - openAPISpecs(app, { - documentation: { - info: { - title: "Languine API", - version: "1.0.0", - description: "API for Languine", - }, - servers: [ - { - url: "http://localhost:3002", - description: "Local server", - }, - { - url: "https://api.languine.ai", - description: "Production server", - }, - ], - }, - }), -); - -app.get( - "/", - apiReference({ - theme: "saturn", - spec: { url: "/openapi" }, - }), -); - -export type AppType = typeof appRoutes; - -export default app; diff --git a/apps/api/src/lib/app.ts b/apps/api/src/lib/app.ts deleted file mode 100644 index c40fcfc..0000000 --- a/apps/api/src/lib/app.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { Hono as Base } from "hono"; -import type { Environment } from "../bindings.js"; - -export const Hono = Base; diff --git a/apps/api/src/lib/auth.ts b/apps/api/src/lib/auth.ts deleted file mode 100644 index 4acf730..0000000 --- a/apps/api/src/lib/auth.ts +++ /dev/null @@ -1,122 +0,0 @@ -import { db } from "@/db"; -import { members, organizations, projects, sessions } from "@/db/schema"; -import WelcomeEmail from "@/emails/templates/welcome"; -import { createId } from "@paralleldrive/cuid2"; -import { betterAuth } from "better-auth"; -import { drizzleAdapter } from "better-auth/adapters/drizzle"; -import { organization } from "better-auth/plugins"; -import { eq } from "drizzle-orm"; -import type { Context } from "hono"; -import { Resend } from "resend"; -import slugify from "slugify"; - -export const setupAuth = (c: Context) => { - return betterAuth({ - database: drizzleAdapter(db(c.env.DB), { - provider: "sqlite", - usePlural: true, - }), - secret: c.env.BETTER_AUTH_SECRET, - baseURL: c.env.BETTER_AUTH_BASE_URL, - trustedOrigins: c.env.BETTER_AUTH_TRUSTED_ORIGINS.split(","), - socialProviders: { - github: { - clientId: c.env.GITHUB_CLIENT_ID, - clientSecret: c.env.GITHUB_CLIENT_SECRET, - }, - google: { - clientId: c.env.GOOGLE_CLIENT_ID, - clientSecret: c.env.GOOGLE_CLIENT_SECRET, - }, - }, - databaseHooks: { - user: { - create: { - after: async (user) => { - const database = db(c.env.DB); - - // Create default organization for new user - const org = await database - .insert(organizations) - .values({ - name: user.name, - slug: `${slugify(user.name, { lower: true })}-${createId().slice(0, 8)}`, - }) - .returning() - .get(); - - // Add user as member of organization - await database.insert(members).values({ - userId: user.id, - organizationId: org.id, - role: "owner", - }); - - // Create default project for new organization - await database.insert(projects).values({ - name: "Default", - organizationId: org.id, - slug: "default", - }); - - // Set active organization for new user's session - await database - .update(sessions) - .set({ activeOrganizationId: org.id }) - .where(eq(sessions.userId, user.id)); - - // Send welcome email to new user - try { - await new Resend(c.env.RESEND_API_KEY).emails.send({ - from: "Languine ", - to: user.email, - subject: "Welcome to Languine", - react: WelcomeEmail({ name: user.name }), - }); - } catch (error) { - console.error("Error sending welcome email", error); - } - }, - }, - }, - session: { - create: { - before: async (session) => { - const database = db(c.env.DB); - - const org = await database - .select() - .from(members) - .where(eq(members.userId, session.userId)) - .leftJoin( - organizations, - eq(organizations.id, members.organizationId), - ) - .limit(1) - .get(); - - return { - data: { - ...session, - activeOrganizationId: org?.organizations?.id, - }, - }; - }, - }, - }, - }, - session: { - cookieCache: { - enabled: true, - maxAge: 5 * 60, - }, - }, - advanced: { - crossSubDomainCookies: { - enabled: !c.env.BETTER_AUTH_BASE_URL?.includes("localhost"), - domain: ".languine.ai", - }, - }, - plugins: [organization()], - }); -}; diff --git a/apps/api/src/lib/envs.ts b/apps/api/src/lib/envs.ts deleted file mode 100644 index 76343fc..0000000 --- a/apps/api/src/lib/envs.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { Environment } from "@/bindings"; -import { getContext } from "hono/context-storage"; - -export const getEnvs = () => { - return getContext(); -}; - -export function getAppUrl() { - if (process.env.NODE_ENV === "development") { - return "http://localhost:3000"; - } - - return "https://languine.ai"; -} diff --git a/apps/api/src/middleware.ts b/apps/api/src/middleware.ts deleted file mode 100644 index 974a4f7..0000000 --- a/apps/api/src/middleware.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { db } from "@/db"; -import { users } from "@/db/schema"; -import { eq } from "drizzle-orm"; -import { createMiddleware } from "hono/factory"; -import { setupAuth } from "./lib/auth"; - -export const sessionMiddleware = createMiddleware(async (c, next) => { - const auth = setupAuth(c); - const headers = new Headers(c.req.raw.headers); - - // Try to get session from cookie first - const session = await auth.api.getSession({ headers }); - - if (session) { - c.set("user", session.user); - - return next(); - } - - // Try user API key from Authorization header (Used in the CLI) - const userApiKey = c.req.header("Authorization")?.replace("Bearer ", ""); - - if (userApiKey) { - const database = db(c.env.DB); - const user = await database - .select() - .from(users) - .where(eq(users.apiKey, userApiKey)) - .get(); - - if (user) { - c.set("user", user); - - return next(); - } - } - - // If no valid session or API key, return unauthorized - return c.json({ error: "Unauthorized" }, 401); -}); diff --git a/apps/api/src/routes/feedback/index.ts b/apps/api/src/routes/feedback/index.ts deleted file mode 100644 index 13f460e..0000000 --- a/apps/api/src/routes/feedback/index.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { Hono } from "@/lib/app"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator as zValidator } from "hono-openapi/zod"; -import { bodySchema, responseSchema } from "./schema"; - -const app = new Hono().post( - "/", - describeRoute({ - description: "Submit feedback", - responses: { - 200: { - description: "Successful feedback submission", - content: { - "application/json": { - schema: resolver(responseSchema), - }, - }, - }, - }, - }), - zValidator("json", bodySchema), - (c) => { - return c.json({ data: "Feedback received!" }); - }, -); - -export default app; diff --git a/apps/api/src/routes/feedback/schema.ts b/apps/api/src/routes/feedback/schema.ts deleted file mode 100644 index 87a0af5..0000000 --- a/apps/api/src/routes/feedback/schema.ts +++ /dev/null @@ -1,31 +0,0 @@ -import z from "zod"; -import "zod-openapi/extend"; - -export const bodySchema = z - .object({ - source: z - .string() - .openapi({ example: "en", description: "Source language code" }), - target: z - .string() - .openapi({ example: "es", description: "Target language code" }), - input: z - .string() - .openapi({ example: "Hello world", description: "Original text" }), - translation: z - .string() - .openapi({ example: "Hola mundo", description: "Machine translation" }), - suggestion: z - .string() - .optional() - .openapi({ example: "Β‘Hola mundo!", description: "User suggestion" }), - comment: z - .string() - .optional() - .openapi({ example: "Too formal", description: "Additional comments" }), - }) - .openapi({ ref: "Body" }); - -export const responseSchema = z - .string() - .openapi({ example: "Feedback received!" }); diff --git a/apps/api/src/routes/fine-tune/index.ts b/apps/api/src/routes/fine-tune/index.ts deleted file mode 100644 index 5302692..0000000 --- a/apps/api/src/routes/fine-tune/index.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { Hono } from "@/lib/app"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator as zValidator } from "hono-openapi/zod"; -import { fineTuneBodySchema, fineTuneResponseSchema } from "./schema"; - -const app = new Hono() - .post( - "/", - describeRoute({ - description: "Fine tune a model", - responses: { - 200: { - description: "Successful fine tune response", - content: { - "application/json": { - schema: resolver(fineTuneResponseSchema), - }, - }, - }, - }, - }), - zValidator("json", fineTuneBodySchema), - (c) => { - return c.json({ data: "Fine tuned model!" }); - }, - ) - .get( - "/:id", - describeRoute({ - description: "Get fine tune status", - responses: { - 200: { - description: "Successfully retrieved fine tune status", - content: { - "application/json": { - schema: resolver(fineTuneResponseSchema), - }, - }, - }, - 404: { - description: "Fine tune not found", - content: { - "application/json": { - schema: resolver(fineTuneResponseSchema), - }, - }, - }, - }, - }), - async (c) => { - const id = c.req.param("id"); - return c.json({ data: `Fine tune ${id} status` }); - }, - ); - -export default app; diff --git a/apps/api/src/routes/fine-tune/schema.ts b/apps/api/src/routes/fine-tune/schema.ts deleted file mode 100644 index 5e840b8..0000000 --- a/apps/api/src/routes/fine-tune/schema.ts +++ /dev/null @@ -1,21 +0,0 @@ -import z from "zod"; -import "zod-openapi/extend"; - -export const fineTuneBodySchema = z - .object({ - instructions: z.string().openapi({ - description: "Instructions text to fine-tune the model with", - example: - "You are a helpful translator assistant. You maintain a professional yet approachable tone, and excel at preserving both meaning and cultural context when translating between languages. You provide clear explanations for idiomatic expressions and cultural references.", - }), - }) - .openapi({ ref: "FineTuneBody" }); - -export const fineTuneResponseSchema = z - .object({ - data: z.string().openapi({ - description: "Response message from fine-tuning", - example: "Fine tuned model!", - }), - }) - .openapi({ ref: "FineTuneResponse" }); diff --git a/apps/api/src/routes/projects/index.ts b/apps/api/src/routes/projects/index.ts deleted file mode 100644 index 4cac06b..0000000 --- a/apps/api/src/routes/projects/index.ts +++ /dev/null @@ -1,223 +0,0 @@ -import { Hono } from "@/lib/app"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator as zValidator } from "hono-openapi/zod"; -import { - createProjectSchema, - projectResponseSchema, - updateProjectSchema, -} from "./schema"; - -const app = new Hono() - .post( - "/", - describeRoute({ - description: "Create a new project", - responses: { - 200: { - description: "Successfully created project", - content: { - "application/json": { - schema: resolver(projectResponseSchema), - }, - }, - }, - 401: { - description: "Unauthorized - Invalid or missing token", - content: { - "application/json": { - schema: resolver(projectResponseSchema), - }, - }, - }, - }, - }), - zValidator("json", createProjectSchema), - async (c) => { - const token = c.req.header("Authorization")?.replace("Bearer ", ""); - - if (!token) { - return c.json({ error: "No token provided" }, 401); - } - - try { - const body = await c.req.valid("json"); - // TODO: Implement project creation logic - return c.json({ - data: { - id: "project_123", - name: body.name, - description: body.description, - createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString(), - }, - }); - } catch (error) { - return c.json({ error: "Failed to create project" }, 500); - } - }, - ) - .get( - "/:id", - describeRoute({ - description: "Get project by ID", - responses: { - 200: { - description: "Successfully retrieved project", - content: { - "application/json": { - schema: resolver(projectResponseSchema), - }, - }, - }, - 401: { - description: "Unauthorized - Invalid or missing token", - content: { - "application/json": { - schema: resolver(projectResponseSchema), - }, - }, - }, - 404: { - description: "Project not found", - content: { - "application/json": { - schema: resolver(projectResponseSchema), - }, - }, - }, - }, - }), - async (c) => { - const token = c.req.header("Authorization")?.replace("Bearer ", ""); - - if (!token) { - return c.json({ error: "No token provided" }, 401); - } - - try { - const id = c.req.param("id"); - // TODO: Implement project retrieval logic - return c.json({ - data: { - id, - name: "Sample Project", - description: "A sample project description", - sourceLanguage: "en", - targetLanguages: ["es", "fr"], - createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString(), - }, - }); - } catch (error) { - return c.json({ error: "Project not found" }, 404); - } - }, - ) - .patch( - "/:id", - describeRoute({ - description: "Update project", - responses: { - 200: { - description: "Successfully updated project", - content: { - "application/json": { - schema: resolver(projectResponseSchema), - }, - }, - }, - 401: { - description: "Unauthorized - Invalid or missing token", - content: { - "application/json": { - schema: resolver(projectResponseSchema), - }, - }, - }, - 404: { - description: "Project not found", - content: { - "application/json": { - schema: resolver(projectResponseSchema), - }, - }, - }, - }, - }), - zValidator("json", updateProjectSchema), - async (c) => { - const token = c.req.header("Authorization")?.replace("Bearer ", ""); - - if (!token) { - return c.json({ error: "No token provided" }, 401); - } - - try { - const id = c.req.param("id"); - const body = await c.req.valid("json"); - // TODO: Implement project update logic - return c.json({ - data: { - id, - name: body.name, - description: body.description, - createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString(), - }, - }); - } catch (error) { - return c.json({ error: "Project not found" }, 404); - } - }, - ) - .delete( - "/:id", - describeRoute({ - description: "Delete project", - responses: { - 200: { - description: "Successfully deleted project", - content: { - "application/json": { - schema: resolver(projectResponseSchema), - }, - }, - }, - 401: { - description: "Unauthorized - Invalid or missing token", - content: { - "application/json": { - schema: resolver(projectResponseSchema), - }, - }, - }, - 404: { - description: "Project not found", - content: { - "application/json": { - schema: resolver(projectResponseSchema), - }, - }, - }, - }, - }), - async (c) => { - const token = c.req.header("Authorization")?.replace("Bearer ", ""); - - if (!token) { - return c.json({ error: "No token provided" }, 401); - } - - try { - const id = c.req.param("id"); - // TODO: Implement project deletion logic - return c.json({ - data: null, - }); - } catch (error) { - return c.json({ error: "Project not found" }, 404); - } - }, - ); - -export default app; diff --git a/apps/api/src/routes/projects/schema.ts b/apps/api/src/routes/projects/schema.ts deleted file mode 100644 index 2789eab..0000000 --- a/apps/api/src/routes/projects/schema.ts +++ /dev/null @@ -1,65 +0,0 @@ -import z from "zod"; -import "zod-openapi/extend"; - -export const createProjectSchema = z - .object({ - name: z.string().min(1).openapi({ - example: "My Translation Project", - description: "Name of the project", - }), - description: z.string().optional().openapi({ - example: "A project for translating my website content", - description: "Optional description of the project", - }), - }) - .openapi({ ref: "CreateProject" }); - -export const updateProjectSchema = z - .object({ - name: z.string().min(1).optional().openapi({ - example: "Updated Project Name", - description: "New name for the project", - }), - description: z.string().optional().openapi({ - example: "Updated project description", - description: "New description for the project", - }), - }) - .refine((data) => data.name || data.description, { - message: "At least one field must be provided", - }) - .openapi({ ref: "UpdateProject" }); - -export const projectResponseSchema = z - .object({ - data: z - .object({ - id: z.string().openapi({ - example: "project_123", - description: "Unique identifier for the project", - }), - name: z.string().openapi({ - example: "My Translation Project", - description: "Name of the project", - }), - description: z.string().nullable().openapi({ - example: "A project for translating my website content", - description: "Description of the project", - }), - createdAt: z.string().datetime().openapi({ - example: "2024-01-01T00:00:00Z", - description: "Timestamp when the project was created", - }), - updatedAt: z.string().datetime().openapi({ - example: "2024-01-01T00:00:00Z", - description: "Timestamp when the project was last updated", - }), - }) - .nullable() - .openapi({ description: "Project information" }), - error: z.string().optional().openapi({ - example: "Project not found", - description: "Error message if request failed", - }), - }) - .openapi({ ref: "ProjectResponse" }); diff --git a/apps/api/src/routes/teams/index.ts b/apps/api/src/routes/teams/index.ts deleted file mode 100644 index 3c52f8f..0000000 --- a/apps/api/src/routes/teams/index.ts +++ /dev/null @@ -1,262 +0,0 @@ -import { db } from "@/db"; -import { members, organizations, projects, users } from "@/db/schema"; -import { Hono } from "@/lib/app"; -import { eq } from "drizzle-orm"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator as zValidator } from "hono-openapi/zod"; -import { createTeamSchema, inviteSchema, teamResponseSchema } from "./schema"; - -const app = new Hono() - .get( - "/", - describeRoute({ - description: "Get teams for current user", - responses: { - 200: { - description: "Successfully retrieved teams", - content: { - "application/json": { - schema: resolver(teamResponseSchema), - }, - }, - }, - 401: { - description: "Unauthorized - Invalid or missing token", - content: { - "application/json": { - schema: resolver(teamResponseSchema), - }, - }, - }, - }, - }), - async (c) => { - const user = c.get("user"); - - try { - const database = db(c.env.DB); - - const teams = await database - .select({ - id: organizations.id, - name: organizations.name, - slug: organizations.slug, - logo: organizations.logo, - role: members.role, - apiKey: organizations.apiKey, - plan: organizations.plan, - }) - .from(members) - .innerJoin( - organizations, - eq(organizations.id, members.organizationId), - ) - .innerJoin(users, eq(users.id, members.userId)) - .where(eq(users.id, user?.id)) - .all(); - - return c.json({ - data: teams.map((team) => ({ - id: team.id, - name: team.name, - slug: team.slug, - logo: team.logo, - role: team.role, - apiKey: team.apiKey, - plan: team.plan, - })), - }); - } catch (error) { - return c.json({ error: "Failed to retrieve teams" }, 500); - } - }, - ) - .post( - "/", - describeRoute({ - description: "Create a new team", - responses: { - 200: { - description: "Successfully created team", - content: { - "application/json": { - schema: resolver(teamResponseSchema), - }, - }, - }, - 401: { - description: "Unauthorized - Invalid or missing token", - content: { - "application/json": { - schema: resolver(teamResponseSchema), - }, - }, - }, - }, - }), - zValidator("json", createTeamSchema), - async (c) => { - const token = c.req.header("Authorization")?.replace("Bearer ", ""); - - if (!token) { - return c.json({ error: "No token provided" }, 401); - } - - try { - const body = await c.req.valid("json"); - // TODO: Implement team creation logic - return c.json({ - data: { - id: "team_123", - name: body.name, - members: [ - { - email: "creator@example.com", - role: "owner", - }, - ], - }, - }); - } catch (error) { - return c.json({ error: "Failed to create team" }, 500); - } - }, - ) - .get( - "/:teamId", - describeRoute({ - description: "Get team details", - responses: { - 200: { - description: "Successfully retrieved team details", - content: { - "application/json": { - schema: resolver(teamResponseSchema), - }, - }, - }, - 401: { - description: "Unauthorized - Invalid or missing token", - content: { - "application/json": { - schema: resolver(teamResponseSchema), - }, - }, - }, - 404: { - description: "Team not found", - content: { - "application/json": { - schema: resolver(teamResponseSchema), - }, - }, - }, - }, - }), - async (c) => { - const token = c.req.header("Authorization")?.replace("Bearer ", ""); - const teamId = c.req.param("teamId"); - - if (!token) { - return c.json({ error: "No token provided" }, 401); - } - - try { - // TODO: Implement team retrieval logic - return c.json({ - data: { - id: teamId, - name: "Example Team", - members: [ - { - email: "member1@example.com", - role: "owner", - }, - { - email: "member2@example.com", - role: "member", - }, - ], - }, - }); - } catch (error) { - return c.json({ error: "Team not found" }, 404); - } - }, - ) - .get( - "/:teamId/projects", - describeRoute({ - description: "Get projects for a team", - responses: { - 200: { - description: "Successfully retrieved team projects", - content: { - "application/json": { - schema: resolver(teamResponseSchema), - }, - }, - }, - 401: { - description: "Unauthorized - Invalid or missing token", - content: { - "application/json": { - schema: resolver(teamResponseSchema), - }, - }, - }, - 404: { - description: "Team not found", - content: { - "application/json": { - schema: resolver(teamResponseSchema), - }, - }, - }, - }, - }), - async (c) => { - const user = c.get("user"); - const teamId = c.req.param("teamId"); - - try { - const database = db(c.env.DB); - - // First check if user has access to this team - const teamMember = await database - .select({ - role: members.role, - }) - .from(members) - .where( - eq(members.userId, user?.id) && eq(members.organizationId, teamId), - ) - .get(); - - if (!teamMember) { - return c.json({ error: "You don't have access to this team" }, 403); - } - - const teamProjects = await database - .select({ - id: projects.id, - name: projects.name, - slug: projects.slug, - description: projects.description, - createdAt: projects.createdAt, - updatedAt: projects.updatedAt, - }) - .from(projects) - .where(eq(projects.organizationId, teamId)) - .all(); - - return c.json({ - data: teamProjects, - }); - } catch (error) { - return c.json({ error: "Failed to retrieve team projects" }, 500); - } - }, - ); - -export default app; diff --git a/apps/api/src/routes/teams/schema.ts b/apps/api/src/routes/teams/schema.ts deleted file mode 100644 index 75c1a83..0000000 --- a/apps/api/src/routes/teams/schema.ts +++ /dev/null @@ -1,58 +0,0 @@ -import z from "zod"; -import "zod-openapi/extend"; - -export const teamResponseSchema = z - .object({ - data: z - .object({ - id: z.string().openapi({ - example: "team_123", - description: "Unique identifier for the team", - }), - name: z.string().openapi({ - example: "My Team", - description: "Name of the team", - }), - members: z - .array( - z.object({ - email: z.string().email().openapi({ - example: "user@example.com", - description: "Email address of team member", - }), - role: z.enum(["owner", "member", "pending"]).openapi({ - example: "member", - description: "Role of the team member", - }), - }), - ) - .openapi({ - description: "List of team members", - }), - }) - .nullable() - .openapi({ description: "Team information" }), - error: z.string().optional().openapi({ - example: "Team not found", - description: "Error message if request failed", - }), - }) - .openapi({ ref: "TeamResponse" }); - -export const createTeamSchema = z - .object({ - name: z.string().min(1).openapi({ - example: "My Team", - description: "Name for the new team", - }), - }) - .openapi({ ref: "CreateTeam" }); - -export const inviteSchema = z - .object({ - email: z.string().email().openapi({ - example: "user@example.com", - description: "Email address of user to invite", - }), - }) - .openapi({ ref: "InviteUser" }); diff --git a/apps/api/src/routes/telemetry/index.ts b/apps/api/src/routes/telemetry/index.ts deleted file mode 100644 index c17aeae..0000000 --- a/apps/api/src/routes/telemetry/index.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { Hono } from "@/lib/app"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator as zValidator } from "hono-openapi/zod"; -import { querySchema } from "./schema"; -import { responseSchema } from "./schema"; - -const app = new Hono().get( - "/", - describeRoute({ - description: "Say hello to the user", - responses: { - 200: { - description: "Successful greeting response", - content: { - "text/plain": { - schema: resolver(responseSchema), - }, - }, - }, - }, - }), - zValidator("query", querySchema), - (c) => { - const query = c.req.valid("query"); - return c.json({ data: `Hello ${query?.name ?? "Hono"}!` }); - }, -); - -export default app; diff --git a/apps/api/src/routes/telemetry/schema.ts b/apps/api/src/routes/telemetry/schema.ts deleted file mode 100644 index 122f9be..0000000 --- a/apps/api/src/routes/telemetry/schema.ts +++ /dev/null @@ -1,10 +0,0 @@ -import z from "zod"; -import "zod-openapi/extend"; - -export const querySchema = z - .object({ - name: z.string().optional().openapi({ example: "Steven" }), - }) - .openapi({ ref: "Query" }); - -export const responseSchema = z.string().openapi({ example: "Hello Steven!" }); diff --git a/apps/api/src/routes/translate/index.ts b/apps/api/src/routes/translate/index.ts deleted file mode 100644 index b288a75..0000000 --- a/apps/api/src/routes/translate/index.ts +++ /dev/null @@ -1,121 +0,0 @@ -import { Hono } from "@/lib/app"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator as zValidator } from "hono-openapi/zod"; -import { - batchTextSchema, - chatSchema, - objectSchema, - recognizeSchema, - textSchema, -} from "./schema"; - -const app = new Hono(); - -app - .post( - "/text", - describeRoute({ - description: "Translate text to target language", - responses: { - 200: { - description: "Successfully translated text", - content: { - "application/json": { - schema: resolver(textSchema), - }, - }, - }, - }, - }), - zValidator("json", textSchema), - async (c) => { - const { text, sourceLocale, targetLocale } = await c.req.json(); - return c.json({ data: text }); // Placeholder response - }, - ) - .post( - "/text/batch", - describeRoute({ - description: "Translate text to multiple target languages", - responses: { - 200: { - description: "Successfully translated text to multiple languages", - content: { - "application/json": { - schema: resolver(batchTextSchema), - }, - }, - }, - }, - }), - zValidator("json", batchTextSchema), - async (c) => { - const { text, sourceLocale, targetLocales, fast } = await c.req.json(); - return c.json({ data: [text] }); // Placeholder response - }, - ) - .post( - "/object", - describeRoute({ - description: "Translate object values to target language", - responses: { - 200: { - description: "Successfully translated object", - content: { - "application/json": { - schema: resolver(objectSchema), - }, - }, - }, - }, - }), - zValidator("json", objectSchema), - async (c) => { - const { content, sourceLocale, targetLocale } = await c.req.json(); - return c.json({ data: content }); // Placeholder response - }, - ) - .post( - "/chat", - describeRoute({ - description: "Translate chat messages to target language", - responses: { - 200: { - description: "Successfully translated chat messages", - content: { - "application/json": { - schema: resolver(chatSchema), - }, - }, - }, - }, - }), - zValidator("json", chatSchema), - async (c) => { - const { messages, sourceLocale, targetLocale } = await c.req.json(); - return c.json({ data: messages }); // Placeholder response - }, - ) - .post( - "/recognize", - describeRoute({ - description: "Recognize the locale of provided text", - responses: { - 200: { - description: "Successfully recognized locale", - content: { - "application/json": { - schema: resolver(recognizeSchema), - }, - }, - }, - }, - }), - zValidator("json", recognizeSchema), - async (c) => { - const { text } = await c.req.json(); - return c.json({ data: "en" }); // Placeholder response - }, - ); - -export default app; diff --git a/apps/api/src/routes/translate/schema.ts b/apps/api/src/routes/translate/schema.ts deleted file mode 100644 index f3e683b..0000000 --- a/apps/api/src/routes/translate/schema.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { z } from "zod"; - -export const translationBaseSchema = z.object({ - sourceLocale: z.string(), - targetLocale: z.string(), -}); - -export const batchTranslationSchema = z.object({ - sourceLocale: z.string(), - targetLocales: z.array(z.string()), - fast: z.boolean().optional(), -}); - -export const textSchema = z.object({ - text: z.string(), - ...translationBaseSchema.shape, -}); - -export const batchTextSchema = z.object({ - text: z.string(), - ...batchTranslationSchema.shape, -}); - -export const objectSchema = z.object({ - content: z.record(z.string()), - ...translationBaseSchema.shape, -}); - -export const chatMessageSchema = z.object({ - name: z.string(), - text: z.string(), -}); - -export const chatSchema = z.object({ - messages: z.array(chatMessageSchema), - ...translationBaseSchema.shape, -}); - -export const recognizeSchema = z.object({ - text: z.string(), -}); diff --git a/apps/api/src/routes/users/index.ts b/apps/api/src/routes/users/index.ts deleted file mode 100644 index b016e97..0000000 --- a/apps/api/src/routes/users/index.ts +++ /dev/null @@ -1,139 +0,0 @@ -import { Hono } from "@/lib/app"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator as zValidator } from "hono-openapi/zod"; -import { updateUserBodySchema, userResponseSchema } from "./schema"; - -const app = new Hono(); - -app - .get( - "/me", - describeRoute({ - description: "Get current user profile", - responses: { - 200: { - description: "Successfully retrieved user profile", - content: { - "application/json": { - schema: resolver(userResponseSchema), - }, - }, - }, - 401: { - description: "Unauthorized - Invalid or missing token", - content: { - "application/json": { - schema: resolver(userResponseSchema), - }, - }, - }, - }, - }), - async (c) => { - const session = c.get("session"); - const user = c.get("user"); - - if (!user) return c.body(null, 401); - - return c.json({ - data: { - id: user.id, - email: user.email, - name: user.name, - activeOrganizationId: session.activeOrganizationId, - }, - }); - }, - ) - .patch( - "/me", - describeRoute({ - description: "Update current user profile", - responses: { - 200: { - description: "Successfully updated user profile", - content: { - "application/json": { - schema: resolver(userResponseSchema), - }, - }, - }, - 401: { - description: "Unauthorized - Invalid or missing token", - content: { - "application/json": { - schema: resolver(userResponseSchema), - }, - }, - }, - }, - }), - zValidator("json", updateUserBodySchema), - async (c) => { - const token = c.req.header("Authorization")?.replace("Bearer ", ""); - - if (!token) { - return c.json({ error: "No token provided" }, 401); - } - - try { - const body = await c.req.valid("json"); - // TODO: Implement user profile update logic - return c.json({ - data: { - email: body.email, - name: body.name, - provider: "github", - }, - }); - } catch (error) { - return c.json({ error: "Failed to update user profile" }, 401); - } - }, - ) - .delete( - "/me", - describeRoute({ - description: "Delete current user account", - responses: { - 200: { - description: "Successfully deleted user account", - content: { - "application/json": { - schema: resolver(userResponseSchema), - }, - }, - }, - 401: { - description: "Unauthorized - Invalid or missing token", - content: { - "application/json": { - schema: resolver(userResponseSchema), - }, - }, - }, - }, - }), - async (c) => { - const token = c.req.header("Authorization")?.replace("Bearer ", ""); - - if (!token) { - return c.json({ error: "No token provided" }, 401); - } - - try { - // TODO: Implement user account deletion logic - return c.json({ - data: { - email: "", - name: "", - provider: "github", - }, - }); - } catch (error) { - return c.json({ error: "Failed to delete user account" }, 401); - } - }, - ); - -export default app; diff --git a/apps/api/src/routes/users/schema.ts b/apps/api/src/routes/users/schema.ts deleted file mode 100644 index b975399..0000000 --- a/apps/api/src/routes/users/schema.ts +++ /dev/null @@ -1,44 +0,0 @@ -import z from "zod"; -import "zod-openapi/extend"; - -export const userResponseSchema = z - .object({ - data: z - .object({ - email: z.string().email().openapi({ - example: "user@example.com", - description: "User's email address", - }), - name: z.string().openapi({ - example: "John Doe", - description: "User's full name", - }), - provider: z.enum(["github", "google"]).openapi({ - example: "github", - description: "OAuth provider used for authentication", - }), - }) - .nullable() - .openapi({ description: "User profile information" }), - error: z.string().optional().openapi({ - example: "Failed to get user profile", - description: "Error message if request failed", - }), - }) - .openapi({ ref: "UserResponse" }); - -export const updateUserBodySchema = z - .object({ - name: z.string().min(1).optional().openapi({ - example: "John Doe", - description: "New name to update user profile with", - }), - email: z.string().email().optional().openapi({ - example: "user@example.com", - description: "New email to update user profile with", - }), - }) - .refine((data) => data.name || data.email, { - message: "At least one field must be provided", - }) - .openapi({ ref: "UpdateUserBody" }); diff --git a/apps/api/tsconfig.json b/apps/api/tsconfig.json deleted file mode 100644 index 3e119e1..0000000 --- a/apps/api/tsconfig.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "compilerOptions": { - "target": "ESNext", - "module": "ESNext", - "moduleResolution": "Bundler", - "strict": true, - "skipLibCheck": true, - "jsx": "react-jsx", - "jsxImportSource": "react", - "lib": [ - "ESNext" - ], - "types": [ - "@cloudflare/workers-types/2023-07-01", - "bun-types" - ], - "baseUrl": ".", - "paths": { - "@/*": ["src/*"] - } - }, -} \ No newline at end of file diff --git a/apps/api/wrangler.json b/apps/api/wrangler.json deleted file mode 100644 index aeaf77a..0000000 --- a/apps/api/wrangler.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "name": "languine-api", - "main": "src/index.ts", - "compatibility_date": "2024-12-29", - "workers_dev": true, - "compatibility_flags": ["nodejs_compat"], - "placement": { - "mode": "smart" - }, - "d1_databases": [ - { - "binding": "DB", - "database_name": "languine-development", - "database_id": "development", - "migrations_dir": "./drizzle" - } - ], - "env": { - "production": { - "name": "languine-api", - "route": { - "pattern": "api.languine.ai/*", - "zone_name": "languine.ai" - }, - "d1_databases": [ - { - "binding": "DB", - "database_name": "languine-production", - "database_id": "5f6dc91e-405e-423a-af2f-eca985fb86ec", - "migrations_dir": "./drizzle" - } - ] - }, - "staging": { - "name": "languine-api-staging", - "route": { - "pattern": "api-staging.languine.ai/*", - "zone_name": "languine.ai" - }, - "d1_databases": [ - { - "binding": "DB", - "database_name": "languine-staging", - "database_id": "", - "migrations_dir": "./drizzle" - } - ] - } - } -} \ No newline at end of file diff --git a/apps/web/.env-example b/apps/web/.env-example index 8d78362..cf7da71 100644 --- a/apps/web/.env-example +++ b/apps/web/.env-example @@ -1,3 +1,6 @@ NEXT_PUBLIC_API_ENDPOINT=http://localhost:3002 OPEN_PANEL_CLIENT_SECRET= NEXT_PUBLIC_OPEN_PANEL_CLIENT_ID= +TURSO_DATABASE_URL= +TURSO_AUTH_TOKEN= +RESEND_API_KEY= \ No newline at end of file diff --git a/apps/web/drizzle.config.ts b/apps/web/drizzle.config.ts new file mode 100644 index 0000000..95edb28 --- /dev/null +++ b/apps/web/drizzle.config.ts @@ -0,0 +1,13 @@ +require("dotenv").config(); + +import type { Config } from "drizzle-kit"; + +export default { + schema: "./src/db/schema.ts", + out: "./migrations", + dialect: "turso", + dbCredentials: { + url: process.env.TURSO_DATABASE_URL!, + authToken: process.env.TURSO_AUTH_TOKEN, + }, +} satisfies Config; diff --git a/apps/api/drizzle/0000_busy_mandrill.sql b/apps/web/drizzle/0000_busy_mandrill.sql similarity index 100% rename from apps/api/drizzle/0000_busy_mandrill.sql rename to apps/web/drizzle/0000_busy_mandrill.sql diff --git a/apps/api/drizzle/0001_steep_scrambler.sql b/apps/web/drizzle/0001_steep_scrambler.sql similarity index 100% rename from apps/api/drizzle/0001_steep_scrambler.sql rename to apps/web/drizzle/0001_steep_scrambler.sql diff --git a/apps/api/drizzle/meta/0000_snapshot.json b/apps/web/drizzle/meta/0000_snapshot.json similarity index 100% rename from apps/api/drizzle/meta/0000_snapshot.json rename to apps/web/drizzle/meta/0000_snapshot.json diff --git a/apps/api/drizzle/meta/0001_snapshot.json b/apps/web/drizzle/meta/0001_snapshot.json similarity index 100% rename from apps/api/drizzle/meta/0001_snapshot.json rename to apps/web/drizzle/meta/0001_snapshot.json diff --git a/apps/api/drizzle/meta/_journal.json b/apps/web/drizzle/meta/_journal.json similarity index 100% rename from apps/api/drizzle/meta/_journal.json rename to apps/web/drizzle/meta/_journal.json diff --git a/apps/web/migrations/0000_sad_red_shift.sql b/apps/web/migrations/0000_sad_red_shift.sql new file mode 100644 index 0000000..2ebca14 --- /dev/null +++ b/apps/web/migrations/0000_sad_red_shift.sql @@ -0,0 +1,129 @@ +CREATE TABLE `accounts` ( + `id` text PRIMARY KEY NOT NULL, + `account_id` text NOT NULL, + `provider_id` text NOT NULL, + `user_id` text NOT NULL, + `access_token` text, + `refresh_token` text, + `id_token` text, + `access_token_expires_at` integer, + `refresh_token_expires_at` integer, + `scope` text, + `password` text, + `created_at` integer NOT NULL, + `updated_at` integer NOT NULL, + FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade +); +--> statement-breakpoint +CREATE INDEX `accounts_user_id_idx` ON `accounts` (`user_id`);--> statement-breakpoint +CREATE INDEX `provider_compound_idx` ON `accounts` (`provider_id`,`account_id`);--> statement-breakpoint +CREATE TABLE `invitations` ( + `id` text PRIMARY KEY NOT NULL, + `organization_id` text NOT NULL, + `email` text NOT NULL, + `role` text, + `status` text NOT NULL, + `expires_at` integer NOT NULL, + `inviter_id` text NOT NULL, + FOREIGN KEY (`organization_id`) REFERENCES `organizations`(`id`) ON UPDATE no action ON DELETE cascade, + FOREIGN KEY (`inviter_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade +); +--> statement-breakpoint +CREATE INDEX `org_email_idx` ON `invitations` (`organization_id`,`email`);--> statement-breakpoint +CREATE INDEX `invitations_expires_at_idx` ON `invitations` (`expires_at`);--> statement-breakpoint +CREATE TABLE `members` ( + `id` text PRIMARY KEY NOT NULL, + `organization_id` text NOT NULL, + `user_id` text NOT NULL, + `role` text NOT NULL, + `created_at` integer NOT NULL, + FOREIGN KEY (`organization_id`) REFERENCES `organizations`(`id`) ON UPDATE no action ON DELETE cascade, + FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade +); +--> statement-breakpoint +CREATE INDEX `org_user_idx` ON `members` (`organization_id`,`user_id`);--> statement-breakpoint +CREATE TABLE `organizations` ( + `id` text PRIMARY KEY NOT NULL, + `name` text NOT NULL, + `slug` text, + `logo` text, + `plan` text DEFAULT 'free' NOT NULL, + `api_key` text NOT NULL, + `created_at` integer NOT NULL, + `metadata` text +); +--> statement-breakpoint +CREATE UNIQUE INDEX `organizations_slug_unique` ON `organizations` (`slug`);--> statement-breakpoint +CREATE UNIQUE INDEX `organizations_api_key_unique` ON `organizations` (`api_key`);--> statement-breakpoint +CREATE INDEX `slug_idx` ON `organizations` (`slug`);--> statement-breakpoint +CREATE INDEX `org_api_key_idx` ON `organizations` (`api_key`);--> statement-breakpoint +CREATE TABLE `project_settings` ( + `id` text PRIMARY KEY NOT NULL, + `project_id` text NOT NULL, + `cache` integer DEFAULT true NOT NULL, + `context` integer DEFAULT true NOT NULL, + `temperature` real DEFAULT 0 NOT NULL, + `instructions` text, + `memory` integer DEFAULT true NOT NULL, + `grammar` integer DEFAULT true NOT NULL, + `created_at` integer NOT NULL, + `updated_at` integer NOT NULL, + FOREIGN KEY (`project_id`) REFERENCES `projects`(`id`) ON UPDATE no action ON DELETE cascade +); +--> statement-breakpoint +CREATE INDEX `project_idx` ON `project_settings` (`project_id`);--> statement-breakpoint +CREATE TABLE `projects` ( + `id` text PRIMARY KEY NOT NULL, + `name` text NOT NULL, + `slug` text NOT NULL, + `description` text, + `organization_id` text NOT NULL, + `created_at` integer NOT NULL, + `updated_at` integer, + FOREIGN KEY (`organization_id`) REFERENCES `organizations`(`id`) ON UPDATE no action ON DELETE cascade +); +--> statement-breakpoint +CREATE INDEX `org_idx` ON `projects` (`organization_id`);--> statement-breakpoint +CREATE UNIQUE INDEX `slug_org_idx` ON `projects` (`slug`,`organization_id`);--> statement-breakpoint +CREATE TABLE `sessions` ( + `id` text PRIMARY KEY NOT NULL, + `expires_at` integer NOT NULL, + `token` text NOT NULL, + `created_at` integer NOT NULL, + `updated_at` integer NOT NULL, + `ip_address` text, + `user_agent` text, + `user_id` text NOT NULL, + `active_organization_id` text, + FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade +); +--> statement-breakpoint +CREATE UNIQUE INDEX `sessions_token_unique` ON `sessions` (`token`);--> statement-breakpoint +CREATE INDEX `user_id_idx` ON `sessions` (`user_id`);--> statement-breakpoint +CREATE INDEX `token_idx` ON `sessions` (`token`);--> statement-breakpoint +CREATE INDEX `expires_at_idx` ON `sessions` (`expires_at`);--> statement-breakpoint +CREATE TABLE `users` ( + `id` text PRIMARY KEY NOT NULL, + `name` text NOT NULL, + `email` text NOT NULL, + `email_verified` integer NOT NULL, + `image` text, + `api_key` text NOT NULL, + `created_at` integer NOT NULL, + `updated_at` integer NOT NULL +); +--> statement-breakpoint +CREATE UNIQUE INDEX `users_email_unique` ON `users` (`email`);--> statement-breakpoint +CREATE UNIQUE INDEX `users_api_key_unique` ON `users` (`api_key`);--> statement-breakpoint +CREATE INDEX `email_idx` ON `users` (`email`);--> statement-breakpoint +CREATE TABLE `verifications` ( + `id` text PRIMARY KEY NOT NULL, + `identifier` text NOT NULL, + `value` text NOT NULL, + `expires_at` integer NOT NULL, + `created_at` integer, + `updated_at` integer +); +--> statement-breakpoint +CREATE INDEX `identifier_idx` ON `verifications` (`identifier`);--> statement-breakpoint +CREATE INDEX `verifications_expires_at_idx` ON `verifications` (`expires_at`); \ No newline at end of file diff --git a/apps/web/migrations/meta/0000_snapshot.json b/apps/web/migrations/meta/0000_snapshot.json new file mode 100644 index 0000000..62b5563 --- /dev/null +++ b/apps/web/migrations/meta/0000_snapshot.json @@ -0,0 +1,895 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "9d8306a6-bd63-4a9a-95ff-88847f376235", + "prevId": "00000000-0000-0000-0000-000000000000", + "tables": { + "accounts": { + "name": "accounts", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "accounts_user_id_idx": { + "name": "accounts_user_id_idx", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "provider_compound_idx": { + "name": "provider_compound_idx", + "columns": [ + "provider_id", + "account_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "accounts_user_id_users_id_fk": { + "name": "accounts_user_id_users_id_fk", + "tableFrom": "accounts", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "invitations": { + "name": "invitations", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "inviter_id": { + "name": "inviter_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "org_email_idx": { + "name": "org_email_idx", + "columns": [ + "organization_id", + "email" + ], + "isUnique": false + }, + "invitations_expires_at_idx": { + "name": "invitations_expires_at_idx", + "columns": [ + "expires_at" + ], + "isUnique": false + } + }, + "foreignKeys": { + "invitations_organization_id_organizations_id_fk": { + "name": "invitations_organization_id_organizations_id_fk", + "tableFrom": "invitations", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "invitations_inviter_id_users_id_fk": { + "name": "invitations_inviter_id_users_id_fk", + "tableFrom": "invitations", + "tableTo": "users", + "columnsFrom": [ + "inviter_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "members": { + "name": "members", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "org_user_idx": { + "name": "org_user_idx", + "columns": [ + "organization_id", + "user_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "members_organization_id_organizations_id_fk": { + "name": "members_organization_id_organizations_id_fk", + "tableFrom": "members", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "members_user_id_users_id_fk": { + "name": "members_user_id_users_id_fk", + "tableFrom": "members", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "organizations": { + "name": "organizations", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "logo": { + "name": "logo", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "plan": { + "name": "plan", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'free'" + }, + "api_key": { + "name": "api_key", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "organizations_slug_unique": { + "name": "organizations_slug_unique", + "columns": [ + "slug" + ], + "isUnique": true + }, + "organizations_api_key_unique": { + "name": "organizations_api_key_unique", + "columns": [ + "api_key" + ], + "isUnique": true + }, + "slug_idx": { + "name": "slug_idx", + "columns": [ + "slug" + ], + "isUnique": false + }, + "org_api_key_idx": { + "name": "org_api_key_idx", + "columns": [ + "api_key" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "project_settings": { + "name": "project_settings", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "project_id": { + "name": "project_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "cache": { + "name": "cache", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "context": { + "name": "context", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "temperature": { + "name": "temperature", + "type": "real", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "instructions": { + "name": "instructions", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "memory": { + "name": "memory", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "grammar": { + "name": "grammar", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "project_idx": { + "name": "project_idx", + "columns": [ + "project_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "project_settings_project_id_projects_id_fk": { + "name": "project_settings_project_id_projects_id_fk", + "tableFrom": "project_settings", + "tableTo": "projects", + "columnsFrom": [ + "project_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "projects": { + "name": "projects", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "org_idx": { + "name": "org_idx", + "columns": [ + "organization_id" + ], + "isUnique": false + }, + "slug_org_idx": { + "name": "slug_org_idx", + "columns": [ + "slug", + "organization_id" + ], + "isUnique": true + } + }, + "foreignKeys": { + "projects_organization_id_organizations_id_fk": { + "name": "projects_organization_id_organizations_id_fk", + "tableFrom": "projects", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "sessions": { + "name": "sessions", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "active_organization_id": { + "name": "active_organization_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "sessions_token_unique": { + "name": "sessions_token_unique", + "columns": [ + "token" + ], + "isUnique": true + }, + "user_id_idx": { + "name": "user_id_idx", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "token_idx": { + "name": "token_idx", + "columns": [ + "token" + ], + "isUnique": false + }, + "expires_at_idx": { + "name": "expires_at_idx", + "columns": [ + "expires_at" + ], + "isUnique": false + } + }, + "foreignKeys": { + "sessions_user_id_users_id_fk": { + "name": "sessions_user_id_users_id_fk", + "tableFrom": "sessions", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "users": { + "name": "users", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email_verified": { + "name": "email_verified", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "api_key": { + "name": "api_key", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "users_email_unique": { + "name": "users_email_unique", + "columns": [ + "email" + ], + "isUnique": true + }, + "users_api_key_unique": { + "name": "users_api_key_unique", + "columns": [ + "api_key" + ], + "isUnique": true + }, + "email_idx": { + "name": "email_idx", + "columns": [ + "email" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "verifications": { + "name": "verifications", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "identifier_idx": { + "name": "identifier_idx", + "columns": [ + "identifier" + ], + "isUnique": false + }, + "verifications_expires_at_idx": { + "name": "verifications_expires_at_idx", + "columns": [ + "expires_at" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + } + }, + "views": {}, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "indexes": {} + } +} \ No newline at end of file diff --git a/apps/web/migrations/meta/_journal.json b/apps/web/migrations/meta/_journal.json new file mode 100644 index 0000000..783e673 --- /dev/null +++ b/apps/web/migrations/meta/_journal.json @@ -0,0 +1,13 @@ +{ + "version": "7", + "dialect": "sqlite", + "entries": [ + { + "idx": 0, + "version": "6", + "when": 1736332317965, + "tag": "0000_sad_red_shift", + "breakpoints": true + } + ] +} \ No newline at end of file diff --git a/apps/web/package.json b/apps/web/package.json index f006dc3..f7c667c 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -7,10 +7,15 @@ "build": "next build", "start": "next start", "lint": "next lint", - "typecheck": "tsc --noEmit" + "typecheck": "tsc --noEmit", + "email:dev": "email dev --port 3002 --dir src/emails/templates", + "db:generate": "drizzle-kit generate", + "db:migrate": "drizzle-kit migrate", + "db:studio": "drizzle-kit studio" }, "dependencies": { "@hookform/resolvers": "^3.10.0", + "@libsql/client": "^0.14.0", "@openpanel/nextjs": "^1.0.7", "@radix-ui/react-alert-dialog": "^1.1.4", "@radix-ui/react-avatar": "^1.1.2", @@ -25,13 +30,22 @@ "@radix-ui/react-switch": "^1.1.2", "@radix-ui/react-tabs": "^1.1.2", "@radix-ui/react-tooltip": "^1.1.6", + "@tanstack/react-query": "^5.62.16", + "@trpc/client": "^11.0.0-rc.688", + "@react-email/components": "0.0.32", + "@react-email/font": "^0.0.9", + "@trpc/react-query": "^11.0.0-rc.688", + "@trpc/server": "^11.0.0-rc.688", "better-auth": "^1.1.10", "class-variance-authority": "^0.7.1", + "client-only": "^0.0.1", "clsx": "^2.1.1", + "dotenv": "^16.4.7", + "drizzle-orm": "^0.38.3", "input-otp": "^1.4.2", "lucide-react": "^0.469.0", "motion": "^11.16.0", - "next": "15.1.3", + "next": "15.1.4", "next-international": "^1.3.1", "next-safe-action": "^7.10.2", "next-themes": "^0.4.4", @@ -41,13 +55,17 @@ "react-hook-form": "^7.54.2", "react-icons": "^5.4.0", "recharts": "^2.15.0", + "server-only": "^0.0.1", "sonner": "^1.7.1", + "superjson": "^2.2.2", "tailwind-merge": "^2.6.0", "zod": "^3.24.1" }, "devDependencies": { + "@tanstack/react-query-devtools": "^5.62.16", "@types/node": "^22", "@types/react": "^19", + "react-email": "3.0.5", "@types/react-dom": "^19", "languine": "^1.0.2", "postcss": "^8", diff --git a/apps/web/src/app/[locale]/(dashboard)/login/page.tsx b/apps/web/src/app/[locale]/(dashboard)/login/page.tsx index e158255..34980f0 100644 --- a/apps/web/src/app/[locale]/(dashboard)/login/page.tsx +++ b/apps/web/src/app/[locale]/(dashboard)/login/page.tsx @@ -3,11 +3,9 @@ import Login from "@/components/login"; import { Logo } from "@/components/logo"; import MatrixTextWall from "@/components/matrix"; import { StackedCode } from "@/components/stacked-code"; -import { getOrganization } from "@/lib/queries"; import { getI18n } from "@/locales/server"; import type { Metadata } from "next"; import Link from "next/link"; -import { redirect } from "next/navigation"; export async function generateMetadata(): Promise { const t = await getI18n(); @@ -19,14 +17,8 @@ export async function generateMetadata(): Promise { } export default async function Page() { - const org = await getOrganization(); - const project = "default"; const t = await getI18n(); - if (org.data) { - redirect(`/${org.data.slug}/${project}`); - } - return (
diff --git a/apps/web/src/app/[locale]/layout.tsx b/apps/web/src/app/[locale]/layout.tsx index 90defc5..fddde6c 100644 --- a/apps/web/src/app/[locale]/layout.tsx +++ b/apps/web/src/app/[locale]/layout.tsx @@ -1,10 +1,11 @@ +import "../globals.css"; + +import { I18nProviderClient } from "@/locales/client"; +import { TRPCProvider } from "@/trpc/client"; import { OpenPanelComponent } from "@openpanel/nextjs"; import type { Metadata } from "next"; import { Geist_Mono } from "next/font/google"; -import "../globals.css"; -import { I18nProviderClient } from "@/locales/client"; - const geistMono = Geist_Mono({ variable: "--font-geist-mono", subsets: ["latin"], @@ -34,7 +35,9 @@ export default async function RootLayout({ disabled={process.env.NODE_ENV !== "production"} /> - {children} + + {children} + ); diff --git a/apps/web/src/app/api/auth/[...all]/route.ts b/apps/web/src/app/api/auth/[...all]/route.ts new file mode 100644 index 0000000..78eb754 --- /dev/null +++ b/apps/web/src/app/api/auth/[...all]/route.ts @@ -0,0 +1,4 @@ +import { auth } from "@/lib/auth/server"; +import { toNextJsHandler } from "better-auth/next-js"; + +export const { GET, POST } = toNextJsHandler(auth.handler); diff --git a/apps/web/src/app/api/trpc/[trpc] /route.ts b/apps/web/src/app/api/trpc/[trpc] /route.ts new file mode 100644 index 0000000..5cb0560 --- /dev/null +++ b/apps/web/src/app/api/trpc/[trpc] /route.ts @@ -0,0 +1,13 @@ +import { createTRPCContext } from "@/trpc/init"; +import { appRouter } from "@/trpc/routers/_app"; +import { fetchRequestHandler } from "@trpc/server/adapters/fetch"; + +const handler = (req: Request) => + fetchRequestHandler({ + endpoint: "/api/trpc", + req, + router: appRouter, + createContext: createTRPCContext, + }); + +export { handler as GET, handler as POST }; diff --git a/apps/web/src/components/dashboard/header.tsx b/apps/web/src/components/dashboard/header.tsx index c50eec0..40e2d85 100644 --- a/apps/web/src/components/dashboard/header.tsx +++ b/apps/web/src/components/dashboard/header.tsx @@ -2,17 +2,13 @@ import { ChangeLanguage } from "@/components/change-language"; import { TeamSelectorServer } from "@/components/team-selector.server"; import { UserMenu } from "@/components/user-menu"; import Link from "next/link"; -import { Suspense } from "react"; import { MdOutlineBook } from "react-icons/md"; -import { Skeleton } from "../ui/skeleton"; export function Header() { return (
- }> - - +
diff --git a/apps/web/src/components/github-sign-in.tsx b/apps/web/src/components/github-sign-in.tsx index b12ea90..0b46e6c 100644 --- a/apps/web/src/components/github-sign-in.tsx +++ b/apps/web/src/components/github-sign-in.tsx @@ -2,7 +2,7 @@ import { OutlinedButton } from "@/components/ui/outlined-button"; import { Spinner } from "@/components/ui/spinner"; -import { auth } from "@/lib/auth"; +import { authClient } from "@/lib/auth/client"; import { useI18n } from "@/locales/client"; import { useState } from "react"; import { FaGithub } from "react-icons/fa"; @@ -14,7 +14,7 @@ export default function GithubSignIn() { const handleGithubLogin = async () => { setIsLoading(true); try { - await auth.signIn.social({ + await authClient.signIn.social({ provider: "github", callbackURL: `${window.location.origin}/login`, }); diff --git a/apps/web/src/components/google-sign-in.tsx b/apps/web/src/components/google-sign-in.tsx index 5099c74..2caee57 100644 --- a/apps/web/src/components/google-sign-in.tsx +++ b/apps/web/src/components/google-sign-in.tsx @@ -2,7 +2,7 @@ import { OutlinedButton } from "@/components/ui/outlined-button"; import { Spinner } from "@/components/ui/spinner"; -import { auth } from "@/lib/auth"; +import { authClient } from "@/lib/auth/client"; import { useI18n } from "@/locales/client"; import { useState } from "react"; import { FaGoogle } from "react-icons/fa"; @@ -16,7 +16,7 @@ export default function GoogleSignIn() { setIsLoading(true); try { - await auth.signIn.social({ + await authClient.signIn.social({ provider: "google", callbackURL: `${window.location.origin}/login`, }); diff --git a/apps/web/src/components/login.tsx b/apps/web/src/components/login.tsx index 039ef4c..6d7986b 100644 --- a/apps/web/src/components/login.tsx +++ b/apps/web/src/components/login.tsx @@ -12,10 +12,10 @@ export default async function Login() {

{t("login.description")}

- +
- +
@@ -35,4 +35,4 @@ export default async function Login() {
); -} \ No newline at end of file +} diff --git a/apps/web/src/components/sign-in.tsx b/apps/web/src/components/sign-in.tsx index c982f01..9f93697 100644 --- a/apps/web/src/components/sign-in.tsx +++ b/apps/web/src/components/sign-in.tsx @@ -1,12 +1,12 @@ "use client"; -import { auth } from "@/lib/auth"; +import { authClient } from "@/lib/auth/client"; import { useI18n } from "@/locales/client"; import Link from "next/link"; export function SignIn() { const t = useI18n(); - const { data: session } = auth.useSession(); + const { data: session } = authClient.useSession(); return ( diff --git a/apps/web/src/components/team-selector.server.tsx b/apps/web/src/components/team-selector.server.tsx index e07e527..5f23bdd 100644 --- a/apps/web/src/components/team-selector.server.tsx +++ b/apps/web/src/components/team-selector.server.tsx @@ -1,8 +1,16 @@ -import { getTeams } from "@/lib/queries"; +import { Skeleton } from "@/components/ui/skeleton"; +import { HydrateClient, trpc } from "@/trpc/server"; +import { Suspense } from "react"; import { TeamSelector } from "./team-selector"; export async function TeamSelectorServer() { - const teams = await getTeams(); + trpc.organization.getAll.prefetch(); - return ; + return ( + + }> + + + + ); } diff --git a/apps/web/src/components/team-selector.tsx b/apps/web/src/components/team-selector.tsx index 20ae89d..31a54f9 100644 --- a/apps/web/src/components/team-selector.tsx +++ b/apps/web/src/components/team-selector.tsx @@ -14,26 +14,28 @@ import { PopoverContent, PopoverTrigger, } from "@/components/ui/popover"; -import type { GetTeamsResponse } from "@/lib/queries"; import { useI18n } from "@/locales/client"; +import { trpc } from "@/trpc/client"; import { Check, Plus, Settings } from "lucide-react"; import { useParams, useRouter } from "next/navigation"; import * as React from "react"; -export function TeamSelector({ teams }: { teams: GetTeamsResponse }) { +export function TeamSelector() { const t = useI18n(); const params = useParams(); const router = useRouter(); + const [organizations] = trpc.organization.getAll.useSuspenseQuery(); + const teamId = params.team; const projectSlug = params.project; - const currentTeam = teams - ? teams.find((team) => team?.id === teamId) || teams.at(0) + const currentTeam = organizations + ? organizations.find((org) => org?.id === teamId) || organizations.at(0) : null; - const currentProject = currentTeam?.projects.find( - (project) => project.slug === projectSlug, + const currentProject = currentTeam?.projects?.find( + (project: { slug: string }) => project.slug === projectSlug, ); const [open, setOpen] = React.useState(false); @@ -61,16 +63,16 @@ export function TeamSelector({ teams }: { teams: GetTeamsResponse }) { {t("teamSelector.teams")}
- {teams.map((team) => ( + {organizations.map((org) => (
{ - router.push(`/${team?.id}/${projectSlug}`); + router.push(`/${org?.id}/${projectSlug}`); setOpen(false); }} > - {team?.name} + {org?.name}
- {currentTeam?.id === team?.id && ( + {currentTeam?.id === org?.id && ( )}
diff --git a/apps/web/src/components/user-menu.tsx b/apps/web/src/components/user-menu.tsx index b881e76..f7cf499 100644 --- a/apps/web/src/components/user-menu.tsx +++ b/apps/web/src/components/user-menu.tsx @@ -8,19 +8,19 @@ import { DropdownMenuSeparator, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; -import { auth } from "@/lib/auth"; +import { authClient } from "@/lib/auth/client"; import { useI18n } from "@/locales/client"; import Link from "next/link"; import { useParams, useRouter } from "next/navigation"; export function UserMenu() { - const { data: session } = auth.useSession(); + const { data: session } = authClient.useSession(); const params = useParams(); const t = useI18n(); const router = useRouter(); const handleSignOut = async () => { - await auth.signOut({ + await authClient.signOut({ fetchOptions: { onSuccess: () => { router.push("/"); diff --git a/apps/web/src/db/index.ts b/apps/web/src/db/index.ts new file mode 100644 index 0000000..a0af0d4 --- /dev/null +++ b/apps/web/src/db/index.ts @@ -0,0 +1,9 @@ +import { createClient } from "@libsql/client"; +import { drizzle } from "drizzle-orm/libsql"; + +const turso = createClient({ + url: process.env.TURSO_DATABASE_URL!, + authToken: process.env.TURSO_AUTH_TOKEN, +}); + +export const db = drizzle(turso); diff --git a/apps/api/README.md b/apps/web/src/db/queries/delete.ts similarity index 100% rename from apps/api/README.md rename to apps/web/src/db/queries/delete.ts diff --git a/apps/web/src/db/queries/insert.ts b/apps/web/src/db/queries/insert.ts new file mode 100644 index 0000000..9f2534e --- /dev/null +++ b/apps/web/src/db/queries/insert.ts @@ -0,0 +1,42 @@ +import { db } from "@/db"; +import { members, organizations, projects, sessions } from "@/db/schema"; +import { createId } from "@paralleldrive/cuid2"; +import { eq } from "drizzle-orm"; +import slugify from "slugify"; + +export async function createDefaultOrganization(user: { + id: string; + name: string; +}) { + // Create default organization for new user + const org = await db + .insert(organizations) + .values({ + name: user.name, + slug: `${slugify(user.name, { lower: true })}-${createId().slice(0, 8)}`, + }) + .returning() + .get(); + + // Add user as member of organization + await db.insert(members).values({ + userId: user.id, + organizationId: org.id, + role: "owner", + }); + + // Create default project for new organization + await db.insert(projects).values({ + name: "Default", + organizationId: org.id, + slug: "default", + }); + + // Set active organization for new user's session + await db + .update(sessions) + .set({ activeOrganizationId: org.id }) + .where(eq(sessions.userId, user.id)); + + return org; +} diff --git a/apps/web/src/db/queries/select.ts b/apps/web/src/db/queries/select.ts new file mode 100644 index 0000000..ec47de1 --- /dev/null +++ b/apps/web/src/db/queries/select.ts @@ -0,0 +1,34 @@ +import { db } from "@/db"; +import { eq } from "drizzle-orm"; +import { members, organizations, projects } from "../schema"; + +export const getDefaultOrganization = async (userId: string) => { + return await db + .select() + .from(members) + .where(eq(members.userId, userId)) + .leftJoin(organizations, eq(organizations.id, members.organizationId)) + .limit(1) + .get(); +}; + +export const getAllOrganizationsWithProjects = async () => { + const orgs = await db.select().from(organizations).all(); + + const orgsWithProjects = await Promise.all( + orgs.map(async (org) => { + const orgProjects = await db + .select() + .from(projects) + .where(eq(projects.organizationId, org.id)) + .all(); + + return { + ...org, + projects: orgProjects, + }; + }), + ); + + return orgsWithProjects; +}; diff --git a/apps/web/src/db/queries/update.ts b/apps/web/src/db/queries/update.ts new file mode 100644 index 0000000..e69de29 diff --git a/apps/api/src/db/schema.ts b/apps/web/src/db/schema.ts similarity index 100% rename from apps/api/src/db/schema.ts rename to apps/web/src/db/schema.ts diff --git a/apps/api/src/emails/components/footer.tsx b/apps/web/src/emails/components/footer.tsx similarity index 81% rename from apps/api/src/emails/components/footer.tsx rename to apps/web/src/emails/components/footer.tsx index a296c67..b692ac9 100644 --- a/apps/api/src/emails/components/footer.tsx +++ b/apps/web/src/emails/components/footer.tsx @@ -1,11 +1,13 @@ -import { getAppUrl } from "@/lib/envs"; +import { getAppUrl } from "@/lib/url"; import { Column, Img, Link, Row, Section, Text } from "@react-email/components"; +const appUrl = getAppUrl(); + export function Footer() { return (
Separator - X + X @@ -36,7 +33,7 @@ export function Footer() { className="text-black no-underline text-xl" > GitHub Languine Logo(process.env.NEXT_PUBLIC_API_ENDPOINT!, { - fetch: async (input: RequestInfo | URL, init?: RequestInit) => { - const requestHeaders = await getCookieHeaders(); - - return fetch(input, { - ...init, - credentials: "include", - headers: { - ...init?.headers, - ...requestHeaders, - }, - }); - }, -}); diff --git a/apps/web/src/lib/auth.ts b/apps/web/src/lib/auth.ts deleted file mode 100644 index 76bf3b5..0000000 --- a/apps/web/src/lib/auth.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { organizationClient } from "better-auth/client/plugins"; -import { nextCookies } from "better-auth/next-js"; -import { createAuthClient } from "better-auth/react"; - -export const auth = createAuthClient({ - baseURL: process.env.NEXT_PUBLIC_API_ENDPOINT, - basePath: "/auth", - socialProviders: { - github: { - clientId: process.env.GITHUB_CLIENT_ID, - clientSecret: process.env.GITHUB_CLIENT_SECRET, - }, - }, - session: { - cookieCache: { - enabled: true, - maxAge: 5 * 60, - }, - }, - advanced: { - crossSubDomainCookies: { - enabled: true, - }, - }, - plugins: [nextCookies(), organizationClient()], -}); diff --git a/apps/web/src/lib/auth/client.ts b/apps/web/src/lib/auth/client.ts new file mode 100644 index 0000000..89e659d --- /dev/null +++ b/apps/web/src/lib/auth/client.ts @@ -0,0 +1,14 @@ +import { organizationClient } from "better-auth/client/plugins"; +import { nextCookies } from "better-auth/next-js"; +import { createAuthClient } from "better-auth/react"; + +export const authClient = createAuthClient({ + baseURL: process.env.BETTER_AUTH_BASE_URL, + session: { + cookieCache: { + enabled: true, + maxAge: 5 * 60, + }, + }, + plugins: [nextCookies(), organizationClient()], +}); diff --git a/apps/web/src/lib/auth/middleware.ts b/apps/web/src/lib/auth/middleware.ts new file mode 100644 index 0000000..93d0cf0 --- /dev/null +++ b/apps/web/src/lib/auth/middleware.ts @@ -0,0 +1,24 @@ +import type { Session, User } from "better-auth"; +import { headers } from "next/headers"; +import { cache } from "react"; + +type SessionData = { + user: User; + session: Session; +}; + +export const getSessionFromRequest = cache( + async (): Promise => { + return await fetch( + `${process.env.BETTER_AUTH_BASE_URL}/api/auth/get-session`, + { + headers: { + cookie: (await headers()).get("cookie") || "", + }, + }, + ).then(async (res) => { + if (!res.ok) return null; + return res.json(); + }); + }, +); diff --git a/apps/web/src/lib/auth/server.ts b/apps/web/src/lib/auth/server.ts new file mode 100644 index 0000000..bd8ab57 --- /dev/null +++ b/apps/web/src/lib/auth/server.ts @@ -0,0 +1,71 @@ +import { db } from "@/db"; +import { createDefaultOrganization } from "@/db/queries/insert"; +import { getDefaultOrganization } from "@/db/queries/select"; +import * as schema from "@/db/schema"; +import WelcomeEmail from "@/emails/templates/welcome"; +import { resend } from "@/lib/resend"; +import { betterAuth } from "better-auth"; +import { drizzleAdapter } from "better-auth/adapters/drizzle"; +import { organization } from "better-auth/plugins"; + +export const auth = betterAuth({ + database: drizzleAdapter(db, { + provider: "sqlite", + usePlural: true, + schema, + }), + secret: process.env.BETTER_AUTH_SECRET, + baseURL: process.env.BETTER_AUTH_BASE_URL, + socialProviders: { + github: { + clientId: process.env.GITHUB_CLIENT_ID!, + clientSecret: process.env.GITHUB_CLIENT_SECRET!, + }, + google: { + clientId: process.env.GOOGLE_CLIENT_ID!, + clientSecret: process.env.GOOGLE_CLIENT_SECRET!, + }, + }, + databaseHooks: { + user: { + create: { + after: async (user) => { + const org = await createDefaultOrganization(user); + + // Send welcome email to new user + try { + await resend.emails.send({ + from: "Languine ", + to: user.email, + subject: "Welcome to Languine", + react: WelcomeEmail({ name: user.name }), + }); + } catch (error) { + console.error("Error sending welcome email", error); + } + }, + }, + }, + session: { + create: { + before: async (session) => { + const org = await getDefaultOrganization(session.userId); + + return { + data: { + ...session, + activeOrganizationId: org?.organizations?.id, + }, + }; + }, + }, + }, + }, + session: { + cookieCache: { + enabled: true, + maxAge: 5 * 60, + }, + }, + plugins: [organization()], +}); diff --git a/apps/web/src/lib/cookies.ts b/apps/web/src/lib/cookies.ts deleted file mode 100644 index 02b2c32..0000000 --- a/apps/web/src/lib/cookies.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { headers } from "next/headers"; - -export const getCookieHeaders = async () => { - const headersList = await headers(); - const cookie = headersList.get("cookie"); - - const requestHeaders: HeadersInit = {}; - if (cookie) { - requestHeaders.cookie = cookie; - } - - return requestHeaders; -}; diff --git a/apps/web/src/lib/queries.ts b/apps/web/src/lib/queries.ts deleted file mode 100644 index 73cfec9..0000000 --- a/apps/web/src/lib/queries.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { api } from "./api"; -import { auth } from "./auth"; -import { getCookieHeaders } from "./cookies"; - -export const getOrganization = async () => { - const requestHeaders = await getCookieHeaders(); - - return auth.organization.getFullOrganization({ - fetchOptions: { - headers: requestHeaders, - credentials: "include", - }, - }); -}; - -export type GetTeamsResponse = Awaited>; - -export const getTeams = async () => { - const response = await api.teams.$get(); - - if (!response.ok) { - return []; - } - - const teams = await response.json(); - - const teamsAndProjects = await Promise.all( - teams?.data.map(async (team) => { - const projects = await api.teams[":teamId"].projects.$get({ - param: { - teamId: team.id, - }, - }); - - if (!projects.ok) { - return null; - } - - const projectsResponse = await projects.json(); - - return { - ...team, - projects: projectsResponse.data, - }; - }), - ); - - return teamsAndProjects; -}; diff --git a/apps/web/src/lib/resend.ts b/apps/web/src/lib/resend.ts new file mode 100644 index 0000000..6ed938e --- /dev/null +++ b/apps/web/src/lib/resend.ts @@ -0,0 +1,3 @@ +import { Resend } from "resend"; + +export const resend = new Resend(process.env.RESEND_API_KEY); diff --git a/apps/web/src/lib/session.ts b/apps/web/src/lib/session.ts index ac591d1..0cd8112 100644 --- a/apps/web/src/lib/session.ts +++ b/apps/web/src/lib/session.ts @@ -1,12 +1,13 @@ +import { authClient } from "@/lib/auth/client"; import { headers } from "next/headers"; -import { auth } from "./auth"; +import { cache } from "react"; -export const getSession = async () => { - const session = await auth.getSession({ +export const getSession = cache(async () => { + const session = await authClient.getSession({ fetchOptions: { headers: await headers(), }, }); return session; -}; +}); diff --git a/apps/web/src/lib/url.ts b/apps/web/src/lib/url.ts new file mode 100644 index 0000000..79019cb --- /dev/null +++ b/apps/web/src/lib/url.ts @@ -0,0 +1,7 @@ +export function getAppUrl() { + if (!process.env.VERCEL_URL) { + return "http://localhost:3000"; + } + + return process.env.VERCEL_URL; +} diff --git a/apps/web/src/middleware.ts b/apps/web/src/middleware.ts index 8f47c63..5b0797d 100644 --- a/apps/web/src/middleware.ts +++ b/apps/web/src/middleware.ts @@ -1,6 +1,8 @@ import { createI18nMiddleware } from "next-international/middleware"; -import type { NextRequest } from "next/server"; +import { type NextRequest, NextResponse } from "next/server"; import languineConfig from "../languine.config"; +import { getDefaultOrganization } from "./db/queries/select"; +import { getSessionFromRequest } from "./lib/auth/middleware"; const I18nMiddleware = createI18nMiddleware({ locales: [...languineConfig.locale.targets, languineConfig.locale.source], @@ -8,7 +10,26 @@ const I18nMiddleware = createI18nMiddleware({ }); export async function middleware(request: NextRequest) { - return I18nMiddleware(request); + const i18nResponse = await I18nMiddleware(request); + + // Only proceed with organization check for login path + if (request.nextUrl.pathname.includes("/login")) { + const session = await getSessionFromRequest(); + + if (!session?.user.id) { + return i18nResponse; + } + + const data = await getDefaultOrganization(session?.user.id); + + if (data?.organizations) { + return NextResponse.redirect( + new URL(`/${data.organizations.slug}/default`, request.url), + ); + } + } + + return i18nResponse; } export const config = { diff --git a/apps/web/src/trpc/client.tsx b/apps/web/src/trpc/client.tsx new file mode 100644 index 0000000..1a1914b --- /dev/null +++ b/apps/web/src/trpc/client.tsx @@ -0,0 +1,81 @@ +"use client"; +// ^-- to make sure we can mount the Provider from a server component +import type { QueryClient } from "@tanstack/react-query"; +import { QueryClientProvider } from "@tanstack/react-query"; +import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; +import { + httpBatchLink, + httpLink, + isNonJsonSerializable, + splitLink, +} from "@trpc/client"; +import { createTRPCReact } from "@trpc/react-query"; +import { useState } from "react"; +import superjson from "superjson"; +import { makeQueryClient } from "./query-client"; +import type { AppRouter } from "./routers/_app"; + +export const trpc = createTRPCReact(); + +let clientQueryClientSingleton: QueryClient; + +function getQueryClient() { + if (typeof window === "undefined") { + // Server: always make a new query client + return makeQueryClient(); + } + // Browser: use singleton pattern to keep the same query client + // biome-ignore lint/suspicious/noAssignInExpressions: + return (clientQueryClientSingleton ??= makeQueryClient()); +} + +function getUrl() { + const base = (() => { + if (typeof window !== "undefined") return ""; + if (process.env.VERCEL_URL) return `https://${process.env.VERCEL_URL}`; + return "http://localhost:3000"; + })(); + return `${base}/api/trpc`; +} + +export function TRPCProvider( + props: Readonly<{ + children: React.ReactNode; + }>, +) { + // NOTE: Avoid useState when initializing the query client if you don't + // have a suspense boundary between this and the code that may + // suspend because React will throw away the client on the initial + // render if it suspends and there is no boundary + const queryClient = getQueryClient(); + const [trpcClient] = useState(() => + trpc.createClient({ + links: [ + splitLink({ + condition: (op) => isNonJsonSerializable(op.input), + // @ts-ignore - it needs the tarnsformer but it doesn't work with handling form data πŸ€·β€β™‚οΈ + true: httpLink({ + url: getUrl(), + // transformer: superjson, + }), + false: httpBatchLink({ + transformer: superjson, + url: getUrl(), + }), + }), + // httpBatchLink({ + // transformer: superjson, + // url: getUrl(), + // }), + ], + }), + ); + return ( + + + {props.children} + + + + ); +} diff --git a/apps/web/src/trpc/init.ts b/apps/web/src/trpc/init.ts new file mode 100644 index 0000000..09a6187 --- /dev/null +++ b/apps/web/src/trpc/init.ts @@ -0,0 +1,32 @@ +import { getSession } from "@/lib/session"; +import { TRPCError, initTRPC } from "@trpc/server"; +import { cache } from "react"; +import superjson from "superjson"; + +export const createTRPCContext = cache(async () => { + return {}; +}); + +const t = initTRPC.context().create({ + transformer: superjson, +}); + +export const createCallerFactory = t.createCallerFactory; + +// Base router and procedure helpers +export const createTRPCRouter = t.router; +export const baseProcedure = t.procedure; + +export const protectedProcedure = t.procedure.use(async (opts) => { + const session = await getSession(); + + if (!session.data?.user) { + throw new TRPCError({ code: "UNAUTHORIZED" }); + } + + return opts.next({ + ctx: { + user: session.data.user, + }, + }); +}); diff --git a/apps/web/src/trpc/query-client.ts b/apps/web/src/trpc/query-client.ts new file mode 100644 index 0000000..4637b84 --- /dev/null +++ b/apps/web/src/trpc/query-client.ts @@ -0,0 +1,24 @@ +import { + QueryClient, + defaultShouldDehydrateQuery, +} from "@tanstack/react-query"; +import superjson from "superjson"; + +export function makeQueryClient() { + return new QueryClient({ + defaultOptions: { + queries: { + staleTime: 30 * 1000, + }, + dehydrate: { + serializeData: superjson.serialize, + shouldDehydrateQuery: (query) => + defaultShouldDehydrateQuery(query) || + query.state.status === "pending", + }, + hydrate: { + deserializeData: superjson.deserialize, + }, + }, + }); +} diff --git a/apps/web/src/trpc/routers/_app.ts b/apps/web/src/trpc/routers/_app.ts new file mode 100644 index 0000000..82577c9 --- /dev/null +++ b/apps/web/src/trpc/routers/_app.ts @@ -0,0 +1,12 @@ +import type { inferRouterOutputs } from "@trpc/server"; +import { createTRPCRouter } from "../init"; +import { organizationRouter } from "./organization"; + +export const appRouter = createTRPCRouter({ + organization: organizationRouter, +}); + +// export type definition of API +export type AppRouter = typeof appRouter; + +export type RouterOutputs = inferRouterOutputs; diff --git a/apps/web/src/trpc/routers/organization.ts b/apps/web/src/trpc/routers/organization.ts new file mode 100644 index 0000000..994a993 --- /dev/null +++ b/apps/web/src/trpc/routers/organization.ts @@ -0,0 +1,121 @@ +import { db } from "@/db"; +import { getAllOrganizationsWithProjects } from "@/db/queries/select"; +import { members, organizations, projects } from "@/db/schema"; +import { createId } from "@paralleldrive/cuid2"; +import { TRPCError } from "@trpc/server"; +import { eq } from "drizzle-orm"; +import slugify from "slugify"; +import { z } from "zod"; +import { createTRPCRouter, protectedProcedure } from "../init"; + +export const organizationRouter = createTRPCRouter({ + getById: protectedProcedure + .input(z.object({ id: z.string() })) + .query(async ({ input }) => { + const org = await db + .select() + .from(organizations) + .where(eq(organizations.id, input.id)) + .get(); + + if (!org) { + throw new TRPCError({ + code: "NOT_FOUND", + message: "Organization not found", + }); + } + + return org; + }), + + getAll: protectedProcedure.input(z.void()).query(async () => { + return getAllOrganizationsWithProjects(); + }), + + create: protectedProcedure + .input( + z.object({ + name: z.string().min(1), + userId: z.string(), + }), + ) + .mutation(async ({ input }) => { + const org = await db + .insert(organizations) + .values({ + name: input.name, + slug: `${slugify(input.name, { lower: true })}-${createId().slice(0, 8)}`, + }) + .returning() + .get(); + + if (!org) { + throw new TRPCError({ + code: "INTERNAL_SERVER_ERROR", + message: "Failed to create organization", + }); + } + + await db.insert(members).values({ + userId: input.userId, + organizationId: org.id, + role: "owner", + }); + + await db.insert(projects).values({ + name: "Default", + organizationId: org.id, + slug: "default", + }); + + return org; + }), + + update: protectedProcedure + .input( + z.object({ + id: z.string(), + name: z.string().min(1), + logo: z.string().optional(), + }), + ) + .mutation(async ({ input }) => { + const org = await db + .update(organizations) + .set({ + name: input.name, + logo: input.logo, + }) + .where(eq(organizations.id, input.id)) + .returning() + .get(); + + if (!org) { + throw new TRPCError({ + code: "INTERNAL_SERVER_ERROR", + message: "Failed to update organization", + }); + } + + return org; + }), + + delete: protectedProcedure + .input(z.object({ id: z.string() })) + .mutation(async ({ input }) => { + const org = await db + .delete(organizations) + .where(eq(organizations.id, input.id)) + .returning() + .get(); + + if (!org) { + throw new TRPCError({ + code: "INTERNAL_SERVER_ERROR", + message: "Failed to delete organization", + }); + } + + return org; + }), +}); diff --git a/apps/web/src/trpc/server.ts b/apps/web/src/trpc/server.ts new file mode 100644 index 0000000..8f91d8e --- /dev/null +++ b/apps/web/src/trpc/server.ts @@ -0,0 +1,16 @@ +import "server-only"; // <-- ensure this file cannot be imported from the client + +import { createHydrationHelpers } from "@trpc/react-query/rsc"; +import { cache } from "react"; +import { createCallerFactory, createTRPCContext } from "./init"; +import { makeQueryClient } from "./query-client"; +import { appRouter } from "./routers/_app"; + +// IMPORTANT: Create a stable getter for the query client that +// will return the same client during the same request. +export const getQueryClient = cache(makeQueryClient); +const caller = createCallerFactory(appRouter)(createTRPCContext); +export const { trpc, HydrateClient } = createHydrationHelpers( + caller, + getQueryClient, +); diff --git a/apps/web/vercel.json b/apps/web/vercel.json index 4984a1d..5bdd55c 100644 --- a/apps/web/vercel.json +++ b/apps/web/vercel.json @@ -1,3 +1,3 @@ { - "regions": ["fra1", "sfo1", "iad1"] + "regions": ["fra1", "syd1", "sfo1"] } \ No newline at end of file diff --git a/bun.lockb b/bun.lockb index 572bc6a1905590285fdec6f384615be76bbfab27..045dc506ce51afd15a57605d221d095bb6fc9601 100755 GIT binary patch delta 130493 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%0DwX{*@teeNz;(C=^1lmLn0MR8uXshZD(i~t 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 T_$FeRur!78^rbUr9O(Z6^QQ~# delta 162348 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�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≻K@g4h^eLpt(Ef`>MtZggqygg|aci!)CLLx8g{X^fS zD=3>+eIUP3F}e)An*a!SP>B|BW4F!J7xuF1AV9WC84xbK9*w>)&khkK;_#ZeNLaq*CD6EF0;0nFH1hFr z_6xl)C6+L{c*vbKtSpV#iA8)EI4~_`V8SRN^3t~7Kv@83_E1AbKe2-cpTrYG16!?_ z4)6XLj)4>vJ&J<+87ugW0#2@vV#w8I%psUSQWDaVqv-5xgQv;z$`-)-0(^uvr!Zd%?_LFds2~Lnf5Vo^{9ROQTd!GflnwANh zcePN`OTBj~^Zpi;Gyh3JUl}U6Tjp3mEPn?CQ)C;Xe<}0sI>5|-(%D`53grEf-c=V| zYhjMwABMM@*8Ep-ayf1&YlRdigVtcOfSvH;(|R8(*ojtX4ZgX+D^4Qy@fqpe6%J({ zGZd%#I)i&Iqzs+GOTN+pouPsSQk)ts&^wT4Aw$Vr-v$)2DGUajXj37C!e*YGsKzOS zXC9`E)TpqT3^U=E8~*!umNrt*30oLiU~a!;ewBlvRIa+NI2drLloDKVs#+u$v^XW; zwL0&{VNJ8~nxw_c=Ppt3Zw9a0*i&ituFzFBU_ zfSCkI3xJ1><%JTr8!8G-F3j5#X!Q)eFDrAQTrX0eZQxR+b0bNuwn37LZHr{lZ7Zf# zy=_rh!!2Kgl3U)uVJg{;8hQIPwK90;~wcr2K|_ypzdb@XEH*8ptxlsc%=umOp~hfu>VMjxs% z%iv_fTR>c7qVPCno|X~BCrZ*AEBM+|Rn#GM7AhW0tA924lg~%`B7q!N2>k{^UX{mm z!fTBf3>>`&hj8eQgq;`V95s~GVN)|E1h$tosL&}R>zdhIS|CEX%9+7tb7=vKZYpmD zTU5$S+@errtw>NNAMwVDRN4I9szh$qPf(y-1<~x7k^bPC9Lwd z<&7I<<*Tv<-_j5HL?E%xh8caG2Jzw$PUceoHds1XaoOm!tI7qVvtvlUFhZ?-0o`UL z43mQNdGHIEr}Tciy4^8ea5lP}#_A>hwK`ptYCkvn6cKM4>hauI+c-x7ubwH&YrJK5 zefiv&qBXjlm1R1em1VHLiGMFYb;#Jix$PP1@d8B`DV(2y!$s-K7skr9k>!j`#Jn>y ndf*Zi;qxCe@#F>4L_B$Y7T?7BW)_~juv!;SSMqJ>F#rDp^4o9O