diff --git a/.github/workflows/prettier.yml b/.github/workflows/prettier.yml new file mode 100644 index 0000000..88010b1 --- /dev/null +++ b/.github/workflows/prettier.yml @@ -0,0 +1,28 @@ +# .github/workflows/prettier.yml + +name: Prettier Check + +on: [pull_request] + +jobs: + prettier-check: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Node.js + uses: actions/setup-node@v2 + with: + node-version: "20" + + - name: Install pnpm + run: | + npm install -g pnpm + + - name: Install dependencies + run: pnpm install + + - name: Run Prettier check + run: pnpm run format-check diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..ff97fb3 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,9 @@ +# Ignore artifacts: +build +coverage +packages/db/drizzle/meta/** +pnpm-lock.yaml +pnpm-workspace.yaml +**/package.json +*.json +web/src/components/ui \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..a987546 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,8 @@ +{ + "semi": true, + "singleQuote": false, + "jsxSingleQuote": false, + "printWidth": 80, + "tabWidth": 4, + "useTabs": true +} diff --git a/apps/api/src/env.ts b/apps/api/src/env.ts index 766567f..7758c9b 100644 --- a/apps/api/src/env.ts +++ b/apps/api/src/env.ts @@ -1,32 +1,35 @@ import { createEnv } from "@t3-oss/env-core"; import { z } from "zod"; export const env = createEnv({ - server: { - CLOUDFLARE_ACCOUNT_ID: z.string({ - description: - "Account ID for the Cloudflare account. Note that this ID should be the same one the bucket is hosted in.", - }), - FALLBACK_WEB_URL:z.string({ - description:"The URL of the frontend. DO NOT ADD A TRAILING SLASH" - }).url(), - R2_ACCESS_KEY_ID: z.string(), - R2_SECRET_ACCESS_KEY: z.string(), - BETTER_AUTH_SECRET: z.string(), - // TODO: add these back once the oauth stuff is implemented. - // GOOGLE_CLIENT_ID: z.string(), - // GOOGLE_CLIENT_SECRET: z.string(), - // DISCORD_CLIENT_ID: z.string(), - // DISCORD_CLIENT_SECRET: z.string(), - // GITHUB_CLIENT_ID: z.string(), - // GITHUB_CLIENT_SECRET: z.string(), - // LINEAR_CLIENT_ID: z.string(), + server: { + CLOUDFLARE_ACCOUNT_ID: z.string({ + description: + "Account ID for the Cloudflare account. Note that this ID should be the same one the bucket is hosted in.", + }), + FALLBACK_WEB_URL: z + .string({ + description: + "The URL of the frontend. DO NOT ADD A TRAILING SLASH", + }) + .url(), + R2_ACCESS_KEY_ID: z.string(), + R2_SECRET_ACCESS_KEY: z.string(), + BETTER_AUTH_SECRET: z.string(), + // TODO: add these back once the oauth stuff is implemented. + // GOOGLE_CLIENT_ID: z.string(), + // GOOGLE_CLIENT_SECRET: z.string(), + // DISCORD_CLIENT_ID: z.string(), + // DISCORD_CLIENT_SECRET: z.string(), + // GITHUB_CLIENT_ID: z.string(), + // GITHUB_CLIENT_SECRET: z.string(), + // LINEAR_CLIENT_ID: z.string(), // LINEAR_CLIENT_SECRET: z.string(), - }, - onValidationError: (issues) => { - console.log("all process variables:", process.env); - console.error("❌ Invalid environment variables:", issues); - throw new Error("Invalid environment variables"); - }, - runtimeEnv: process.env, - emptyStringAsUndefined: true, -}); \ No newline at end of file + }, + onValidationError: (issues) => { + console.log("all process variables:", process.env); + console.error("❌ Invalid environment variables:", issues); + throw new Error("Invalid environment variables"); + }, + runtimeEnv: process.env, + emptyStringAsUndefined: true, +}); diff --git a/apps/api/src/index.ts b/apps/api/src/index.ts index 5e7be12..aeeabb7 100644 --- a/apps/api/src/index.ts +++ b/apps/api/src/index.ts @@ -2,40 +2,50 @@ import { type ScheduledController, type ExecutionContext, } from "@cloudflare/workers-types"; -import { authHandler, backupHandler, userhandler, logHandler, healthHandler } from "./routes"; +import { + authHandler, + backupHandler, + userhandler, + logHandler, + healthHandler, +} from "./routes"; import { generalCorsPolicy, betterAuthCorsPolicy } from "./lib/functions/cors"; import { HonoBetterAuth } from "./lib/functions"; -import { setUserSessionContextMiddleware, authenticatedMiddleware } from "./lib/functions/middleware"; - +import { + setUserSessionContextMiddleware, + authenticatedMiddleware, +} from "./lib/functions/middleware"; interface Env {} -// api stuff +// api stuff export const api = HonoBetterAuth() - .use( - "*", - generalCorsPolicy, // see if we can get rid of this one maybe later? - betterAuthCorsPolicy, - async (c, next) => setUserSessionContextMiddleware(c,next), - async (c, next) => authenticatedMiddleware(c,next) - ) - .route("/health", healthHandler) - .route("/log", logHandler) - .route("/backup", backupHandler) - .route("/user", userhandler); - + .use( + "*", + generalCorsPolicy, // see if we can get rid of this one maybe later? + betterAuthCorsPolicy, + async (c, next) => setUserSessionContextMiddleware(c, next), + async (c, next) => authenticatedMiddleware(c, next), + ) + .route("/health", healthHandler) + .route("/log", logHandler) + .route("/backup", backupHandler) + .route("/user", userhandler) + .route("/api/auth/*", authHandler); //TODO: Ensure that this is the correct route segment to start requests from. + +/// // cron stuff /** * The basic logic for running a cron job in Cloudflare Workers. Will be updated to be more specific later. */ const cron = async ( - controller: ScheduledController, - _: Env, - ctx: ExecutionContext + controller: ScheduledController, + _: Env, + ctx: ExecutionContext, ) => { // NOTE: controller.cron is what we will use to check what jobs need to be running - // ctx.waitUntil(doBackup()); + // ctx.waitUntil(doBackup()); }; export default { @@ -43,6 +53,5 @@ export default { scheduled: cron, }; - // Special type only exported for the web client -export type ApiType = typeof api; \ No newline at end of file +export type ApiType = typeof api; diff --git a/apps/api/src/lib/auth.ts b/apps/api/src/lib/auth.ts index c3af295..0bf0a32 100644 --- a/apps/api/src/lib/auth.ts +++ b/apps/api/src/lib/auth.ts @@ -1,98 +1,97 @@ import { betterAuth } from "better-auth"; import { drizzleAdapter } from "better-auth/adapters/drizzle"; import { db } from "db"; // your drizzle instance -import { env } from "../env"; -import {APP_NAME, AUTH_CONFIG} from "shared/constants" +import { APP_NAME, AUTH_CONFIG } from "shared/constants"; export const auth = betterAuth({ - database: drizzleAdapter(db, { - provider: "sqlite", - debugLogs: true, - }), - databaseHooks: { - user: { - create: { - // used in order to break up the first and last name into separate fields - before: async (user) => { - // split the name into first and last name (name object is mapped to the first name by the config) - const [firstName, ...rest] = user.name.split(" "); - const lastName = rest.join(" "); - return { - data: { ...user, firstName, lastName }, - }; - }, - }, - }, - }, - user: { - // this maps the default "name" field to the "firstName" field in the database - fields: { - name: "firstName", - }, - // this declares the extra fields that are not in the default user schema that better auth creates, but are in the database - additionalFields: { - firstName: { - type: "string", - defaultValue: "", - }, - lastName: { - type: "string", - defaultValue: "", - }, - lastSeen: { - type: "date", - required: true, - defaultValue: Date.now(), - input: false, - }, - // role: { - // type: "string", - // defaultValue: "user", - // validator: { - // input: z.enum(["user", "admin"]), - // output: z.enum(["user", "admin"]), - // }, - // }, - }, - }, - advanced: { - cookiePrefix: APP_NAME, - }, - emailAndPassword: { - enabled: AUTH_CONFIG.emailAndPassword.enabled, - minPasswordLength: AUTH_CONFIG.emailAndPassword.minPasswordLength, - maxPasswordLength: AUTH_CONFIG.emailAndPassword.maxPasswordLength, - }, - // TODO: Reference the following link to see if it is easier to have the social provider's returned values map to first and last name instead - // https://www.better-auth.com/docs/concepts/database#extending-core-schema:~:text=Example%3A%20Mapping%20Profile%20to%20User%20For%20firstName%20and%20lastName - // socialProviders: { - // google: { - // clientId: env.GOOGLE_CLIENT_ID, - // clientSecret: env.GOOGLE_CLIENT_SECRET, - // }, - // discord: { - // clientId: env.DISCORD_CLIENT_ID, - // clientSecret: env.DISCORD_CLIENT_SECRET, - // }, - // github: { - // clientId: env.GITHUB_CLIENT_ID, - // clientSecret: env.GITHUB_CLIENT_SECRET, - // }, - // linear: { - // clientId: env.LINEAR_CLIENT_ID, - // clientSecret: env.LINEAR_CLIENT_SECRET, - // }, - // }, - rateLimit: { - window: 10, // time window in seconds - max: 100, // max requests in the window - }, - session: { - expiresIn: 60 * 60 * 24 * 7, // 7 days - updateAge: 60 * 60 * 24, // 1 day (every 1 day the session expiration is updated) - cookieCache: { - enabled: true, - maxAge: 5 * 60, - }, - }, + database: drizzleAdapter(db, { + provider: "sqlite", + debugLogs: true, + }), + databaseHooks: { + user: { + create: { + // used in order to break up the first and last name into separate fields + before: async (user) => { + // split the name into first and last name (name object is mapped to the first name by the config) + const [firstName, ...rest] = user.name.split(" "); + const lastName = rest.join(" "); + return { + data: { ...user, firstName, lastName }, + }; + }, + }, + }, + }, + user: { + // this maps the default "name" field to the "firstName" field in the database + fields: { + name: "firstName", + }, + // this declares the extra fields that are not in the default user schema that better auth creates, but are in the database + additionalFields: { + firstName: { + type: "string", + defaultValue: "", + }, + lastName: { + type: "string", + defaultValue: "", + }, + lastSeen: { + type: "date", + required: true, + defaultValue: Date.now(), + input: false, + }, + // role: { + // type: "string", + // defaultValue: "user", + // validator: { + // input: z.enum(["user", "admin"]), + // output: z.enum(["user", "admin"]), + // }, + // }, + }, + }, + advanced: { + cookiePrefix: APP_NAME, + }, + emailAndPassword: { + enabled: AUTH_CONFIG.emailAndPassword.enabled, + minPasswordLength: AUTH_CONFIG.emailAndPassword.minPasswordLength, + maxPasswordLength: AUTH_CONFIG.emailAndPassword.maxPasswordLength, + }, + // TODO: Reference the following link to see if it is easier to have the social provider's returned values map to first and last name instead + // https://www.better-auth.com/docs/concepts/database#extending-core-schema:~:text=Example%3A%20Mapping%20Profile%20to%20User%20For%20firstName%20and%20lastName + // socialProviders: { + // google: { + // clientId: env.GOOGLE_CLIENT_ID, + // clientSecret: env.GOOGLE_CLIENT_SECRET, + // }, + // discord: { + // clientId: env.DISCORD_CLIENT_ID, + // clientSecret: env.DISCORD_CLIENT_SECRET, + // }, + // github: { + // clientId: env.GITHUB_CLIENT_ID, + // clientSecret: env.GITHUB_CLIENT_SECRET, + // }, + // linear: { + // clientId: env.LINEAR_CLIENT_ID, + // clientSecret: env.LINEAR_CLIENT_SECRET, + // }, + // }, + rateLimit: { + window: 10, // time window in seconds + max: 100, // max requests in the window + }, + session: { + expiresIn: 60 * 60 * 24 * 7, // 7 days + updateAge: 60 * 60 * 24, // 1 day (every 1 day the session expiration is updated) + cookieCache: { + enabled: true, + maxAge: 5 * 60, + }, + }, }); diff --git a/apps/api/src/lib/functions/cors.ts b/apps/api/src/lib/functions/cors.ts index 05ea6ba..34f1a3f 100644 --- a/apps/api/src/lib/functions/cors.ts +++ b/apps/api/src/lib/functions/cors.ts @@ -1,30 +1,30 @@ -import { cors } from "hono/cors" -import {env} from "../../env" +import { cors } from "hono/cors"; +import { env } from "../../env"; /** * General CORS policy for the API. Will run on every request, but others can be specified for individual routes. */ -export const generalCorsPolicy = cors({ - origin: env.FALLBACK_WEB_URL, - allowHeaders: [ - "Content-Type", - "Authorization", - "Access-Control-Allow-Origin", - ], - allowMethods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"], - exposeHeaders: ["Content-Length"], - maxAge: 600, - credentials: true, - }); - +export const generalCorsPolicy = cors({ + origin: env.FALLBACK_WEB_URL, + allowHeaders: [ + "Content-Type", + "Authorization", + "Access-Control-Allow-Origin", + ], + allowMethods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"], + exposeHeaders: ["Content-Length"], + maxAge: 600, + credentials: true, +}); + /** * CORS policy specifically for the Better Auth routes. */ export const betterAuthCorsPolicy = cors({ - origin: env.FALLBACK_WEB_URL, - allowHeaders: ["Content-Type", "Authorization"], - allowMethods: ["POST", "GET", "OPTIONS"], - exposeHeaders: ["Content-Length"], - maxAge: 600, - credentials: true, -}); \ No newline at end of file + origin: env.FALLBACK_WEB_URL, + allowHeaders: ["Content-Type", "Authorization"], + allowMethods: ["POST", "GET", "OPTIONS"], + exposeHeaders: ["Content-Length"], + maxAge: 600, + credentials: true, +}); diff --git a/apps/api/src/lib/functions/database.ts b/apps/api/src/lib/functions/database.ts index 3f81d43..aa25129 100644 --- a/apps/api/src/lib/functions/database.ts +++ b/apps/api/src/lib/functions/database.ts @@ -1,27 +1,30 @@ import { env } from "../../env"; /** -* -*/ + * + */ -export async function getDatabaseDumpTurso(databseName: string, organizationSlug: string) { - const res = await fetch( - `https://${databseName}-${organizationSlug}.turso.io/dump`, - { - method: "GET", - headers: new Headers({ - // Authorization: `Bearer ${env.BACKUPS_DB_BEARER}`, - }), - } - ); +export async function getDatabaseDumpTurso( + databseName: string, + organizationSlug: string, +) { + const res = await fetch( + `https://${databseName}-${organizationSlug}.turso.io/dump`, + { + method: "GET", + headers: new Headers({ + // Authorization: `Bearer ${env.BACKUPS_DB_BEARER}`, + }), + }, + ); - if (!res.ok) { - throw new Error( - `Failed to get database dump: ${res.status} ${res.statusText}` - ); - } + if (!res.ok) { + throw new Error( + `Failed to get database dump: ${res.status} ${res.statusText}`, + ); + } - return res.text(); + return res.text(); } /** @@ -29,6 +32,4 @@ export async function getDatabaseDumpTurso(databseName: string, organizationSlug * Function will take in an database information and the type to make the appropriate query. * */ -export async function pingDatabase(){ - -} +export async function pingDatabase() {} diff --git a/apps/api/src/lib/functions/index.ts b/apps/api/src/lib/functions/index.ts index 9a3550e..ed7dfd6 100644 --- a/apps/api/src/lib/functions/index.ts +++ b/apps/api/src/lib/functions/index.ts @@ -1,21 +1,19 @@ -import { HonoOptions } from "hono/hono-base"; -import { env } from "../../env"; +import type { HonoOptions } from "hono/hono-base"; import { Hono } from "hono"; -import { BlankEnv } from "hono/types"; -import {auth} from "../auth" -import { UserType, SessionType } from "../types"; +import type { BlankEnv } from "hono/types"; +import type { UserType, SessionType } from "../types"; /** * @description Wrapper for the Hono constructor that includes the BetterAuth types * @param options Hono options */ -export function HonoBetterAuth(options?: HonoOptions | undefined){ +export function HonoBetterAuth(options?: HonoOptions | undefined) { return new Hono<{ Variables: { - user: UserType - session: SessionType - } + user: UserType; + session: SessionType; + }; }>({ - ...options - }) -}; + ...options, + }); +} diff --git a/apps/api/src/lib/functions/middleware.ts b/apps/api/src/lib/functions/middleware.ts index 84783d0..484b359 100644 --- a/apps/api/src/lib/functions/middleware.ts +++ b/apps/api/src/lib/functions/middleware.ts @@ -1,35 +1,37 @@ -import { Context, Next } from "hono"; -import {auth} from "../auth" +import type { Context, Next } from "hono"; +import { auth } from "../auth"; export const MIDDLEWARE_PUBLIC_ROUTES = ["/health", "/api/auth"]; // We need to grab the specific type of context here as we know for the middleware it will always be the same as the overall api -// TODO: Make this type safe +// TODO: Make this type safe export async function setUserSessionContextMiddleware(c: Context, next: Next) { - const session = await auth.api.getSession({ headers: c.req.raw.headers }); + const session = await auth.api.getSession({ headers: c.req.raw.headers }); - if (!session) { - c.set("user", null); - c.set("session", null); - return next(); - } + if (!session) { + c.set("user", null); + c.set("session", null); + return next(); + } - c.set("user", session.user); - c.set("session", session.session); - return next(); + c.set("user", session.user); + c.set("session", session.session); + return next(); } -// TODO: Make this type safe -export async function authenticatedMiddleware(c:Context, next:Next){ - // First check if it is a public route and if so we will return (make sure this works) - const isPublicRoute = MIDDLEWARE_PUBLIC_ROUTES.some((route) => c.req.path.startsWith(route)); - if (isPublicRoute) { - return next(); - } - const user = c.get("user"); - const session = c.get("session"); - if (!(user && session)) { - return c.json({ error: "Unauthorized" }, 401); - } - return next(); -} \ No newline at end of file +// TODO: Make this type safe +export async function authenticatedMiddleware(c: Context, next: Next) { + // First check if it is a public route and if so we will return (make sure this works) + const isPublicRoute = MIDDLEWARE_PUBLIC_ROUTES.some((route) => + c.req.path.startsWith(route), + ); + if (isPublicRoute) { + return next(); + } + const user = c.get("user"); + const session = c.get("session"); + if (!(user && session)) { + return c.json({ error: "Unauthorized" }, 401); + } + return next(); +} diff --git a/apps/api/src/lib/types.ts b/apps/api/src/lib/types.ts index a3bb7b5..665d2eb 100644 --- a/apps/api/src/lib/types.ts +++ b/apps/api/src/lib/types.ts @@ -1,5 +1,4 @@ -import { api } from ".."; -import { auth } from "../lib/auth" +import { auth } from "../lib/auth"; export type UserType = typeof auth.$Infer.Session.user | null; export type SessionType = typeof auth.$Infer.Session.session | null; diff --git a/apps/api/src/routes/auth.ts b/apps/api/src/routes/auth.ts index 5b3b8ee..32c17fc 100644 --- a/apps/api/src/routes/auth.ts +++ b/apps/api/src/routes/auth.ts @@ -1,10 +1,8 @@ -import { auth } from "../lib/auth" +import { auth } from "../lib/auth"; import { HonoBetterAuth } from "../lib/functions"; -const authHandler = HonoBetterAuth() -.on(["POST", "GET"], "/api/auth/*", (c) => { - return auth.handler(c.req.raw); +const authHandler = HonoBetterAuth().on(["POST", "GET"], "/", (c) => { + return auth.handler(c.req.raw); }); export default authHandler; - diff --git a/apps/api/src/routes/backup.ts b/apps/api/src/routes/backup.ts index da7841e..6d5b143 100644 --- a/apps/api/src/routes/backup.ts +++ b/apps/api/src/routes/backup.ts @@ -1,10 +1,7 @@ -import { Hono } from "hono"; -import { zValidator } from "@hono/zod-validator"; import { HonoBetterAuth } from "../lib/functions"; - -const backupHandler = HonoBetterAuth().post("/:backupId", async (c)=>{ - return c.json({message: "Backup endpoint hit"}, 200); +const backupHandler = HonoBetterAuth().post("/:backupId", async (c) => { + return c.json({ message: "Backup endpoint hit" }, 200); }); -export default backupHandler; \ No newline at end of file +export default backupHandler; diff --git a/apps/api/src/routes/index.ts b/apps/api/src/routes/index.ts index 641cb0f..18c0d6c 100644 --- a/apps/api/src/routes/index.ts +++ b/apps/api/src/routes/index.ts @@ -5,7 +5,7 @@ import logHandler from "./log"; import userhandler from "./user"; const healthHandler = HonoBetterAuth().get("/", (c) => { - return c.json({ status: "It's alive!" }, 200); + return c.json({ status: "It's alive!" }, 200); }); -export {healthHandler, authHandler, backupHandler, logHandler, userhandler} \ No newline at end of file +export { healthHandler, authHandler, backupHandler, logHandler, userhandler }; diff --git a/apps/api/src/routes/log.ts b/apps/api/src/routes/log.ts index f5b17a8..b6835ff 100644 --- a/apps/api/src/routes/log.ts +++ b/apps/api/src/routes/log.ts @@ -1,7 +1,7 @@ import { HonoBetterAuth } from "../lib/functions"; -const logHandler = HonoBetterAuth().post("/", async (c)=>{ - return c.json({message: "Log endpoint hit"}, 200); +const logHandler = HonoBetterAuth().post("/", async (c) => { + return c.json({ message: "Log endpoint hit" }, 200); }); -export default logHandler; \ No newline at end of file +export default logHandler; diff --git a/apps/api/src/routes/user.ts b/apps/api/src/routes/user.ts index a9eea56..159deb3 100644 --- a/apps/api/src/routes/user.ts +++ b/apps/api/src/routes/user.ts @@ -2,35 +2,35 @@ import { zValidator } from "@hono/zod-validator"; import { z } from "zod"; import { HonoBetterAuth } from "../lib/functions"; import { db, eq } from "db"; -import {user} from "db/schema" +import { user } from "db/schema"; const userhandler = HonoBetterAuth() -.get("/", async(c) =>{ - const user = c.get("user"); - if (!user){ - return c.json({ error: "User has been deleted." }, 400); - } - return c.json({ user }, 200); -}) -.get( - "/:userId", - zValidator( - "query", - z.object({ - // TODO: Tighten up a little bit - userId: z.string().min(1, "User ID is required"), - }) - ), - async (c) => { - const userId = c.req.param("userId"); - const requestedUser = await db.query.user.findFirst({ - where:eq(user.id, userId) - }) - if (!requestedUser) { - return c.json({ error: "User not found" }, 404); - } - return c.json({ user: requestedUser }, 200); - } -); + .get("/", async (c) => { + const user = c.get("user"); + if (!user) { + return c.json({ error: "User has been deleted." }, 400); + } + return c.json({ user }, 200); + }) + .get( + "/:userId", + zValidator( + "query", + z.object({ + // TODO: Tighten up a little bit + userId: z.string().min(1, "User ID is required"), + }), + ), + async (c) => { + const userId = c.req.param("userId"); + const requestedUser = await db.query.user.findFirst({ + where: eq(user.id, userId), + }); + if (!requestedUser) { + return c.json({ error: "User not found" }, 404); + } + return c.json({ user: requestedUser }, 200); + }, + ); -export default userhandler; \ No newline at end of file +export default userhandler; diff --git a/apps/lambda/functions/database.ts b/apps/lambda/functions/database.ts index a0f35dd..3b38f5d 100644 --- a/apps/lambda/functions/database.ts +++ b/apps/lambda/functions/database.ts @@ -1,4 +1,4 @@ /** - * Functions for interacting with the database. + * Functions for interacting with the database. * Any logic the lambda needs to interact with the database should live here. */ diff --git a/apps/lambda/functions/s3.ts b/apps/lambda/functions/s3.ts index 91cf69d..a0424a8 100644 --- a/apps/lambda/functions/s3.ts +++ b/apps/lambda/functions/s3.ts @@ -1,21 +1,20 @@ import { PutObjectCommand, S3Client } from "@aws-sdk/client-s3"; // import { env } from "../../env"; - const S3 = new S3Client({ - region: "auto", - endpoint: `https://${env.BACKUPS_CLOUDFLARE_ACCOUNT_ID}.r2.cloudflarestorage.com`, - credentials: { - accessKeyId: process.env.BACKUPS_BUCKET_ACCESS_KEY_ID!, - secretAccessKey: process.env.BACKUPS_BUCKET_SECRET_ACCESS_KEY!, - }, + region: "auto", + endpoint: `https://${env.BACKUPS_CLOUDFLARE_ACCOUNT_ID}.r2.cloudflarestorage.com`, + credentials: { + accessKeyId: process.env.BACKUPS_BUCKET_ACCESS_KEY_ID!, + secretAccessKey: process.env.BACKUPS_BUCKET_SECRET_ACCESS_KEY!, + }, }); async function uploadToS3(fileName: string, dumpFile: string) { - const cmd = new PutObjectCommand({ - Key: fileName, - Bucket: process.env.BACKUPS_BUCKET_NAME!, - Body: Buffer.from(dumpFile, "utf-8"), - }); - return S3.send(cmd); + const cmd = new PutObjectCommand({ + Key: fileName, + Bucket: process.env.BACKUPS_BUCKET_NAME!, + Body: Buffer.from(dumpFile, "utf-8"), + }); + return S3.send(cmd); } diff --git a/apps/lambda/index.ts b/apps/lambda/index.ts index 017aabd..6fbd456 100644 --- a/apps/lambda/index.ts +++ b/apps/lambda/index.ts @@ -1,5 +1,3 @@ import { Context, S3Event, APIGatewayProxyEvent } from "aws-lambda"; -export const handler = async (event: S3Event, context: Context) => { - -}; +export const handler = async (event: S3Event, context: Context) => {}; diff --git a/apps/web/index.html b/apps/web/index.html index 6e0de30..869f127 100644 --- a/apps/web/index.html +++ b/apps/web/index.html @@ -1,20 +1,20 @@ - + - - - - - - - - - Fallback - - -
- - + + + + + + + + + Fallback + + +
+ + diff --git a/apps/web/src/components/ui/button.tsx b/apps/web/src/components/ui/button.tsx index a2df8dc..fde652b 100644 --- a/apps/web/src/components/ui/button.tsx +++ b/apps/web/src/components/ui/button.tsx @@ -1,59 +1,58 @@ -import * as React from "react" -import { Slot } from "@radix-ui/react-slot" -import { cva, type VariantProps } from "class-variance-authority" +import * as React from "react"; +import { Slot } from "@radix-ui/react-slot"; +import { cva, type VariantProps } from "class-variance-authority"; -import { cn } from "@/lib/utils" +import { cn } from "@/lib/utils"; const buttonVariants = cva( - "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", - { - variants: { - variant: { - default: - "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90", - destructive: - "bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60", - outline: - "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50", - secondary: - "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80", - ghost: - "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50", - link: "text-primary underline-offset-4 hover:underline", - }, - size: { - default: "h-9 px-4 py-2 has-[>svg]:px-3", - sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5", - lg: "h-10 rounded-md px-6 has-[>svg]:px-4", - icon: "size-9", - }, - }, - defaultVariants: { - variant: "default", - size: "default", - }, - } -) + "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", + { + variants: { + variant: { + default: + "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90", + destructive: + "bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60", + outline: + "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50", + secondary: + "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80", + ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50", + link: "text-primary underline-offset-4 hover:underline", + }, + size: { + default: "h-9 px-4 py-2 has-[>svg]:px-3", + sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5", + lg: "h-10 rounded-md px-6 has-[>svg]:px-4", + icon: "size-9", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + }, +); function Button({ - className, - variant, - size, - asChild = false, - ...props + className, + variant, + size, + asChild = false, + ...props }: React.ComponentProps<"button"> & - VariantProps & { - asChild?: boolean - }) { - const Comp = asChild ? Slot : "button" + VariantProps & { + asChild?: boolean; + }) { + const Comp = asChild ? Slot : "button"; - return ( - - ) + return ( + + ); } -export { Button, buttonVariants } +export { Button, buttonVariants }; diff --git a/apps/web/src/components/ui/sonner.tsx b/apps/web/src/components/ui/sonner.tsx index 33d2d2a..ec702d9 100644 --- a/apps/web/src/components/ui/sonner.tsx +++ b/apps/web/src/components/ui/sonner.tsx @@ -1,23 +1,23 @@ -import { useTheme } from "next-themes" -import { Toaster as Sonner, type ToasterProps } from "sonner" +import { useTheme } from "next-themes"; +import { Toaster as Sonner, type ToasterProps } from "sonner"; const Toaster = ({ ...props }: ToasterProps) => { - const { theme = "system" } = useTheme() + const { theme = "system" } = useTheme(); - return ( - - ) -} + return ( + + ); +}; -export { Toaster } +export { Toaster }; diff --git a/apps/web/src/lib/auth-client.ts b/apps/web/src/lib/auth-client.ts index 1b3469c..cd2771a 100644 --- a/apps/web/src/lib/auth-client.ts +++ b/apps/web/src/lib/auth-client.ts @@ -2,6 +2,5 @@ import { createAuthClient } from "better-auth/react"; // import {env} from "../env" export const authClient = createAuthClient({ - baseURL: import.meta.env.FALLBACK_PUBLIC_API_URL, - + baseURL: import.meta.env.FALLBACK_PUBLIC_API_URL, }); diff --git a/apps/web/src/lib/functions/api.ts b/apps/web/src/lib/functions/api.ts index 41ab86c..908a00f 100644 --- a/apps/web/src/lib/functions/api.ts +++ b/apps/web/src/lib/functions/api.ts @@ -1,13 +1,12 @@ -import type {ApiType} from "api" +import type { ApiType } from "api"; // import {env} from "../../env" -import { hc} from "hono/client" - +import { hc } from "hono/client"; export const apiClient = hc( - import.meta.env.VITE_FALLBACK_API_URL || "http://localhost:8787", - { - init: { - credentials: "include", - }, - } + import.meta.env.VITE_FALLBACK_API_URL || "http://localhost:8787", + { + init: { + credentials: "include", + }, + }, ); diff --git a/apps/web/src/lib/functions/auth.ts b/apps/web/src/lib/functions/auth.ts index 6bd8143..a25f51d 100644 --- a/apps/web/src/lib/functions/auth.ts +++ b/apps/web/src/lib/functions/auth.ts @@ -1,78 +1,89 @@ import { authClient } from "../auth-client"; import type { EmailSignUpType, EmailSignInType } from "../types"; import { toast } from "sonner"; -import { getRandomSignInGreeting, getFirstName, getRandomSignUpGreeting } from "../utils"; +import { + getRandomSignInGreeting, + getFirstName, + getRandomSignUpGreeting, +} from "../utils"; const callbackURL = "/dashboard"; // A URL to redirect to after the user verifies their email (optional) -export async function signUpEmail(inputs:EmailSignUpType){ - const {redirectUrl, ...restInputs} = inputs; - await authClient.signUp.email( - { - ...restInputs, - callbackURL: redirectUrl || callbackURL, - }, - { - onSuccess: (ctx) => { - // This could be wrong - const firstName = getFirstName(ctx.data.user.name); - toast.success(getRandomSignUpGreeting(firstName)); - }, - onError: () => { - toast.error("Something went wrong signing you up. Please try again later."); - }, - } - ); +export async function signUpEmail(inputs: EmailSignUpType) { + const { redirectUrl, ...restInputs } = inputs; + await authClient.signUp.email( + { + ...restInputs, + callbackURL: redirectUrl || callbackURL, + }, + { + onSuccess: (ctx) => { + // This could be wrong + const firstName = getFirstName(ctx.data.user.name); + toast.success(getRandomSignUpGreeting(firstName)); + }, + onError: () => { + toast.error( + "Something went wrong signing you up. Please try again later.", + ); + }, + }, + ); } export async function signInEmail(inputs: EmailSignInType) { - const { redirectUrl, ...restInputs } = inputs; - const { data, error } = await authClient.signIn.email( - { - ...restInputs, - callbackURL: redirectUrl || callbackURL, - }, - { - onSuccess: (ctx) => { - const firstName = getFirstName(ctx.data.user.name); - toast.success(getRandomSignInGreeting(firstName)); - }, - onError: () => { - toast.error("Something went wrong signing you in. Please try again later."); - }, - } - ); + const { redirectUrl, ...restInputs } = inputs; + const { data, error } = await authClient.signIn.email( + { + ...restInputs, + callbackURL: redirectUrl || callbackURL, + }, + { + onSuccess: (ctx) => { + const firstName = getFirstName(ctx.data.user.name); + toast.success(getRandomSignInGreeting(firstName)); + }, + onError: () => { + toast.error( + "Something went wrong signing you in. Please try again later.", + ); + }, + }, + ); - return {name:data?.user.name, error:error?.message} + return { name: data?.user.name, error: error?.message }; } -export async function signInOauth(provider:string, redirectUrl?:string) { - await authClient.signIn.social({ - provider, - /** - * A URL to redirect after the user authenticates with the provider - * @default "/" - */ - callbackURL : redirectUrl || callbackURL, - /** - * A URL to redirect if an error occurs during the sign in process - */ - errorCallbackURL: "/error", // come back and confiure this later maybe for logging purposes - - }, -{ - onSuccess: (ctx) => { - const firstName = getFirstName(ctx.data.user.name); - toast.success(getRandomSignInGreeting(firstName)); - }, - onError: () => { - // display the error message - toast.error("Something went wrong signing you in. Please try again later."); - }, -}); +export async function signInOauth(provider: string, redirectUrl?: string) { + await authClient.signIn.social( + { + provider, + /** + * A URL to redirect after the user authenticates with the provider + * @default "/" + */ + callbackURL: redirectUrl || callbackURL, + /** + * A URL to redirect if an error occurs during the sign in process + */ + errorCallbackURL: "/error", // come back and confiure this later maybe for logging purposes + }, + { + onSuccess: (ctx) => { + const firstName = getFirstName(ctx.data.user.name); + toast.success(getRandomSignInGreeting(firstName)); + }, + onError: () => { + // display the error message + toast.error( + "Something went wrong signing you in. Please try again later.", + ); + }, + }, + ); } export async function signOut() { - const {data, error} = await authClient.signOut(); - return {data ,error} + const { data, error } = await authClient.signOut(); + return { data, error }; } diff --git a/apps/web/src/lib/queries.ts b/apps/web/src/lib/queries.ts index 870583b..5375bc5 100644 --- a/apps/web/src/lib/queries.ts +++ b/apps/web/src/lib/queries.ts @@ -2,15 +2,15 @@ import { queryOptions } from "@tanstack/react-query"; import { apiClient } from "./functions/api"; export const pingServerQuery = queryOptions({ - queryKey: ["ping"], - queryFn: async () => { - const response = await apiClient.health.$get().catch(() => undefined); + queryKey: ["ping"], + queryFn: async () => { + const response = await apiClient.health.$get().catch(() => undefined); - if (response?.status === 200) { - const data = await response.json(); - return data.status; - } + if (response?.status === 200) { + const data = await response.json(); + return data.status; + } - return "Unable to establish connection with server"; - }, + return "Unable to establish connection with server"; + }, }); diff --git a/apps/web/src/lib/types.ts b/apps/web/src/lib/types.ts index 357d61c..3fc43bb 100644 --- a/apps/web/src/lib/types.ts +++ b/apps/web/src/lib/types.ts @@ -1,5 +1,5 @@ -import z from "zod"; +import z from "zod"; import { emailSignUpSchema, emailSignInSchema } from "./zod"; export type EmailSignUpType = z.infer; -export type EmailSignInType = z.infer; \ No newline at end of file +export type EmailSignInType = z.infer; diff --git a/apps/web/src/lib/utils.ts b/apps/web/src/lib/utils.ts index a24d3c8..373868f 100644 --- a/apps/web/src/lib/utils.ts +++ b/apps/web/src/lib/utils.ts @@ -3,26 +3,29 @@ import { twMerge } from "tailwind-merge"; import { GREETINGS_FUNCTIONS } from "shared/constants"; export function cn(...inputs: ClassValue[]) { - return twMerge(clsx(inputs)); + return twMerge(clsx(inputs)); } export function getRandomArrayItem(array: T[]): T | undefined { - const randomIndex = Math.floor(Math.random() * array.length); - return array[randomIndex]; + const randomIndex = Math.floor(Math.random() * array.length); + return array[randomIndex]; } export function getFirstName(fullName: string): string { - const names = fullName.split(" "); - return names.length > 0 ? names[0] : fullName; + const names = fullName.split(" "); + return names.length > 0 ? names[0] : fullName; } export function getRandomSignInGreeting(name: string): string { - const randomGreetingFunction = getRandomArrayItem(GREETINGS_FUNCTIONS.onSignIn)!; - return randomGreetingFunction(name); + const randomGreetingFunction = getRandomArrayItem< + (typeof GREETINGS_FUNCTIONS.onSignIn)[number] + >(GREETINGS_FUNCTIONS.onSignIn)!; + return randomGreetingFunction(name); } export function getRandomSignUpGreeting(name: string): string { - /// @ts-expect-error - const randomGreetingFunction = getRandomArrayItem(GREETINGS_FUNCTIONSonSignUp)!; - return randomGreetingFunction(name); -} \ No newline at end of file + const randomGreetingFunction = getRandomArrayItem< + (typeof GREETINGS_FUNCTIONS.onSignUp)[number] + >(GREETINGS_FUNCTIONS.onSignUp)!; + return randomGreetingFunction(name); +} diff --git a/apps/web/src/lib/zod.ts b/apps/web/src/lib/zod.ts index 9ebbcb0..dbc1133 100644 --- a/apps/web/src/lib/zod.ts +++ b/apps/web/src/lib/zod.ts @@ -1,17 +1,23 @@ -import z from "zod" +import z from "zod"; import { AUTH_CONFIG } from "shared/constants"; const basicStringSchema = z.string().min(1).max(50); export const emailSignUpSchema = z.object({ - email: z.string().email(), - password: z.string().min(AUTH_CONFIG.emailAndPassword.minPasswordLength).max(AUTH_CONFIG.emailAndPassword.maxPasswordLength), - name: basicStringSchema, - redirectUrl: z.string().url().optional(), -}) + email: z.string().email(), + password: z + .string() + .min(AUTH_CONFIG.emailAndPassword.minPasswordLength) + .max(AUTH_CONFIG.emailAndPassword.maxPasswordLength), + name: basicStringSchema, + redirectUrl: z.string().url().optional(), +}); export const emailSignInSchema = z.object({ - email: z.string().email(), - password: z.string().min(AUTH_CONFIG.emailAndPassword.minPasswordLength).max(AUTH_CONFIG.emailAndPassword.maxPasswordLength), - redirectUrl: z.string().url().optional(), -}) \ No newline at end of file + email: z.string().email(), + password: z + .string() + .min(AUTH_CONFIG.emailAndPassword.minPasswordLength) + .max(AUTH_CONFIG.emailAndPassword.maxPasswordLength), + redirectUrl: z.string().url().optional(), +}); diff --git a/apps/web/src/main.tsx b/apps/web/src/main.tsx index 5c4dfdd..a8f4189 100644 --- a/apps/web/src/main.tsx +++ b/apps/web/src/main.tsx @@ -11,21 +11,21 @@ const router = createAppRouter(); // Register the router instance for type safety declare module "@tanstack/react-router" { - interface Register { - router: typeof router; - } + interface Register { + router: typeof router; + } } // Render the app const rootElement = document.getElementById("app"); if (rootElement && !rootElement.innerHTML) { - const root = ReactDOM.createRoot(rootElement); - root.render( - - - - , - ); + const root = ReactDOM.createRoot(rootElement); + root.render( + + + + , + ); } // If you want to start measuring performance in your app, pass a function diff --git a/apps/web/src/reportWebVitals.ts b/apps/web/src/reportWebVitals.ts index 16b66b5..cb97c7f 100644 --- a/apps/web/src/reportWebVitals.ts +++ b/apps/web/src/reportWebVitals.ts @@ -1,13 +1,13 @@ const reportWebVitals = (onPerfEntry?: () => void) => { - if (onPerfEntry && onPerfEntry instanceof Function) { - import('web-vitals').then(({ onCLS, onINP, onFCP, onLCP, onTTFB }) => { - onCLS(onPerfEntry) - onINP(onPerfEntry) - onFCP(onPerfEntry) - onLCP(onPerfEntry) - onTTFB(onPerfEntry) - }) - } -} + if (onPerfEntry && onPerfEntry instanceof Function) { + import("web-vitals").then(({ onCLS, onINP, onFCP, onLCP, onTTFB }) => { + onCLS(onPerfEntry); + onINP(onPerfEntry); + onFCP(onPerfEntry); + onLCP(onPerfEntry); + onTTFB(onPerfEntry); + }); + } +}; -export default reportWebVitals +export default reportWebVitals; diff --git a/apps/web/src/routeTree.gen.ts b/apps/web/src/routeTree.gen.ts index b121d72..daae5fc 100644 --- a/apps/web/src/routeTree.gen.ts +++ b/apps/web/src/routeTree.gen.ts @@ -8,106 +8,106 @@ // You should NOT make any changes in this file as it will be overwritten. // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. -import { Route as rootRouteImport } from './routes/__root' -import { Route as IndexRouteImport } from './routes/index' -import { Route as TeamIndexRouteImport } from './routes/team/index' -import { Route as TeamNewRouteImport } from './routes/team/new' -import { Route as TeamTeamIdRouteImport } from './routes/team/$teamId' +import { Route as rootRouteImport } from "./routes/__root"; +import { Route as IndexRouteImport } from "./routes/index"; +import { Route as TeamIndexRouteImport } from "./routes/team/index"; +import { Route as TeamNewRouteImport } from "./routes/team/new"; +import { Route as TeamTeamIdRouteImport } from "./routes/team/$teamId"; const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => rootRouteImport, -} as any) + id: "/", + path: "/", + getParentRoute: () => rootRouteImport, +} as any); const TeamIndexRoute = TeamIndexRouteImport.update({ - id: '/team/', - path: '/team/', - getParentRoute: () => rootRouteImport, -} as any) + id: "/team/", + path: "/team/", + getParentRoute: () => rootRouteImport, +} as any); const TeamNewRoute = TeamNewRouteImport.update({ - id: '/team/new', - path: '/team/new', - getParentRoute: () => rootRouteImport, -} as any) + id: "/team/new", + path: "/team/new", + getParentRoute: () => rootRouteImport, +} as any); const TeamTeamIdRoute = TeamTeamIdRouteImport.update({ - id: '/team/$teamId', - path: '/team/$teamId', - getParentRoute: () => rootRouteImport, -} as any) + id: "/team/$teamId", + path: "/team/$teamId", + getParentRoute: () => rootRouteImport, +} as any); export interface FileRoutesByFullPath { - '/': typeof IndexRoute - '/team/$teamId': typeof TeamTeamIdRoute - '/team/new': typeof TeamNewRoute - '/team': typeof TeamIndexRoute + "/": typeof IndexRoute; + "/team/$teamId": typeof TeamTeamIdRoute; + "/team/new": typeof TeamNewRoute; + "/team": typeof TeamIndexRoute; } export interface FileRoutesByTo { - '/': typeof IndexRoute - '/team/$teamId': typeof TeamTeamIdRoute - '/team/new': typeof TeamNewRoute - '/team': typeof TeamIndexRoute + "/": typeof IndexRoute; + "/team/$teamId": typeof TeamTeamIdRoute; + "/team/new": typeof TeamNewRoute; + "/team": typeof TeamIndexRoute; } export interface FileRoutesById { - __root__: typeof rootRouteImport - '/': typeof IndexRoute - '/team/$teamId': typeof TeamTeamIdRoute - '/team/new': typeof TeamNewRoute - '/team/': typeof TeamIndexRoute + __root__: typeof rootRouteImport; + "/": typeof IndexRoute; + "/team/$teamId": typeof TeamTeamIdRoute; + "/team/new": typeof TeamNewRoute; + "/team/": typeof TeamIndexRoute; } export interface FileRouteTypes { - fileRoutesByFullPath: FileRoutesByFullPath - fullPaths: '/' | '/team/$teamId' | '/team/new' | '/team' - fileRoutesByTo: FileRoutesByTo - to: '/' | '/team/$teamId' | '/team/new' | '/team' - id: '__root__' | '/' | '/team/$teamId' | '/team/new' | '/team/' - fileRoutesById: FileRoutesById + fileRoutesByFullPath: FileRoutesByFullPath; + fullPaths: "/" | "/team/$teamId" | "/team/new" | "/team"; + fileRoutesByTo: FileRoutesByTo; + to: "/" | "/team/$teamId" | "/team/new" | "/team"; + id: "__root__" | "/" | "/team/$teamId" | "/team/new" | "/team/"; + fileRoutesById: FileRoutesById; } export interface RootRouteChildren { - IndexRoute: typeof IndexRoute - TeamTeamIdRoute: typeof TeamTeamIdRoute - TeamNewRoute: typeof TeamNewRoute - TeamIndexRoute: typeof TeamIndexRoute + IndexRoute: typeof IndexRoute; + TeamTeamIdRoute: typeof TeamTeamIdRoute; + TeamNewRoute: typeof TeamNewRoute; + TeamIndexRoute: typeof TeamIndexRoute; } -declare module '@tanstack/react-router' { - interface FileRoutesByPath { - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport - parentRoute: typeof rootRouteImport - } - '/team/': { - id: '/team/' - path: '/team' - fullPath: '/team' - preLoaderRoute: typeof TeamIndexRouteImport - parentRoute: typeof rootRouteImport - } - '/team/new': { - id: '/team/new' - path: '/team/new' - fullPath: '/team/new' - preLoaderRoute: typeof TeamNewRouteImport - parentRoute: typeof rootRouteImport - } - '/team/$teamId': { - id: '/team/$teamId' - path: '/team/$teamId' - fullPath: '/team/$teamId' - preLoaderRoute: typeof TeamTeamIdRouteImport - parentRoute: typeof rootRouteImport - } - } +declare module "@tanstack/react-router" { + interface FileRoutesByPath { + "/": { + id: "/"; + path: "/"; + fullPath: "/"; + preLoaderRoute: typeof IndexRouteImport; + parentRoute: typeof rootRouteImport; + }; + "/team/": { + id: "/team/"; + path: "/team"; + fullPath: "/team"; + preLoaderRoute: typeof TeamIndexRouteImport; + parentRoute: typeof rootRouteImport; + }; + "/team/new": { + id: "/team/new"; + path: "/team/new"; + fullPath: "/team/new"; + preLoaderRoute: typeof TeamNewRouteImport; + parentRoute: typeof rootRouteImport; + }; + "/team/$teamId": { + id: "/team/$teamId"; + path: "/team/$teamId"; + fullPath: "/team/$teamId"; + preLoaderRoute: typeof TeamTeamIdRouteImport; + parentRoute: typeof rootRouteImport; + }; + } } const rootRouteChildren: RootRouteChildren = { - IndexRoute: IndexRoute, - TeamTeamIdRoute: TeamTeamIdRoute, - TeamNewRoute: TeamNewRoute, - TeamIndexRoute: TeamIndexRoute, -} + IndexRoute: IndexRoute, + TeamTeamIdRoute: TeamTeamIdRoute, + TeamNewRoute: TeamNewRoute, + TeamIndexRoute: TeamIndexRoute, +}; export const routeTree = rootRouteImport - ._addFileChildren(rootRouteChildren) - ._addFileTypes() + ._addFileChildren(rootRouteChildren) + ._addFileTypes(); diff --git a/apps/web/src/router.tsx b/apps/web/src/router.tsx index 3f84949..26772ab 100644 --- a/apps/web/src/router.tsx +++ b/apps/web/src/router.tsx @@ -5,25 +5,25 @@ import { setupRouterSsrQueryIntegration } from "@tanstack/react-router-ssr-query import { routeTree } from "./routeTree.gen"; export function createAppRouter() { - // Create a new query client instance - const queryClient = new QueryClient(); + // Create a new query client instance + const queryClient = new QueryClient(); - const router = createRouter({ - routeTree, - context: { queryClient }, - defaultPreload: "intent", - scrollRestoration: true, - defaultStructuralSharing: true, - defaultPreloadStaleTime: 0, - }); + const router = createRouter({ + routeTree, + context: { queryClient }, + defaultPreload: "intent", + scrollRestoration: true, + defaultStructuralSharing: true, + defaultPreloadStaleTime: 0, + }); - // Enable react query SSR - setupRouterSsrQueryIntegration({ - queryClient, - router, - handleRedirects: true, - wrapQueryClient: true, - }); + // Enable react query SSR + setupRouterSsrQueryIntegration({ + queryClient, + router, + handleRedirects: true, + wrapQueryClient: true, + }); - return router; + return router; } diff --git a/apps/web/src/routes/__root.tsx b/apps/web/src/routes/__root.tsx index 98b6cba..fd1e4aa 100644 --- a/apps/web/src/routes/__root.tsx +++ b/apps/web/src/routes/__root.tsx @@ -3,14 +3,14 @@ import { Outlet, createRootRouteWithContext } from "@tanstack/react-router"; import { TanStackRouterDevtools } from "@tanstack/react-router-devtools"; type RouterContext = { - queryClient: QueryClient; + queryClient: QueryClient; }; export const Route = createRootRouteWithContext()({ - component: () => ( - <> - - - - ), + component: () => ( + <> + + + + ), }); diff --git a/apps/web/src/routes/index.tsx b/apps/web/src/routes/index.tsx index c9e8c37..a129d24 100644 --- a/apps/web/src/routes/index.tsx +++ b/apps/web/src/routes/index.tsx @@ -2,44 +2,39 @@ import { createFileRoute } from "@tanstack/react-router"; import { APP_NAME } from "shared/constants"; import { Button } from "@/components/ui/button"; import { toast } from "sonner"; -import { apiClient } from "@/lib/functions/api"; import { pingServerQuery } from "@/lib/queries"; import { useQuery } from "@tanstack/react-query"; export const Route = createFileRoute("/")({ - loader: ({ context: { queryClient } }) => - queryClient.ensureQueryData(pingServerQuery), - component: App, + loader: ({ context: { queryClient } }) => + queryClient.ensureQueryData(pingServerQuery), + component: App, }); function App() { - console.log("App rendered"); - console.log(import.meta.env); - const { data, refetch } = useQuery(pingServerQuery); - return ( -
-

Connection Status: {data || "Not Connected"}

-

Welcome to {APP_NAME}!

-

- This is a simple app using create-tsrouter-app. -

- -
- ); -} - -async function fetchData() { - return apiClient.health.$get(); + console.log("App rendered"); + console.log(import.meta.env); + const { data, refetch } = useQuery(pingServerQuery); + return ( +
+

Connection Status: {data || "Not Connected"}

+

Welcome to {APP_NAME}!

+

+ This is a simple app using create-tsrouter-app. +

+ +
+ ); } diff --git a/apps/web/src/routes/team/$teamId.tsx b/apps/web/src/routes/team/$teamId.tsx index 69e4aa4..b0e7470 100644 --- a/apps/web/src/routes/team/$teamId.tsx +++ b/apps/web/src/routes/team/$teamId.tsx @@ -1,11 +1,11 @@ -import { createFileRoute } from '@tanstack/react-router' +import { createFileRoute } from "@tanstack/react-router"; -export const Route = createFileRoute('/team/$teamId')({ - component: RouteComponent, -}) +export const Route = createFileRoute("/team/$teamId")({ + component: RouteComponent, +}); function RouteComponent() { - const { teamId } = Route.useParams(); + const { teamId } = Route.useParams(); - return
Slug is {teamId}
; + return
Slug is {teamId}
; } diff --git a/apps/web/src/routes/team/index.tsx b/apps/web/src/routes/team/index.tsx index 917a0fc..4da561f 100644 --- a/apps/web/src/routes/team/index.tsx +++ b/apps/web/src/routes/team/index.tsx @@ -1,9 +1,9 @@ -import { createFileRoute } from '@tanstack/react-router' +import { createFileRoute } from "@tanstack/react-router"; -export const Route = createFileRoute('/team/')({ - component: RouteComponent, -}) +export const Route = createFileRoute("/team/")({ + component: RouteComponent, +}); function RouteComponent() { - return
BTestd
+ return
BTestd
; } diff --git a/apps/web/src/routes/team/new.tsx b/apps/web/src/routes/team/new.tsx index 48a9ed0..39a1e55 100644 --- a/apps/web/src/routes/team/new.tsx +++ b/apps/web/src/routes/team/new.tsx @@ -1,9 +1,9 @@ -import { createFileRoute } from '@tanstack/react-router' +import { createFileRoute } from "@tanstack/react-router"; -export const Route = createFileRoute('/team/new')({ - component: RouteComponent, -}) +export const Route = createFileRoute("/team/new")({ + component: RouteComponent, +}); function RouteComponent() { - return
Create a new team!
+ return
Create a new team!
; } diff --git a/apps/web/src/styles.css b/apps/web/src/styles.css index 4bc2c75..603a4c2 100644 --- a/apps/web/src/styles.css +++ b/apps/web/src/styles.css @@ -4,121 +4,121 @@ @custom-variant dark (&:is(.dark *)); :root { - --background: oklch(1 0 0); - --foreground: oklch(0.145 0 0); - --card: oklch(1 0 0); - --card-foreground: oklch(0.145 0 0); - --popover: oklch(1 0 0); - --popover-foreground: oklch(0.145 0 0); - --primary: oklch(0.205 0 0); - --primary-foreground: oklch(0.985 0 0); - --secondary: oklch(0.97 0 0); - --secondary-foreground: oklch(0.205 0 0); - --muted: oklch(0.97 0 0); - --muted-foreground: oklch(0.556 0 0); - --accent: oklch(0.97 0 0); - --accent-foreground: oklch(0.205 0 0); - --destructive: oklch(0.577 0.245 27.325); - --destructive-foreground: oklch(0.577 0.245 27.325); - --border: oklch(0.922 0 0); - --input: oklch(0.922 0 0); - --ring: oklch(0.708 0 0); - --chart-1: oklch(0.646 0.222 41.116); - --chart-2: oklch(0.6 0.118 184.704); - --chart-3: oklch(0.398 0.07 227.392); - --chart-4: oklch(0.828 0.189 84.429); - --chart-5: oklch(0.769 0.188 70.08); - --radius: 0.625rem; - --sidebar: oklch(0.985 0 0); - --sidebar-foreground: oklch(0.145 0 0); - --sidebar-primary: oklch(0.205 0 0); - --sidebar-primary-foreground: oklch(0.985 0 0); - --sidebar-accent: oklch(0.97 0 0); - --sidebar-accent-foreground: oklch(0.205 0 0); - --sidebar-border: oklch(0.922 0 0); - --sidebar-ring: oklch(0.708 0 0); + --background: oklch(1 0 0); + --foreground: oklch(0.145 0 0); + --card: oklch(1 0 0); + --card-foreground: oklch(0.145 0 0); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.145 0 0); + --primary: oklch(0.205 0 0); + --primary-foreground: oklch(0.985 0 0); + --secondary: oklch(0.97 0 0); + --secondary-foreground: oklch(0.205 0 0); + --muted: oklch(0.97 0 0); + --muted-foreground: oklch(0.556 0 0); + --accent: oklch(0.97 0 0); + --accent-foreground: oklch(0.205 0 0); + --destructive: oklch(0.577 0.245 27.325); + --destructive-foreground: oklch(0.577 0.245 27.325); + --border: oklch(0.922 0 0); + --input: oklch(0.922 0 0); + --ring: oklch(0.708 0 0); + --chart-1: oklch(0.646 0.222 41.116); + --chart-2: oklch(0.6 0.118 184.704); + --chart-3: oklch(0.398 0.07 227.392); + --chart-4: oklch(0.828 0.189 84.429); + --chart-5: oklch(0.769 0.188 70.08); + --radius: 0.625rem; + --sidebar: oklch(0.985 0 0); + --sidebar-foreground: oklch(0.145 0 0); + --sidebar-primary: oklch(0.205 0 0); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.97 0 0); + --sidebar-accent-foreground: oklch(0.205 0 0); + --sidebar-border: oklch(0.922 0 0); + --sidebar-ring: oklch(0.708 0 0); } .dark { - --background: oklch(0.145 0 0); - --foreground: oklch(0.985 0 0); - --card: oklch(0.145 0 0); - --card-foreground: oklch(0.985 0 0); - --popover: oklch(0.145 0 0); - --popover-foreground: oklch(0.985 0 0); - --primary: oklch(0.985 0 0); - --primary-foreground: oklch(0.205 0 0); - --secondary: oklch(0.269 0 0); - --secondary-foreground: oklch(0.985 0 0); - --muted: oklch(0.269 0 0); - --muted-foreground: oklch(0.708 0 0); - --accent: oklch(0.269 0 0); - --accent-foreground: oklch(0.985 0 0); - --destructive: oklch(0.396 0.141 25.723); - --destructive-foreground: oklch(0.637 0.237 25.331); - --border: oklch(0.269 0 0); - --input: oklch(0.269 0 0); - --ring: oklch(0.439 0 0); - --chart-1: oklch(0.488 0.243 264.376); - --chart-2: oklch(0.696 0.17 162.48); - --chart-3: oklch(0.769 0.188 70.08); - --chart-4: oklch(0.627 0.265 303.9); - --chart-5: oklch(0.645 0.246 16.439); - --sidebar: oklch(0.205 0 0); - --sidebar-foreground: oklch(0.985 0 0); - --sidebar-primary: oklch(0.488 0.243 264.376); - --sidebar-primary-foreground: oklch(0.985 0 0); - --sidebar-accent: oklch(0.269 0 0); - --sidebar-accent-foreground: oklch(0.985 0 0); - --sidebar-border: oklch(0.269 0 0); - --sidebar-ring: oklch(0.439 0 0); + --background: oklch(0.145 0 0); + --foreground: oklch(0.985 0 0); + --card: oklch(0.145 0 0); + --card-foreground: oklch(0.985 0 0); + --popover: oklch(0.145 0 0); + --popover-foreground: oklch(0.985 0 0); + --primary: oklch(0.985 0 0); + --primary-foreground: oklch(0.205 0 0); + --secondary: oklch(0.269 0 0); + --secondary-foreground: oklch(0.985 0 0); + --muted: oklch(0.269 0 0); + --muted-foreground: oklch(0.708 0 0); + --accent: oklch(0.269 0 0); + --accent-foreground: oklch(0.985 0 0); + --destructive: oklch(0.396 0.141 25.723); + --destructive-foreground: oklch(0.637 0.237 25.331); + --border: oklch(0.269 0 0); + --input: oklch(0.269 0 0); + --ring: oklch(0.439 0 0); + --chart-1: oklch(0.488 0.243 264.376); + --chart-2: oklch(0.696 0.17 162.48); + --chart-3: oklch(0.769 0.188 70.08); + --chart-4: oklch(0.627 0.265 303.9); + --chart-5: oklch(0.645 0.246 16.439); + --sidebar: oklch(0.205 0 0); + --sidebar-foreground: oklch(0.985 0 0); + --sidebar-primary: oklch(0.488 0.243 264.376); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.269 0 0); + --sidebar-accent-foreground: oklch(0.985 0 0); + --sidebar-border: oklch(0.269 0 0); + --sidebar-ring: oklch(0.439 0 0); } @theme inline { - --color-background: var(--background); - --color-foreground: var(--foreground); - --color-card: var(--card); - --color-card-foreground: var(--card-foreground); - --color-popover: var(--popover); - --color-popover-foreground: var(--popover-foreground); - --color-primary: var(--primary); - --color-primary-foreground: var(--primary-foreground); - --color-secondary: var(--secondary); - --color-secondary-foreground: var(--secondary-foreground); - --color-muted: var(--muted); - --color-muted-foreground: var(--muted-foreground); - --color-accent: var(--accent); - --color-accent-foreground: var(--accent-foreground); - --color-destructive: var(--destructive); - --color-destructive-foreground: var(--destructive-foreground); - --color-border: var(--border); - --color-input: var(--input); - --color-ring: var(--ring); - --color-chart-1: var(--chart-1); - --color-chart-2: var(--chart-2); - --color-chart-3: var(--chart-3); - --color-chart-4: var(--chart-4); - --color-chart-5: var(--chart-5); - --radius-sm: calc(var(--radius) - 4px); - --radius-md: calc(var(--radius) - 2px); - --radius-lg: var(--radius); - --radius-xl: calc(var(--radius) + 4px); - --color-sidebar: var(--sidebar); - --color-sidebar-foreground: var(--sidebar-foreground); - --color-sidebar-primary: var(--sidebar-primary); - --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); - --color-sidebar-accent: var(--sidebar-accent); - --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); - --color-sidebar-border: var(--sidebar-border); - --color-sidebar-ring: var(--sidebar-ring); + --color-background: var(--background); + --color-foreground: var(--foreground); + --color-card: var(--card); + --color-card-foreground: var(--card-foreground); + --color-popover: var(--popover); + --color-popover-foreground: var(--popover-foreground); + --color-primary: var(--primary); + --color-primary-foreground: var(--primary-foreground); + --color-secondary: var(--secondary); + --color-secondary-foreground: var(--secondary-foreground); + --color-muted: var(--muted); + --color-muted-foreground: var(--muted-foreground); + --color-accent: var(--accent); + --color-accent-foreground: var(--accent-foreground); + --color-destructive: var(--destructive); + --color-destructive-foreground: var(--destructive-foreground); + --color-border: var(--border); + --color-input: var(--input); + --color-ring: var(--ring); + --color-chart-1: var(--chart-1); + --color-chart-2: var(--chart-2); + --color-chart-3: var(--chart-3); + --color-chart-4: var(--chart-4); + --color-chart-5: var(--chart-5); + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); + --color-sidebar: var(--sidebar); + --color-sidebar-foreground: var(--sidebar-foreground); + --color-sidebar-primary: var(--sidebar-primary); + --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); + --color-sidebar-accent: var(--sidebar-accent); + --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); + --color-sidebar-border: var(--sidebar-border); + --color-sidebar-ring: var(--sidebar-ring); } @layer base { - * { - @apply border-border outline-ring/50; - } + * { + @apply border-border outline-ring/50; + } - body { - @apply bg-background text-foreground; - } -} \ No newline at end of file + body { + @apply bg-background text-foreground; + } +} diff --git a/apps/web/vite.config.ts b/apps/web/vite.config.ts index 5e79047..aac6114 100644 --- a/apps/web/vite.config.ts +++ b/apps/web/vite.config.ts @@ -8,27 +8,26 @@ import path from "path"; // https://vitejs.dev/config/ export default defineConfig({ - resolve:{ - alias:{ - "@":path.resolve(__dirname, "src") - }, - }, - envDir: process.env.NODE_ENV !== "production" ? "../../" : undefined, // this may break when deploying. Def come back to this - plugins: [ - // Please make sure that '@tanstack/router-plugin' is passed before '@vitejs/plugin-react' - tanstackRouter({ - target: "react", - autoCodeSplitting: true, - }), - react(), - cloudflare(), - tailwindcss(), - ], - server:{ - cors:false, - - } + resolve: { + alias: { + "@": path.resolve(__dirname, "src"), + }, + }, + envDir: process.env.NODE_ENV !== "production" ? "../../" : undefined, // this may break when deploying. Def come back to this + plugins: [ + // Please make sure that '@tanstack/router-plugin' is passed before '@vitejs/plugin-react' + tanstackRouter({ + target: "react", + autoCodeSplitting: true, + }), + react(), + cloudflare(), + tailwindcss(), + ], + server: { + cors: false, + }, }); // TODO: Come back and look for a way to validate the environment variables before the app starts up and fail if they are not validated -// TODO: Add intellisense for import.meta.env - https://vite.dev/guide/env-and-mode.html#intellisense-for-typescript \ No newline at end of file +// TODO: Add intellisense for import.meta.env - https://vite.dev/guide/env-and-mode.html#intellisense-for-typescript diff --git a/apps/web/wrangler.jsonc b/apps/web/wrangler.jsonc index d162df9..a9c74f5 100644 --- a/apps/web/wrangler.jsonc +++ b/apps/web/wrangler.jsonc @@ -1,7 +1,7 @@ { - "name": "fallback-web", - "compatibility_date": "2025-04-03", - "assets": { - "not_found_handling": "single-page-application" - } -} \ No newline at end of file + "name": "fallback-web", + "compatibility_date": "2025-04-03", + "assets": { + "not_found_handling": "single-page-application", + }, +} diff --git a/package.json b/package.json index b3e3e56..e57c865 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,9 @@ "build": "turbo run build", "dev": "turbo run dev", "lint": "turbo run lint", - "format": "prettier --write \"**/*.{ts,tsx,md}\"", + "prettier-watch": "onchange \"**/*\" -- prettier --write --ignore-unknown {{changed}}", + "format": "prettier --write .", + "format-check": "prettier --check .", "check-types": "turbo run check-types" }, "devDependencies": { diff --git a/packages/db/drizzle.config.ts b/packages/db/drizzle.config.ts index b2427ea..7079a90 100644 --- a/packages/db/drizzle.config.ts +++ b/packages/db/drizzle.config.ts @@ -3,16 +3,17 @@ import dotenv from "dotenv"; import path from "path"; dotenv.config({ - path: "../../.env", + path: "../../.env", }); export default defineConfig({ - schema: "./schema.ts", - out: "./drizzle", - dialect: "turso", - dbCredentials: { - url: process.env.TURSO_DATABASE_URL!, - authToken: process.env.TURSO_AUTH_TOKEN, - }, - breakpoints: true, + schema: "./schema.ts", + out: "./drizzle", + dialect: "turso", + casing: "snake_case", + dbCredentials: { + url: process.env.TURSO_DATABASE_URL!, + authToken: process.env.TURSO_AUTH_TOKEN, + }, + breakpoints: true, }); diff --git a/packages/db/drizzle/0000_burly_anthem.sql b/packages/db/drizzle/0000_burly_anthem.sql new file mode 100644 index 0000000..cfedb33 --- /dev/null +++ b/packages/db/drizzle/0000_burly_anthem.sql @@ -0,0 +1,109 @@ +CREATE TABLE `account` ( + `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 `user`(`id`) ON UPDATE no action ON DELETE cascade +); +--> statement-breakpoint +CREATE TABLE `backup_job` ( + `id` text PRIMARY KEY DEFAULT 'job_Uo08ixz9YL0TyOEs0Rb8-' NOT NULL, + `name` text(255) NOT NULL, + `authentication_data` blob NOT NULL, + `databaseType` text NOT NULL, + `cronString` text(255) NOT NULL, + `team_id` text NOT NULL, + FOREIGN KEY (`team_id`) REFERENCES `team`(`id`) ON UPDATE no action ON DELETE cascade +); +--> statement-breakpoint +CREATE TABLE `backup_job_run` ( + `id` text PRIMARY KEY DEFAULT 'tDmkUTi21o31TBOr30l_t' NOT NULL, + `invocationType` text NOT NULL, + `databaseType` text NOT NULL, + `team_id` text NOT NULL, + `startedAt` integer DEFAULT (current_timestamp) NOT NULL, + `completedAt` integer, + `result` text, + FOREIGN KEY (`team_id`) REFERENCES `team`(`id`) ON UPDATE no action ON DELETE cascade +); +--> statement-breakpoint +CREATE TABLE `log` ( + `id` text PRIMARY KEY DEFAULT 'Wh4vcUY8VKKc5f3-dqD-b' NOT NULL, + `logType` text NOT NULL, + `message` text(255) NOT NULL, + `occurredAt` integer DEFAULT (current_timestamp) NOT NULL, + `team_id` text +); +--> statement-breakpoint +CREATE TABLE `session` ( + `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, + FOREIGN KEY (`user_id`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE cascade +); +--> statement-breakpoint +CREATE UNIQUE INDEX `session_token_unique` ON `session` (`token`);--> statement-breakpoint +CREATE TABLE `team` ( + `id` text PRIMARY KEY DEFAULT 'team_IXqln_REYURcJvr8D8TxV' NOT NULL, + `name` text(255) NOT NULL, + `createdAt` integer DEFAULT (current_timestamp) NOT NULL, + `updatedAt` integer DEFAULT (current_timestamp) NOT NULL, + `isprivate` integer DEFAULT true NOT NULL +); +--> statement-breakpoint +CREATE TABLE `team_invite` ( + `id` text PRIMARY KEY DEFAULT 'invite_duPSKBsqK32Ec7-zjmU-b' NOT NULL, + `team_id` text NOT NULL, + `email` text NOT NULL, + `createdAt` integer DEFAULT (current_timestamp) NOT NULL, + `expiresAt` integer NOT NULL, + `acceptedAt` integer, + FOREIGN KEY (`team_id`) REFERENCES `team`(`id`) ON UPDATE no action ON DELETE cascade +); +--> statement-breakpoint +CREATE TABLE `user` ( + `id` text PRIMARY KEY NOT NULL, + `first_name` text(255) NOT NULL, + `last_name` text(255) NOT NULL, + `email` text NOT NULL, + `pronouns` text(255), + `createdAt` integer DEFAULT (current_timestamp) NOT NULL, + `email_verified` integer NOT NULL, + `image` text, + `updated_at` integer NOT NULL, + `lastSeen` integer DEFAULT (current_timestamp) NOT NULL, + `siteRole` text DEFAULT 'USER' NOT NULL +); +--> statement-breakpoint +CREATE UNIQUE INDEX `user_email_unique` ON `user` (`email`);--> statement-breakpoint +CREATE TABLE `user_to_team` ( + `user_id` text NOT NULL, + `team_id` text NOT NULL, + `role` text DEFAULT 'USER' NOT NULL, + PRIMARY KEY(`user_id`, `team_id`), + FOREIGN KEY (`user_id`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE cascade, + FOREIGN KEY (`team_id`) REFERENCES `team`(`id`) ON UPDATE no action ON DELETE cascade +); +--> statement-breakpoint +CREATE TABLE `verification` ( + `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 +); diff --git a/packages/db/drizzle/0001_rainy_star_brand.sql b/packages/db/drizzle/0001_rainy_star_brand.sql new file mode 100644 index 0000000..55a7073 --- /dev/null +++ b/packages/db/drizzle/0001_rainy_star_brand.sql @@ -0,0 +1,68 @@ +PRAGMA foreign_keys=OFF;--> statement-breakpoint +CREATE TABLE `__new_backup_job` ( + `id` text PRIMARY KEY DEFAULT 'job_vWupaPoP_lm2b3OUjgm_N' NOT NULL, + `name` text(255) NOT NULL, + `authentication_data` blob NOT NULL, + `databaseType` text NOT NULL, + `cronString` text(255) NOT NULL, + `team_id` text NOT NULL, + FOREIGN KEY (`team_id`) REFERENCES `team`(`id`) ON UPDATE no action ON DELETE cascade +); +--> statement-breakpoint +INSERT INTO `__new_backup_job`("id", "name", "authentication_data", "databaseType", "cronString", "team_id") SELECT "id", "name", "authentication_data", "databaseType", "cronString", "team_id" FROM `backup_job`;--> statement-breakpoint +DROP TABLE `backup_job`;--> statement-breakpoint +ALTER TABLE `__new_backup_job` RENAME TO `backup_job`;--> statement-breakpoint +PRAGMA foreign_keys=ON;--> statement-breakpoint +CREATE TABLE `__new_backup_job_run` ( + `id` text PRIMARY KEY DEFAULT 'OEscBwrDXIOeqyEQFogfd' NOT NULL, + `invocationType` text NOT NULL, + `backup_job_id` text NOT NULL, + `startedAt` integer DEFAULT (current_timestamp) NOT NULL, + `completedAt` integer, + `result` text, + FOREIGN KEY (`backup_job_id`) REFERENCES `backup_job`(`id`) ON UPDATE no action ON DELETE cascade +); +--> statement-breakpoint +INSERT INTO `__new_backup_job_run`("id", "invocationType", "backup_job_id", "startedAt", "completedAt", "result") SELECT "id", "invocationType", "backup_job_id", "startedAt", "completedAt", "result" FROM `backup_job_run`;--> statement-breakpoint +DROP TABLE `backup_job_run`;--> statement-breakpoint +ALTER TABLE `__new_backup_job_run` RENAME TO `backup_job_run`;--> statement-breakpoint +CREATE TABLE `__new_log` ( + `id` text PRIMARY KEY DEFAULT 'HOxRMqecJIc5bbz4avVbS' NOT NULL, + `logType` text NOT NULL, + `message` text(255) NOT NULL, + `occurredAt` integer DEFAULT (current_timestamp) NOT NULL, + `team_id` text +); +--> statement-breakpoint +INSERT INTO `__new_log`("id", "logType", "message", "occurredAt", "team_id") SELECT "id", "logType", "message", "occurredAt", "team_id" FROM `log`;--> statement-breakpoint +DROP TABLE `log`;--> statement-breakpoint +ALTER TABLE `__new_log` RENAME TO `log`;--> statement-breakpoint +CREATE TABLE `__new_team` ( + `id` text PRIMARY KEY DEFAULT 'team_bV4cUqNKMb5V_ERTpSY0H' NOT NULL, + `name` text(255) NOT NULL, + `createdAt` integer DEFAULT (current_timestamp) NOT NULL, + `updatedAt` integer DEFAULT (current_timestamp) NOT NULL, + `isprivate` integer DEFAULT true NOT NULL +); +--> statement-breakpoint +INSERT INTO `__new_team`("id", "name", "createdAt", "updatedAt", "isprivate") SELECT "id", "name", "createdAt", "updatedAt", "isprivate" FROM `team`;--> statement-breakpoint +DROP TABLE `team`;--> statement-breakpoint +ALTER TABLE `__new_team` RENAME TO `team`;--> statement-breakpoint +CREATE TABLE `__new_team_invite` ( + `id` text PRIMARY KEY DEFAULT 'invite_VpbVO2_d8L-hYmEakecti' NOT NULL, + `team_id` text NOT NULL, + `email` text NOT NULL, + `createdAt` integer DEFAULT (current_timestamp) NOT NULL, + `expiresAt` integer NOT NULL, + `acceptedAt` integer, + FOREIGN KEY (`team_id`) REFERENCES `team`(`id`) ON UPDATE no action ON DELETE cascade +); +--> statement-breakpoint +INSERT INTO `__new_team_invite`("id", "team_id", "email", "createdAt", "expiresAt", "acceptedAt") SELECT "id", "team_id", "email", "createdAt", "expiresAt", "acceptedAt" FROM `team_invite`;--> statement-breakpoint +DROP TABLE `team_invite`;--> statement-breakpoint +ALTER TABLE `__new_team_invite` RENAME TO `team_invite`;--> statement-breakpoint +DROP INDEX "session_token_unique";--> statement-breakpoint +DROP INDEX "user_email_unique";--> statement-breakpoint +ALTER TABLE `user_to_team` ALTER COLUMN "role" TO "role" text NOT NULL DEFAULT 'MEMBER';--> statement-breakpoint +CREATE UNIQUE INDEX `session_token_unique` ON `session` (`token`);--> statement-breakpoint +CREATE UNIQUE INDEX `user_email_unique` ON `user` (`email`); \ No newline at end of file diff --git a/packages/db/drizzle/0002_sleepy_gladiator.sql b/packages/db/drizzle/0002_sleepy_gladiator.sql new file mode 100644 index 0000000..e1a1d25 --- /dev/null +++ b/packages/db/drizzle/0002_sleepy_gladiator.sql @@ -0,0 +1,66 @@ +ALTER TABLE `user` RENAME COLUMN "createdAt" TO "created_at";--> statement-breakpoint +ALTER TABLE `user` RENAME COLUMN "lastSeen" TO "last_seen";--> statement-breakpoint +ALTER TABLE `user` RENAME COLUMN "siteRole" TO "site_role";--> statement-breakpoint +PRAGMA foreign_keys=OFF;--> statement-breakpoint +CREATE TABLE `__new_backup_job` ( + `id` text PRIMARY KEY DEFAULT 'job_nib5vIL9oPEtu7dNan2ul' NOT NULL, + `name` text(255) NOT NULL, + `authentication_data` text NOT NULL, + `database_type` text NOT NULL, + `cron_string` text(255) NOT NULL, + `team_id` text NOT NULL, + FOREIGN KEY (`team_id`) REFERENCES `team`(`id`) ON UPDATE no action ON DELETE cascade +); +--> statement-breakpoint +INSERT INTO `__new_backup_job`("id", "name", "authentication_data", "database_type", "cron_string", "team_id") SELECT "id", "name", "authentication_data", "database_type", "cron_string", "team_id" FROM `backup_job`;--> statement-breakpoint +DROP TABLE `backup_job`;--> statement-breakpoint +ALTER TABLE `__new_backup_job` RENAME TO `backup_job`;--> statement-breakpoint +PRAGMA foreign_keys=ON;--> statement-breakpoint +CREATE TABLE `__new_backup_job_run` ( + `id` text PRIMARY KEY DEFAULT 'K9keXoj_tukZ75sqTyzzv' NOT NULL, + `invocation_type` text NOT NULL, + `backup_job_id` text NOT NULL, + `started_at` integer DEFAULT (current_timestamp) NOT NULL, + `completed_at` integer, + `result` text, + FOREIGN KEY (`backup_job_id`) REFERENCES `backup_job`(`id`) ON UPDATE no action ON DELETE cascade +); +--> statement-breakpoint +INSERT INTO `__new_backup_job_run`("id", "invocation_type", "backup_job_id", "started_at", "completed_at", "result") SELECT "id", "invocation_type", "backup_job_id", "started_at", "completed_at", "result" FROM `backup_job_run`;--> statement-breakpoint +DROP TABLE `backup_job_run`;--> statement-breakpoint +ALTER TABLE `__new_backup_job_run` RENAME TO `backup_job_run`;--> statement-breakpoint +CREATE TABLE `__new_log` ( + `id` text PRIMARY KEY DEFAULT '-uSM8wQav1gg9JOy3ZFZM' NOT NULL, + `log_type` text NOT NULL, + `message` text(255) NOT NULL, + `occurred_at` integer DEFAULT (current_timestamp) NOT NULL, + `team_id` text +); +--> statement-breakpoint +INSERT INTO `__new_log`("id", "log_type", "message", "occurred_at", "team_id") SELECT "id", "log_type", "message", "occurred_at", "team_id" FROM `log`;--> statement-breakpoint +DROP TABLE `log`;--> statement-breakpoint +ALTER TABLE `__new_log` RENAME TO `log`;--> statement-breakpoint +CREATE TABLE `__new_team` ( + `id` text PRIMARY KEY DEFAULT 'team_Kxjr2ebHxf-77vHe_X5Ig' NOT NULL, + `name` text(255) NOT NULL, + `created_at` integer DEFAULT (current_timestamp) NOT NULL, + `updated_at` integer DEFAULT (current_timestamp) NOT NULL, + `isprivate` integer DEFAULT true NOT NULL +); +--> statement-breakpoint +INSERT INTO `__new_team`("id", "name", "created_at", "updated_at", "isprivate") SELECT "id", "name", "created_at", "updated_at", "isprivate" FROM `team`;--> statement-breakpoint +DROP TABLE `team`;--> statement-breakpoint +ALTER TABLE `__new_team` RENAME TO `team`;--> statement-breakpoint +CREATE TABLE `__new_team_invite` ( + `id` text PRIMARY KEY DEFAULT 'invite_8MPIbFuBE5R9JKi-zVSmp' NOT NULL, + `team_id` text NOT NULL, + `email` text NOT NULL, + `created_at` integer DEFAULT (current_timestamp) NOT NULL, + `expires_at` integer NOT NULL, + `accepted_at` integer, + FOREIGN KEY (`team_id`) REFERENCES `team`(`id`) ON UPDATE no action ON DELETE cascade +); +--> statement-breakpoint +INSERT INTO `__new_team_invite`("id", "team_id", "email", "created_at", "expires_at", "accepted_at") SELECT "id", "team_id", "email", "created_at", "expires_at", "accepted_at" FROM `team_invite`;--> statement-breakpoint +DROP TABLE `team_invite`;--> statement-breakpoint +ALTER TABLE `__new_team_invite` RENAME TO `team_invite`; \ No newline at end of file diff --git a/packages/db/drizzle/meta/0000_snapshot.json b/packages/db/drizzle/meta/0000_snapshot.json new file mode 100644 index 0000000..9afe4fc --- /dev/null +++ b/packages/db/drizzle/meta/0000_snapshot.json @@ -0,0 +1,744 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "088da9fa-9596-4eed-8335-93574f75c2d8", + "prevId": "00000000-0000-0000-0000-000000000000", + "tables": { + "account": { + "name": "account", + "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": {}, + "foreignKeys": { + "account_user_id_user_id_fk": { + "name": "account_user_id_user_id_fk", + "tableFrom": "account", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "backup_job": { + "name": "backup_job", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false, + "default": "'job_Uo08ixz9YL0TyOEs0Rb8-'" + }, + "name": { + "name": "name", + "type": "text(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "authentication_data": { + "name": "authentication_data", + "type": "blob", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "databaseType": { + "name": "databaseType", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "cronString": { + "name": "cronString", + "type": "text(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "backup_job_team_id_team_id_fk": { + "name": "backup_job_team_id_team_id_fk", + "tableFrom": "backup_job", + "tableTo": "team", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "backup_job_run": { + "name": "backup_job_run", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false, + "default": "'tDmkUTi21o31TBOr30l_t'" + }, + "invocationType": { + "name": "invocationType", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "databaseType": { + "name": "databaseType", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "startedAt": { + "name": "startedAt", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(current_timestamp)" + }, + "completedAt": { + "name": "completedAt", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "result": { + "name": "result", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "backup_job_run_team_id_team_id_fk": { + "name": "backup_job_run_team_id_team_id_fk", + "tableFrom": "backup_job_run", + "tableTo": "team", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "log": { + "name": "log", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false, + "default": "'Wh4vcUY8VKKc5f3-dqD-b'" + }, + "logType": { + "name": "logType", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "message": { + "name": "message", + "type": "text(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "occurredAt": { + "name": "occurredAt", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(current_timestamp)" + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "session": { + "name": "session", + "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 + } + }, + "indexes": { + "session_token_unique": { + "name": "session_token_unique", + "columns": [ + "token" + ], + "isUnique": true + } + }, + "foreignKeys": { + "session_user_id_user_id_fk": { + "name": "session_user_id_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "team": { + "name": "team", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false, + "default": "'team_IXqln_REYURcJvr8D8TxV'" + }, + "name": { + "name": "name", + "type": "text(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "createdAt": { + "name": "createdAt", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(current_timestamp)" + }, + "updatedAt": { + "name": "updatedAt", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(current_timestamp)" + }, + "isprivate": { + "name": "isprivate", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "team_invite": { + "name": "team_invite", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false, + "default": "'invite_duPSKBsqK32Ec7-zjmU-b'" + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "createdAt": { + "name": "createdAt", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(current_timestamp)" + }, + "expiresAt": { + "name": "expiresAt", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "acceptedAt": { + "name": "acceptedAt", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "team_invite_team_id_team_id_fk": { + "name": "team_invite_team_id_team_id_fk", + "tableFrom": "team_invite", + "tableTo": "team", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "user": { + "name": "user", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "first_name": { + "name": "first_name", + "type": "text(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "last_name": { + "name": "last_name", + "type": "text(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "pronouns": { + "name": "pronouns", + "type": "text(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "createdAt": { + "name": "createdAt", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(current_timestamp)" + }, + "email_verified": { + "name": "email_verified", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "lastSeen": { + "name": "lastSeen", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(current_timestamp)" + }, + "siteRole": { + "name": "siteRole", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'USER'" + } + }, + "indexes": { + "user_email_unique": { + "name": "user_email_unique", + "columns": [ + "email" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "user_to_team": { + "name": "user_to_team", + "columns": { + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'USER'" + } + }, + "indexes": {}, + "foreignKeys": { + "user_to_team_user_id_user_id_fk": { + "name": "user_to_team_user_id_user_id_fk", + "tableFrom": "user_to_team", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "user_to_team_team_id_team_id_fk": { + "name": "user_to_team_team_id_team_id_fk", + "tableFrom": "user_to_team", + "tableTo": "team", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "user_to_team_user_id_team_id_pk": { + "columns": [ + "user_id", + "team_id" + ], + "name": "user_to_team_user_id_team_id_pk" + } + }, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "verification": { + "name": "verification", + "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": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + } + }, + "views": {}, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "indexes": {} + } +} \ No newline at end of file diff --git a/packages/db/drizzle/meta/0001_snapshot.json b/packages/db/drizzle/meta/0001_snapshot.json new file mode 100644 index 0000000..86f20da --- /dev/null +++ b/packages/db/drizzle/meta/0001_snapshot.json @@ -0,0 +1,737 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "4f885387-4647-4aba-aefa-cc1ed860fe72", + "prevId": "088da9fa-9596-4eed-8335-93574f75c2d8", + "tables": { + "account": { + "name": "account", + "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": {}, + "foreignKeys": { + "account_user_id_user_id_fk": { + "name": "account_user_id_user_id_fk", + "tableFrom": "account", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "backup_job": { + "name": "backup_job", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false, + "default": "'job_vWupaPoP_lm2b3OUjgm_N'" + }, + "name": { + "name": "name", + "type": "text(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "authentication_data": { + "name": "authentication_data", + "type": "blob", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "databaseType": { + "name": "databaseType", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "cronString": { + "name": "cronString", + "type": "text(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "backup_job_team_id_team_id_fk": { + "name": "backup_job_team_id_team_id_fk", + "tableFrom": "backup_job", + "tableTo": "team", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "backup_job_run": { + "name": "backup_job_run", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false, + "default": "'OEscBwrDXIOeqyEQFogfd'" + }, + "invocationType": { + "name": "invocationType", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "backup_job_id": { + "name": "backup_job_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "startedAt": { + "name": "startedAt", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(current_timestamp)" + }, + "completedAt": { + "name": "completedAt", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "result": { + "name": "result", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "backup_job_run_backup_job_id_backup_job_id_fk": { + "name": "backup_job_run_backup_job_id_backup_job_id_fk", + "tableFrom": "backup_job_run", + "tableTo": "backup_job", + "columnsFrom": [ + "backup_job_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "log": { + "name": "log", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false, + "default": "'HOxRMqecJIc5bbz4avVbS'" + }, + "logType": { + "name": "logType", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "message": { + "name": "message", + "type": "text(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "occurredAt": { + "name": "occurredAt", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(current_timestamp)" + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "session": { + "name": "session", + "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 + } + }, + "indexes": { + "session_token_unique": { + "name": "session_token_unique", + "columns": [ + "token" + ], + "isUnique": true + } + }, + "foreignKeys": { + "session_user_id_user_id_fk": { + "name": "session_user_id_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "team": { + "name": "team", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false, + "default": "'team_bV4cUqNKMb5V_ERTpSY0H'" + }, + "name": { + "name": "name", + "type": "text(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "createdAt": { + "name": "createdAt", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(current_timestamp)" + }, + "updatedAt": { + "name": "updatedAt", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(current_timestamp)" + }, + "isprivate": { + "name": "isprivate", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "team_invite": { + "name": "team_invite", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false, + "default": "'invite_VpbVO2_d8L-hYmEakecti'" + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "createdAt": { + "name": "createdAt", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(current_timestamp)" + }, + "expiresAt": { + "name": "expiresAt", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "acceptedAt": { + "name": "acceptedAt", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "team_invite_team_id_team_id_fk": { + "name": "team_invite_team_id_team_id_fk", + "tableFrom": "team_invite", + "tableTo": "team", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "user": { + "name": "user", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "first_name": { + "name": "first_name", + "type": "text(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "last_name": { + "name": "last_name", + "type": "text(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "pronouns": { + "name": "pronouns", + "type": "text(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "createdAt": { + "name": "createdAt", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(current_timestamp)" + }, + "email_verified": { + "name": "email_verified", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "lastSeen": { + "name": "lastSeen", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(current_timestamp)" + }, + "siteRole": { + "name": "siteRole", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'USER'" + } + }, + "indexes": { + "user_email_unique": { + "name": "user_email_unique", + "columns": [ + "email" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "user_to_team": { + "name": "user_to_team", + "columns": { + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'MEMBER'" + } + }, + "indexes": {}, + "foreignKeys": { + "user_to_team_user_id_user_id_fk": { + "name": "user_to_team_user_id_user_id_fk", + "tableFrom": "user_to_team", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "user_to_team_team_id_team_id_fk": { + "name": "user_to_team_team_id_team_id_fk", + "tableFrom": "user_to_team", + "tableTo": "team", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "user_to_team_user_id_team_id_pk": { + "columns": [ + "user_id", + "team_id" + ], + "name": "user_to_team_user_id_team_id_pk" + } + }, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "verification": { + "name": "verification", + "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": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + } + }, + "views": {}, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "indexes": {} + } +} \ No newline at end of file diff --git a/packages/db/drizzle/meta/0002_snapshot.json b/packages/db/drizzle/meta/0002_snapshot.json new file mode 100644 index 0000000..21fcf04 --- /dev/null +++ b/packages/db/drizzle/meta/0002_snapshot.json @@ -0,0 +1,753 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "ca9f58f3-935d-4f73-ac8e-bb440a3560d6", + "prevId": "4f885387-4647-4aba-aefa-cc1ed860fe72", + "tables": { + "account": { + "name": "account", + "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": {}, + "foreignKeys": { + "account_user_id_user_id_fk": { + "name": "account_user_id_user_id_fk", + "tableFrom": "account", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "backup_job": { + "name": "backup_job", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false, + "default": "'job_nib5vIL9oPEtu7dNan2ul'" + }, + "name": { + "name": "name", + "type": "text(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "authentication_data": { + "name": "authentication_data", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "database_type": { + "name": "database_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "cron_string": { + "name": "cron_string", + "type": "text(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "backup_job_team_id_team_id_fk": { + "name": "backup_job_team_id_team_id_fk", + "tableFrom": "backup_job", + "tableTo": "team", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "backup_job_run": { + "name": "backup_job_run", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false, + "default": "'K9keXoj_tukZ75sqTyzzv'" + }, + "invocation_type": { + "name": "invocation_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "backup_job_id": { + "name": "backup_job_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "started_at": { + "name": "started_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(current_timestamp)" + }, + "completed_at": { + "name": "completed_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "result": { + "name": "result", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "backup_job_run_backup_job_id_backup_job_id_fk": { + "name": "backup_job_run_backup_job_id_backup_job_id_fk", + "tableFrom": "backup_job_run", + "tableTo": "backup_job", + "columnsFrom": [ + "backup_job_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "log": { + "name": "log", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false, + "default": "'-uSM8wQav1gg9JOy3ZFZM'" + }, + "log_type": { + "name": "log_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "message": { + "name": "message", + "type": "text(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "occurred_at": { + "name": "occurred_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(current_timestamp)" + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "session": { + "name": "session", + "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 + } + }, + "indexes": { + "session_token_unique": { + "name": "session_token_unique", + "columns": [ + "token" + ], + "isUnique": true + } + }, + "foreignKeys": { + "session_user_id_user_id_fk": { + "name": "session_user_id_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "team": { + "name": "team", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false, + "default": "'team_Kxjr2ebHxf-77vHe_X5Ig'" + }, + "name": { + "name": "name", + "type": "text(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(current_timestamp)" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(current_timestamp)" + }, + "isprivate": { + "name": "isprivate", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "team_invite": { + "name": "team_invite", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false, + "default": "'invite_8MPIbFuBE5R9JKi-zVSmp'" + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(current_timestamp)" + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "accepted_at": { + "name": "accepted_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "team_invite_team_id_team_id_fk": { + "name": "team_invite_team_id_team_id_fk", + "tableFrom": "team_invite", + "tableTo": "team", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "user": { + "name": "user", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "first_name": { + "name": "first_name", + "type": "text(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "last_name": { + "name": "last_name", + "type": "text(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "pronouns": { + "name": "pronouns", + "type": "text(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(current_timestamp)" + }, + "email_verified": { + "name": "email_verified", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "last_seen": { + "name": "last_seen", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(current_timestamp)" + }, + "site_role": { + "name": "site_role", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'USER'" + } + }, + "indexes": { + "user_email_unique": { + "name": "user_email_unique", + "columns": [ + "email" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "user_to_team": { + "name": "user_to_team", + "columns": { + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'MEMBER'" + } + }, + "indexes": {}, + "foreignKeys": { + "user_to_team_user_id_user_id_fk": { + "name": "user_to_team_user_id_user_id_fk", + "tableFrom": "user_to_team", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "user_to_team_team_id_team_id_fk": { + "name": "user_to_team_team_id_team_id_fk", + "tableFrom": "user_to_team", + "tableTo": "team", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "user_to_team_user_id_team_id_pk": { + "columns": [ + "user_id", + "team_id" + ], + "name": "user_to_team_user_id_team_id_pk" + } + }, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "verification": { + "name": "verification", + "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": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + } + }, + "views": {}, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": { + "\"backup_job\".\"databaseType\"": "\"backup_job\".\"database_type\"", + "\"backup_job\".\"cronString\"": "\"backup_job\".\"cron_string\"", + "\"backup_job_run\".\"invocationType\"": "\"backup_job_run\".\"invocation_type\"", + "\"backup_job_run\".\"startedAt\"": "\"backup_job_run\".\"started_at\"", + "\"backup_job_run\".\"completedAt\"": "\"backup_job_run\".\"completed_at\"", + "\"log\".\"logType\"": "\"log\".\"log_type\"", + "\"log\".\"occurredAt\"": "\"log\".\"occurred_at\"", + "\"team\".\"createdAt\"": "\"team\".\"created_at\"", + "\"team\".\"updatedAt\"": "\"team\".\"updated_at\"", + "\"team_invite\".\"createdAt\"": "\"team_invite\".\"created_at\"", + "\"team_invite\".\"expiresAt\"": "\"team_invite\".\"expires_at\"", + "\"team_invite\".\"acceptedAt\"": "\"team_invite\".\"accepted_at\"", + "\"user\".\"createdAt\"": "\"user\".\"created_at\"", + "\"user\".\"lastSeen\"": "\"user\".\"last_seen\"", + "\"user\".\"siteRole\"": "\"user\".\"site_role\"" + } + }, + "internal": { + "indexes": {} + } +} \ No newline at end of file diff --git a/packages/db/drizzle/meta/_journal.json b/packages/db/drizzle/meta/_journal.json new file mode 100644 index 0000000..cbdb726 --- /dev/null +++ b/packages/db/drizzle/meta/_journal.json @@ -0,0 +1,27 @@ +{ + "version": "7", + "dialect": "sqlite", + "entries": [ + { + "idx": 0, + "version": "6", + "when": 1757879160497, + "tag": "0000_burly_anthem", + "breakpoints": true + }, + { + "idx": 1, + "version": "6", + "when": 1757907991011, + "tag": "0001_rainy_star_brand", + "breakpoints": true + }, + { + "idx": 2, + "version": "6", + "when": 1757908757865, + "tag": "0002_sleepy_gladiator", + "breakpoints": true + } + ] +} \ No newline at end of file diff --git a/packages/db/index.ts b/packages/db/index.ts index 0c876af..6dc1bd8 100644 --- a/packages/db/index.ts +++ b/packages/db/index.ts @@ -1,23 +1,21 @@ import { drizzle } from "drizzle-orm/libsql/web"; import { createClient } from "@libsql/client/web"; -import * as schema from "./schema" +import * as schema from "./schema"; import dotenv from "dotenv"; dotenv.config({ - path: "../../.env", - debug: true + path: "../../.env", + debug: true, }); -export * from "drizzle-orm" -export * from "./schema" +export * from "drizzle-orm"; +export * from "./schema"; -console.log(process.env) +console.log(process.env); const client = createClient({ - url: process.env.TURSO_DATABASE_URL!, - authToken: process.env.TURSO_AUTH_TOKEN!, + url: process.env.TURSO_DATABASE_URL!, + authToken: process.env.TURSO_AUTH_TOKEN!, }); -export const db = drizzle({ client, casing:"snake_case", schema }); - - +export const db = drizzle({ client, casing: "snake_case", schema }); diff --git a/packages/db/schema.ts b/packages/db/schema.ts index 79e4473..cd44f68 100644 --- a/packages/db/schema.ts +++ b/packages/db/schema.ts @@ -1,128 +1,212 @@ import { sql, relations } from "drizzle-orm"; import { - integer, - text, - blob, - sqliteTable, - customType, - primaryKey, + integer, + text, + sqliteTable, + primaryKey, } from "drizzle-orm/sqlite-core"; import { nanoid } from "nanoid"; const STANDARD_VARCHAR_LENGTH = 255; function standardVarcharFactory() { - return text({ - length: STANDARD_VARCHAR_LENGTH, - }).notNull(); + return text({ + length: STANDARD_VARCHAR_LENGTH, + }).notNull(); } function standardDateFactory() { - return integer({ mode: "timestamp_ms" }) - .notNull() - .default(sql`(current_timestamp)`); + return integer({ mode: "timestamp_ms" }) + .notNull() + .default(sql`(current_timestamp)`); } -function standardIdFactory() { - return text("id").notNull(); +function standardIdFactory(prefix?: string) { + return text("id") + .notNull() + .default(`${prefix ?? ""}${nanoid()}`); } -const logType = text({enum:['INFO',"WARNING","ERROR"]}); -const invocationType = text({enum:["MANUAL","CRON"]}); -const databaseType = text({enum:["SQLITE", "POSTGRESQL"]}); -const backupResult = text({enum:["SUCCESS", "FAILURE", "CANCELED"]}); -const roleType = text({enum:["ADMIN", "USER"]}); +const logType = text({ enum: ["INFO", "WARNING", "ERROR"] }); +const invocationType = text({ enum: ["MANUAL", "CRON"] }); +const databaseType = text({ enum: ["SQLITE", "POSTGRESQL"] }); +const backupResult = text({ enum: ["SUCCESS", "FAILURE", "CANCELED"] }); +const roleType = text({ enum: ["ADMIN", "MEMBER"] }); +const siteRoleType = text({ enum: ["SUPER_ADMIN", "ADMIN", "USER"] }); -// TODO: COME BACK AND ADD RELATIONS AND REFERENCES export const user = sqliteTable("user", { - id: text("id").primaryKey(), - firstName: text("first_name", { length: 255 }).notNull(), - lastName: text("last_name", { length: 255 }).notNull(), - email: text("email").notNull().unique(), - pronouns: text("pronouns", { length: 255 }), - createdAt:standardDateFactory(), - emailVerified: integer("email_verified", { mode: "boolean" }).notNull(), - image: text("image"), - updatedAt: integer("updated_at", { mode: "timestamp" }).notNull(), - lastSeen: standardDateFactory(), - // role: t - // .text("role", { enum: ["admin", "user"] }) - // .notNull() - // .default("user"), + id: text("id").primaryKey(), + firstName: text("first_name", { length: 255 }).notNull(), + lastName: text("last_name", { length: 255 }).notNull(), + email: text("email").notNull().unique(), + pronouns: text("pronouns", { length: 255 }), + createdAt: standardDateFactory(), + emailVerified: integer("email_verified", { mode: "boolean" }).notNull(), + image: text("image"), + updatedAt: integer("updated_at", { mode: "timestamp" }).notNull(), + lastSeen: standardDateFactory(), + siteRole: siteRoleType.notNull().default("USER"), }); +export const userRelations = relations(user, ({ many }) => ({ + teams: many(userToTeam), +})); + export const team = sqliteTable("team", { - id: standardIdFactory().primaryKey(), - name: standardVarcharFactory(), - createdAt: standardDateFactory(), - updatedAt: standardDateFactory(), - isprivate: integer({ mode: "boolean" }).notNull().default(true), + id: standardIdFactory("team_").primaryKey(), + name: standardVarcharFactory(), + createdAt: standardDateFactory(), + updatedAt: standardDateFactory(), + isprivate: integer({ mode: "boolean" }).notNull().default(true), }); +export const teamRelations = relations(team, ({ many }) => ({ + members: many(userToTeam), + invites: many(teamInvite), + logs: many(log), + backupJobs: many(backupJob), +})); -export const userToTeam = sqliteTable("user_to_team", { - userId: text("user_id").notNull().references(() => user.id, { onDelete: "cascade" }), - teamId: text("team_id").notNull().references(() => team.id, { onDelete: "cascade" }), - role: roleType.notNull().default("USER") -}); +export const userToTeam = sqliteTable( + "user_to_team", + { + userId: text("user_id") + .notNull() + .references(() => user.id, { onDelete: "cascade" }), + teamId: text("team_id") + .notNull() + .references(() => team.id, { onDelete: "cascade" }), + role: roleType.notNull().default("MEMBER"), + }, + (table) => [ + primaryKey({ columns: [table.userId, table.teamId] }), // composite primary key + ], +); + +export const userToTeamRelations = relations(userToTeam, ({ one }) => ({ + user: one(user, { + fields: [userToTeam.userId], + references: [user.id], + }), + team: one(team, { + fields: [userToTeam.teamId], + references: [team.id], + }), +})); -export const backupJob = sqliteTable("backup_job",{ - id: standardIdFactory().primaryKey(), - +export const teamInvite = sqliteTable("team_invite", { + id: standardIdFactory("invite_").primaryKey(), + teamId: text("team_id") + .notNull() + .references(() => team.id, { onDelete: "cascade" }), + email: text("email").notNull(), + createdAt: standardDateFactory(), + expiresAt: integer({ mode: "timestamp_ms" }).notNull(), + acceptedAt: integer({ mode: "timestamp_ms" }), }); +export const teamInviteRelations = relations(teamInvite, ({ one }) => ({ + team: one(team, { + fields: [teamInvite.teamId], + references: [team.id], + }), +})); +export const backupJob = sqliteTable("backup_job", { + id: standardIdFactory("job_").primaryKey(), + name: standardVarcharFactory(), + authenticationData: text({ mode: "json" }).notNull(), //This type might need to be altered. We will see how nice it plays when we write our first data here. + databaseType: databaseType.notNull(), + cronString: standardVarcharFactory(), + teamId: text("team_id") + .notNull() + .references(() => team.id, { onDelete: "cascade" }), +}); +export const backupJobRelations = relations(backupJob, ({ one, many }) => ({ + team: one(team, { + fields: [backupJob.teamId], + references: [team.id], + }), + runs: many(backupJobRun), +})); +export const backupJobRun = sqliteTable("backup_job_run", { + id: standardIdFactory().primaryKey(), + invocationType: invocationType.notNull(), + backupJobId: text("backup_job_id") + .notNull() + .references(() => backupJob.id, { onDelete: "cascade" }), + startedAt: standardDateFactory(), + completedAt: integer({ mode: "timestamp_ms" }), + result: backupResult, +}); +export const backupJobRunRelations = relations(backupJobRun, ({ one }) => ({ + backupJob: one(backupJob, { + fields: [backupJobRun.backupJobId], + references: [backupJob.id], + }), +})); +export const log = sqliteTable("log", { + id: standardIdFactory().primaryKey(), + logType: logType.notNull(), + message: standardVarcharFactory(), + occurredAt: standardDateFactory(), + teamId: text("team_id"), +}); - - +export const logRelations = relations(log, ({ one }) => ({ + team: one(team, { + fields: [log.teamId], + references: [team.id], + }), +})); // Auth Tables - DO NOT MODIFY export const session = sqliteTable("session", (t) => ({ - id: t.text("id").primaryKey(), - expiresAt: t.integer("expires_at", { mode: "timestamp" }).notNull(), - token: t.text("token").notNull().unique(), - createdAt: t.integer("created_at", { mode: "timestamp" }).notNull(), - updatedAt: t.integer("updated_at", { mode: "timestamp" }).notNull(), - ipAddress: t.text("ip_address"), - userAgent: t.text("user_agent"), - userId: t - .text("user_id") - .notNull() - .references(() => user.id, { onDelete: "cascade" }), + id: t.text("id").primaryKey(), + expiresAt: t.integer("expires_at", { mode: "timestamp" }).notNull(), + token: t.text("token").notNull().unique(), + createdAt: t.integer("created_at", { mode: "timestamp" }).notNull(), + updatedAt: t.integer("updated_at", { mode: "timestamp" }).notNull(), + ipAddress: t.text("ip_address"), + userAgent: t.text("user_agent"), + userId: t + .text("user_id") + .notNull() + .references(() => user.id, { onDelete: "cascade" }), })); export const account = sqliteTable("account", (t) => ({ - id: t.text("id").primaryKey(), - accountId: t.text("account_id").notNull(), - providerId: t.text("provider_id").notNull(), - userId: t - .text("user_id") - .notNull() - .references(() => user.id, { onDelete: "cascade" }), - accessToken: t.text("access_token"), - refreshToken: t.text("refresh_token"), - idToken: t.text("id_token"), - accessTokenExpiresAt: t.integer("access_token_expires_at", { - mode: "timestamp", - }), - refreshTokenExpiresAt: t.integer("refresh_token_expires_at", { - mode: "timestamp", - }), - scope: t.text("scope"), - password: t.text("password"), - createdAt: t.integer("created_at", { mode: "timestamp" }).notNull(), - updatedAt: t.integer("updated_at", { mode: "timestamp" }).notNull(), + id: t.text("id").primaryKey(), + accountId: t.text("account_id").notNull(), + providerId: t.text("provider_id").notNull(), + userId: t + .text("user_id") + .notNull() + .references(() => user.id, { onDelete: "cascade" }), + accessToken: t.text("access_token"), + refreshToken: t.text("refresh_token"), + idToken: t.text("id_token"), + accessTokenExpiresAt: t.integer("access_token_expires_at", { + mode: "timestamp", + }), + refreshTokenExpiresAt: t.integer("refresh_token_expires_at", { + mode: "timestamp", + }), + scope: t.text("scope"), + password: t.text("password"), + createdAt: t.integer("created_at", { mode: "timestamp" }).notNull(), + updatedAt: t.integer("updated_at", { mode: "timestamp" }).notNull(), })); export const verification = sqliteTable("verification", (t) => ({ - id: t.text("id").primaryKey(), - identifier: t.text("identifier").notNull(), - value: t.text("value").notNull(), - expiresAt: t.integer("expires_at", { mode: "timestamp" }).notNull(), - createdAt: t.integer("created_at", { mode: "timestamp" }), - updatedAt: t.integer("updated_at", { mode: "timestamp" }), -})); \ No newline at end of file + id: t.text("id").primaryKey(), + identifier: t.text("identifier").notNull(), + value: t.text("value").notNull(), + expiresAt: t.integer("expires_at", { mode: "timestamp" }).notNull(), + createdAt: t.integer("created_at", { mode: "timestamp" }), + updatedAt: t.integer("updated_at", { mode: "timestamp" }), +})); diff --git a/packages/shared/constants.ts b/packages/shared/constants.ts index 7cd05bf..af96feb 100644 --- a/packages/shared/constants.ts +++ b/packages/shared/constants.ts @@ -1,49 +1,49 @@ -export const APP_NAME = "Fallback" +export const APP_NAME = "Fallback"; export const USER_PERMISSIONS = { - member: { - canView: true, - canEdit: false, - canDelete: false, - }, + member: { + canView: true, + canEdit: false, + canDelete: false, + }, }; export const AUTH_CONFIG = { - emailAndPassword: { - enabled: true, - minPasswordLength: 8, - maxPasswordLength: 64, - }, - socialProviders: { - google: { - enabled: true, - icon_path: "some test idk", - }, - discord: { - enabled: true, - icon_path: "some test idk", - }, - github: { - enabled: true, - icon_path: "some test idk", - }, - linear: { - enabled: true, - icon_path: "some test idk", - }, - }, + emailAndPassword: { + enabled: true, + minPasswordLength: 8, + maxPasswordLength: 64, + }, + socialProviders: { + google: { + enabled: true, + icon_path: "some test idk", + }, + discord: { + enabled: true, + icon_path: "some test idk", + }, + github: { + enabled: true, + icon_path: "some test idk", + }, + linear: { + enabled: true, + icon_path: "some test idk", + }, + }, }; export const GREETINGS_FUNCTIONS = { - onSignIn: [ - (name: string) => `Welcome back, ${name}!`, - (name: string) => `Hello again, ${name}!`, - (name: string) => `Good to see you again, ${name}!`, - (name: string) => `Hey ${name}, welcome back!`, - (name: string) => `Hi ${name}, glad you're back!`, - ], - onSignUp: [ - (name: string) => `Welcome to the platform, ${name}!`, - (name: string) => `Hi ${name}, we're glad to have you here!`, - (name: string) => `Hello ${name}, thanks for joining us!`, - (name: string) => `Welcome aboard ${name}!`, - ], -}; \ No newline at end of file + onSignIn: [ + (name: string) => `Welcome back, ${name}!`, + (name: string) => `Hello again, ${name}!`, + (name: string) => `Good to see you again, ${name}!`, + (name: string) => `Hey ${name}, welcome back!`, + (name: string) => `Hi ${name}, glad you're back!`, + ], + onSignUp: [ + (name: string) => `Welcome to the platform, ${name}!`, + (name: string) => `Hi ${name}, we're glad to have you here!`, + (name: string) => `Hello ${name}, thanks for joining us!`, + (name: string) => `Welcome aboard ${name}!`, + ], +}; diff --git a/packages/shared/index.ts b/packages/shared/index.ts index 99e501e..6deddb9 100644 --- a/packages/shared/index.ts +++ b/packages/shared/index.ts @@ -1,3 +1,3 @@ export * from "./constants"; // export * from "./types"; -// export * from "./zod"; \ No newline at end of file +// export * from "./zod";