From 2b086101acd13120302680cf786b36904a22b051 Mon Sep 17 00:00:00 2001 From: rd-code-23 Date: Mon, 24 Aug 2026 14:45:57 -0700 Subject: [PATCH] feat(api): add exercises table --- .../drizzle/0002_illegal_captain_flint.sql | 11 + apps/api/drizzle/meta/0002_snapshot.json | 230 ++++++++++++++++++ apps/api/drizzle/meta/_journal.json | 7 + apps/api/src/db/schema/exercises.ts | 32 +++ apps/api/src/db/schema/index.ts | 1 + 5 files changed, 281 insertions(+) create mode 100644 apps/api/drizzle/0002_illegal_captain_flint.sql create mode 100644 apps/api/drizzle/meta/0002_snapshot.json create mode 100644 apps/api/src/db/schema/exercises.ts diff --git a/apps/api/drizzle/0002_illegal_captain_flint.sql b/apps/api/drizzle/0002_illegal_captain_flint.sql new file mode 100644 index 0000000..1e1d421 --- /dev/null +++ b/apps/api/drizzle/0002_illegal_captain_flint.sql @@ -0,0 +1,11 @@ +CREATE TABLE "exercises" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "owner_user_id" uuid, + "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 "exercises" ADD CONSTRAINT "exercises_owner_user_id_users_id_fk" FOREIGN KEY ("owner_user_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +CREATE INDEX "exercises_owner_user_id_idx" ON "exercises" USING btree ("owner_user_id"); \ No newline at end of file diff --git a/apps/api/drizzle/meta/0002_snapshot.json b/apps/api/drizzle/meta/0002_snapshot.json new file mode 100644 index 0000000..25140f1 --- /dev/null +++ b/apps/api/drizzle/meta/0002_snapshot.json @@ -0,0 +1,230 @@ +{ + "id": "47894e1e-8c9d-4bb1-b7cf-f9c7bc139177", + "prevId": "f3030a79-0c25-4a1d-9fd6-131e2bfd5f62", + "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.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 022f4ed..26458ea 100644 --- a/apps/api/drizzle/meta/_journal.json +++ b/apps/api/drizzle/meta/_journal.json @@ -15,6 +15,13 @@ "when": 1787093265922, "tag": "0001_cloudy_whirlwind", "breakpoints": true + }, + { + "idx": 2, + "version": "7", + "when": 1787607334174, + "tag": "0002_illegal_captain_flint", + "breakpoints": true } ] } diff --git a/apps/api/src/db/schema/exercises.ts b/apps/api/src/db/schema/exercises.ts new file mode 100644 index 0000000..5a4557a --- /dev/null +++ b/apps/api/src/db/schema/exercises.ts @@ -0,0 +1,32 @@ +/** Defines built-in and user-owned exercises available for routine building. */ +import { index, pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core'; + +import { users } from './users.js'; + +export const exercises = pgTable( + 'exercises', + { + /** Primary identifier for the exercise. */ + id: uuid('id').defaultRandom().primaryKey(), + + /** Owning user for a custom exercise; null identifies a built-in exercise. */ + ownerUserId: uuid('owner_user_id').references(() => users.id), + + /** User-visible exercise name, such as Bicep Curl. */ + name: text('name').notNull(), + + /** Optional instructions or explanation for performing the exercise. */ + description: text('description'), + + /** Time when the exercise was created. */ + createdAt: timestamp('created_at', { withTimezone: true }) + .defaultNow() + .notNull(), + + /** Time when the exercise name or description was last changed. */ + updatedAt: timestamp('updated_at', { withTimezone: true }) + .defaultNow() + .notNull(), + }, + (table) => [index('exercises_owner_user_id_idx').on(table.ownerUserId)], +); diff --git a/apps/api/src/db/schema/index.ts b/apps/api/src/db/schema/index.ts index e7cafb7..ac404c4 100644 --- a/apps/api/src/db/schema/index.ts +++ b/apps/api/src/db/schema/index.ts @@ -1,3 +1,4 @@ /** Exposes the complete Drizzle schema from feature-focused table modules. */ +export * from './exercises.js'; export * from './sessions.js'; export * from './users.js';