diff --git a/apps/api/drizzle/0003_overrated_nightshade.sql b/apps/api/drizzle/0003_overrated_nightshade.sql new file mode 100644 index 0000000..e2b8210 --- /dev/null +++ b/apps/api/drizzle/0003_overrated_nightshade.sql @@ -0,0 +1,11 @@ +CREATE TABLE "routines" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "user_id" uuid NOT NULL, + "name" text NOT NULL, + "description" text, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL +); +--> statement-breakpoint +ALTER TABLE "routines" ADD CONSTRAINT "routines_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +CREATE INDEX "routines_user_id_idx" ON "routines" USING btree ("user_id"); \ No newline at end of file diff --git a/apps/api/drizzle/meta/0003_snapshot.json b/apps/api/drizzle/meta/0003_snapshot.json new file mode 100644 index 0000000..57c932b --- /dev/null +++ b/apps/api/drizzle/meta/0003_snapshot.json @@ -0,0 +1,308 @@ +{ + "id": "72d07b04-09c7-43fa-a79a-4ea70a6a75ad", + "prevId": "47894e1e-8c9d-4bb1-b7cf-f9c7bc139177", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.exercises": { + "name": "exercises", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "owner_user_id": { + "name": "owner_user_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "exercises_owner_user_id_idx": { + "name": "exercises_owner_user_id_idx", + "columns": [ + { + "expression": "owner_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "exercises_owner_user_id_users_id_fk": { + "name": "exercises_owner_user_id_users_id_fk", + "tableFrom": "exercises", + "tableTo": "users", + "columnsFrom": ["owner_user_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.routines": { + "name": "routines", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "routines_user_id_idx": { + "name": "routines_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "routines_user_id_users_id_fk": { + "name": "routines_user_id_users_id_fk", + "tableFrom": "routines", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.sessions": { + "name": "sessions", + "schema": "", + "columns": { + "sid": { + "name": "sid", + "type": "varchar", + "primaryKey": true, + "notNull": true + }, + "sess": { + "name": "sess", + "type": "json", + "primaryKey": false, + "notNull": true + }, + "expire": { + "name": "expire", + "type": "timestamp (6)", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "sessions_expire_idx": { + "name": "sessions_expire_idx", + "columns": [ + { + "expression": "expire", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "avatar_url": { + "name": "avatar_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "auth_provider": { + "name": "auth_provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "auth_provider_subject": { + "name": "auth_provider_subject", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "users_auth_provider_subject_unique": { + "name": "users_auth_provider_subject_unique", + "columns": [ + { + "expression": "auth_provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "auth_provider_subject", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} diff --git a/apps/api/drizzle/meta/_journal.json b/apps/api/drizzle/meta/_journal.json index 26458ea..f834cac 100644 --- a/apps/api/drizzle/meta/_journal.json +++ b/apps/api/drizzle/meta/_journal.json @@ -22,6 +22,13 @@ "when": 1787607334174, "tag": "0002_illegal_captain_flint", "breakpoints": true + }, + { + "idx": 3, + "version": "7", + "when": 1787608216413, + "tag": "0003_overrated_nightshade", + "breakpoints": true } ] } diff --git a/apps/api/src/db/schema/index.ts b/apps/api/src/db/schema/index.ts index ac404c4..1651bc4 100644 --- a/apps/api/src/db/schema/index.ts +++ b/apps/api/src/db/schema/index.ts @@ -1,4 +1,5 @@ /** Exposes the complete Drizzle schema from feature-focused table modules. */ export * from './exercises.js'; +export * from './routines.js'; export * from './sessions.js'; export * from './users.js'; diff --git a/apps/api/src/db/schema/routines.ts b/apps/api/src/db/schema/routines.ts new file mode 100644 index 0000000..089c955 --- /dev/null +++ b/apps/api/src/db/schema/routines.ts @@ -0,0 +1,34 @@ +/** Defines reusable workout routines owned by registered users. */ +import { index, pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core'; + +import { users } from './users.js'; + +export const routines = pgTable( + 'routines', + { + /** Primary identifier for the routine. */ + id: uuid('id').defaultRandom().primaryKey(), + + /** Registered user who owns and controls the routine. */ + userId: uuid('user_id') + .notNull() + .references(() => users.id), + + /** User-visible routine name, such as Upper Body. */ + name: text('name').notNull(), + + /** Optional description or notes about the routine. */ + description: text('description'), + + /** Time when the routine was created. */ + createdAt: timestamp('created_at', { withTimezone: true }) + .defaultNow() + .notNull(), + + /** Time when the routine was last changed. */ + updatedAt: timestamp('updated_at', { withTimezone: true }) + .defaultNow() + .notNull(), + }, + (table) => [index('routines_user_id_idx').on(table.userId)], +);