diff --git a/packages/backend/drizzle/0005_great_timeslip.sql b/packages/backend/drizzle/0005_great_timeslip.sql new file mode 100644 index 0000000..c7f5e9b --- /dev/null +++ b/packages/backend/drizzle/0005_great_timeslip.sql @@ -0,0 +1,26 @@ +CREATE TABLE "Organisation" ( + "id" integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY (sequence name "Organisation_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 CACHE 1), + "name" varchar(256) NOT NULL, + "description" varchar(1024), + "slug" varchar(64) NOT NULL, + "createdAt" timestamp DEFAULT now(), + "updatedAt" timestamp DEFAULT now(), + CONSTRAINT "Organisation_slug_unique" UNIQUE("slug") +); +--> statement-breakpoint +CREATE TABLE "OrganisationMember" ( + "id" integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY (sequence name "OrganisationMember_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 CACHE 1), + "organisationId" integer NOT NULL, + "userId" integer NOT NULL, + "role" varchar(32) NOT NULL, + "createdAt" timestamp DEFAULT now() +); +--> statement-breakpoint +ALTER TABLE "Project" RENAME COLUMN "ownerId" TO "creatorId";--> statement-breakpoint +ALTER TABLE "Project" DROP CONSTRAINT "Project_ownerId_User_id_fk"; +--> statement-breakpoint +ALTER TABLE "Project" ADD COLUMN "organisationId" integer NOT NULL;--> statement-breakpoint +ALTER TABLE "OrganisationMember" ADD CONSTRAINT "OrganisationMember_organisationId_Organisation_id_fk" FOREIGN KEY ("organisationId") REFERENCES "public"."Organisation"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "OrganisationMember" ADD CONSTRAINT "OrganisationMember_userId_User_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."User"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "Project" ADD CONSTRAINT "Project_organisationId_Organisation_id_fk" FOREIGN KEY ("organisationId") REFERENCES "public"."Organisation"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "Project" ADD CONSTRAINT "Project_creatorId_User_id_fk" FOREIGN KEY ("creatorId") REFERENCES "public"."User"("id") ON DELETE no action ON UPDATE no action; \ No newline at end of file diff --git a/packages/backend/drizzle/meta/0005_snapshot.json b/packages/backend/drizzle/meta/0005_snapshot.json new file mode 100644 index 0000000..82b991c --- /dev/null +++ b/packages/backend/drizzle/meta/0005_snapshot.json @@ -0,0 +1,431 @@ +{ + "id": "c7a99155-1dc7-414d-88b6-8f485daa0c58", + "prevId": "7add1c5b-39bd-495a-9728-ef7da4b40405", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.Issue": { + "name": "Issue", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "Issue_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "2147483647", + "cache": "1", + "cycle": false + } + }, + "projectId": { + "name": "projectId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "number": { + "name": "number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "varchar(256)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "varchar(2048)", + "primaryKey": false, + "notNull": true + }, + "assigneeId": { + "name": "assigneeId", + "type": "integer", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "unique_project_issue_number": { + "name": "unique_project_issue_number", + "columns": [ + { + "expression": "projectId", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "number", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "Issue_projectId_Project_id_fk": { + "name": "Issue_projectId_Project_id_fk", + "tableFrom": "Issue", + "tableTo": "Project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "Issue_assigneeId_User_id_fk": { + "name": "Issue_assigneeId_User_id_fk", + "tableFrom": "Issue", + "tableTo": "User", + "columnsFrom": [ + "assigneeId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.Organisation": { + "name": "Organisation", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "Organisation_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "2147483647", + "cache": "1", + "cycle": false + } + }, + "name": { + "name": "name", + "type": "varchar(256)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "varchar(1024)", + "primaryKey": false, + "notNull": false + }, + "slug": { + "name": "slug", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "Organisation_slug_unique": { + "name": "Organisation_slug_unique", + "nullsNotDistinct": false, + "columns": [ + "slug" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.OrganisationMember": { + "name": "OrganisationMember", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "OrganisationMember_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "2147483647", + "cache": "1", + "cycle": false + } + }, + "organisationId": { + "name": "organisationId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "OrganisationMember_organisationId_Organisation_id_fk": { + "name": "OrganisationMember_organisationId_Organisation_id_fk", + "tableFrom": "OrganisationMember", + "tableTo": "Organisation", + "columnsFrom": [ + "organisationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "OrganisationMember_userId_User_id_fk": { + "name": "OrganisationMember_userId_User_id_fk", + "tableFrom": "OrganisationMember", + "tableTo": "User", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.Project": { + "name": "Project", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "Project_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "2147483647", + "cache": "1", + "cycle": false + } + }, + "blob": { + "name": "blob", + "type": "varchar(4)", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(256)", + "primaryKey": false, + "notNull": true + }, + "organisationId": { + "name": "organisationId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "creatorId": { + "name": "creatorId", + "type": "integer", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "Project_organisationId_Organisation_id_fk": { + "name": "Project_organisationId_Organisation_id_fk", + "tableFrom": "Project", + "tableTo": "Organisation", + "columnsFrom": [ + "organisationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "Project_creatorId_User_id_fk": { + "name": "Project_creatorId_User_id_fk", + "tableFrom": "Project", + "tableTo": "User", + "columnsFrom": [ + "creatorId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.User": { + "name": "User", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "User_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "2147483647", + "cache": "1", + "cycle": false + } + }, + "name": { + "name": "name", + "type": "varchar(256)", + "primaryKey": false, + "notNull": true + }, + "username": { + "name": "username", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true + }, + "passwordHash": { + "name": "passwordHash", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "User_username_unique": { + "name": "User_username_unique", + "nullsNotDistinct": false, + "columns": [ + "username" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/packages/backend/drizzle/meta/_journal.json b/packages/backend/drizzle/meta/_journal.json index ab1ab28..dcde1a6 100644 --- a/packages/backend/drizzle/meta/_journal.json +++ b/packages/backend/drizzle/meta/_journal.json @@ -36,6 +36,13 @@ "when": 1766365630065, "tag": "0004_slow_demogoblin", "breakpoints": true + }, + { + "idx": 5, + "version": "7", + "when": 1766433489198, + "tag": "0005_great_timeslip", + "breakpoints": true } ] } \ No newline at end of file diff --git a/packages/shared/src/schema.ts b/packages/shared/src/schema.ts index 25e7b8f..786d8ab 100644 --- a/packages/shared/src/schema.ts +++ b/packages/shared/src/schema.ts @@ -1,6 +1,6 @@ import { integer, pgTable, timestamp, uniqueIndex, varchar } from "drizzle-orm/pg-core"; import { createInsertSchema, createSelectSchema } from "drizzle-zod"; -import { z } from "zod"; +import type { z } from "zod"; export const User = pgTable("User", { id: integer().primaryKey().generatedAlwaysAsIdentity(), @@ -11,11 +11,35 @@ export const User = pgTable("User", { updatedAt: timestamp({ withTimezone: false }).defaultNow(), }); +export const Organisation = pgTable("Organisation", { + id: integer().primaryKey().generatedAlwaysAsIdentity(), + name: varchar({ length: 256 }).notNull(), + description: varchar({ length: 1024 }), + slug: varchar({ length: 64 }).notNull().unique(), + createdAt: timestamp({ withTimezone: false }).defaultNow(), + updatedAt: timestamp({ withTimezone: false }).defaultNow(), +}); + +export const OrganisationMember = pgTable("OrganisationMember", { + id: integer().primaryKey().generatedAlwaysAsIdentity(), + organisationId: integer() + .notNull() + .references(() => Organisation.id), + userId: integer() + .notNull() + .references(() => User.id), + role: varchar({ length: 32 }).notNull(), + createdAt: timestamp({ withTimezone: false }).defaultNow(), +}); + export const Project = pgTable("Project", { id: integer().primaryKey().generatedAlwaysAsIdentity(), blob: varchar({ length: 4 }).notNull(), name: varchar({ length: 256 }).notNull(), - ownerId: integer() + organisationId: integer() + .notNull() + .references(() => Organisation.id), + creatorId: integer() .notNull() .references(() => User.id), }); @@ -46,6 +70,12 @@ export const Issue = pgTable( export const UserSelectSchema = createSelectSchema(User); export const UserInsertSchema = createInsertSchema(User); +export const OrganisationSelectSchema = createSelectSchema(Organisation); +export const OrganisationInsertSchema = createInsertSchema(Organisation); + +export const OrganisationMemberSelectSchema = createSelectSchema(OrganisationMember); +export const OrganisationMemberInsertSchema = createInsertSchema(OrganisationMember); + export const ProjectSelectSchema = createSelectSchema(Project); export const ProjectInsertSchema = createInsertSchema(Project); @@ -56,6 +86,12 @@ export const IssueInsertSchema = createInsertSchema(Issue); export type UserRecord = z.infer; export type UserInsert = z.infer; +export type OrganisationRecord = z.infer; +export type OrganisationInsert = z.infer; + +export type OrganisationMemberRecord = z.infer; +export type OrganisationMemberInsert = z.infer; + export type ProjectRecord = z.infer; export type ProjectInsert = z.infer; @@ -71,5 +107,6 @@ export type IssueResponse = { export type ProjectResponse = { Project: ProjectRecord; - User: UserRecord; + Organisation: OrganisationRecord; + Creator: UserRecord; };