mirror of
https://github.com/hex248/sprint.git
synced 2026-02-07 18:23:03 +00:00
full comments system
This commit is contained in:
8
packages/backend/drizzle/0021_issue_comments.sql
Normal file
8
packages/backend/drizzle/0021_issue_comments.sql
Normal file
@@ -0,0 +1,8 @@
|
||||
CREATE TABLE "IssueComment" (
|
||||
"id" integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
|
||||
"issueId" integer NOT NULL REFERENCES "Issue" ("id") ON DELETE CASCADE,
|
||||
"userId" integer NOT NULL REFERENCES "User" ("id") ON DELETE CASCADE,
|
||||
"body" varchar(2048) NOT NULL,
|
||||
"createdAt" timestamp NOT NULL DEFAULT now(),
|
||||
"updatedAt" timestamp NOT NULL DEFAULT now()
|
||||
);
|
||||
11
packages/backend/drizzle/0021_skinny_sally_floyd.sql
Normal file
11
packages/backend/drizzle/0021_skinny_sally_floyd.sql
Normal file
@@ -0,0 +1,11 @@
|
||||
CREATE TABLE "IssueComment" (
|
||||
"id" integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY (sequence name "IssueComment_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 CACHE 1),
|
||||
"issueId" integer NOT NULL,
|
||||
"userId" integer NOT NULL,
|
||||
"body" varchar(2048) NOT NULL,
|
||||
"createdAt" timestamp DEFAULT now(),
|
||||
"updatedAt" timestamp DEFAULT now()
|
||||
);
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "IssueComment" ADD CONSTRAINT "IssueComment_issueId_Issue_id_fk" FOREIGN KEY ("issueId") REFERENCES "public"."Issue"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "IssueComment" ADD CONSTRAINT "IssueComment_userId_User_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."User"("id") ON DELETE cascade ON UPDATE no action;
|
||||
909
packages/backend/drizzle/meta/0021_snapshot.json
Normal file
909
packages/backend/drizzle/meta/0021_snapshot.json
Normal file
@@ -0,0 +1,909 @@
|
||||
{
|
||||
"id": "c19db523-bbc5-4069-9eea-bdd37cf36c64",
|
||||
"prevId": "fb4c4093-9a4d-43b2-844d-f8e9912c564a",
|
||||
"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(64)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"description": {
|
||||
"name": "description",
|
||||
"type": "varchar(2048)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"status": {
|
||||
"name": "status",
|
||||
"type": "varchar(24)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "'TO DO'"
|
||||
},
|
||||
"creatorId": {
|
||||
"name": "creatorId",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"sprintId": {
|
||||
"name": "sprintId",
|
||||
"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_creatorId_User_id_fk": {
|
||||
"name": "Issue_creatorId_User_id_fk",
|
||||
"tableFrom": "Issue",
|
||||
"tableTo": "User",
|
||||
"columnsFrom": [
|
||||
"creatorId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"Issue_sprintId_Sprint_id_fk": {
|
||||
"name": "Issue_sprintId_Sprint_id_fk",
|
||||
"tableFrom": "Issue",
|
||||
"tableTo": "Sprint",
|
||||
"columnsFrom": [
|
||||
"sprintId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.IssueAssignee": {
|
||||
"name": "IssueAssignee",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "integer",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"identity": {
|
||||
"type": "always",
|
||||
"name": "IssueAssignee_id_seq",
|
||||
"schema": "public",
|
||||
"increment": "1",
|
||||
"startWith": "1",
|
||||
"minValue": "1",
|
||||
"maxValue": "2147483647",
|
||||
"cache": "1",
|
||||
"cycle": false
|
||||
}
|
||||
},
|
||||
"issueId": {
|
||||
"name": "issueId",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"userId": {
|
||||
"name": "userId",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"assignedAt": {
|
||||
"name": "assignedAt",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": "now()"
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"unique_issue_user": {
|
||||
"name": "unique_issue_user",
|
||||
"columns": [
|
||||
{
|
||||
"expression": "issueId",
|
||||
"isExpression": false,
|
||||
"asc": true,
|
||||
"nulls": "last"
|
||||
},
|
||||
{
|
||||
"expression": "userId",
|
||||
"isExpression": false,
|
||||
"asc": true,
|
||||
"nulls": "last"
|
||||
}
|
||||
],
|
||||
"isUnique": true,
|
||||
"concurrently": false,
|
||||
"method": "btree",
|
||||
"with": {}
|
||||
}
|
||||
},
|
||||
"foreignKeys": {
|
||||
"IssueAssignee_issueId_Issue_id_fk": {
|
||||
"name": "IssueAssignee_issueId_Issue_id_fk",
|
||||
"tableFrom": "IssueAssignee",
|
||||
"tableTo": "Issue",
|
||||
"columnsFrom": [
|
||||
"issueId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"IssueAssignee_userId_User_id_fk": {
|
||||
"name": "IssueAssignee_userId_User_id_fk",
|
||||
"tableFrom": "IssueAssignee",
|
||||
"tableTo": "User",
|
||||
"columnsFrom": [
|
||||
"userId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.IssueComment": {
|
||||
"name": "IssueComment",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "integer",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"identity": {
|
||||
"type": "always",
|
||||
"name": "IssueComment_id_seq",
|
||||
"schema": "public",
|
||||
"increment": "1",
|
||||
"startWith": "1",
|
||||
"minValue": "1",
|
||||
"maxValue": "2147483647",
|
||||
"cache": "1",
|
||||
"cycle": false
|
||||
}
|
||||
},
|
||||
"issueId": {
|
||||
"name": "issueId",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"userId": {
|
||||
"name": "userId",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"body": {
|
||||
"name": "body",
|
||||
"type": "varchar(2048)",
|
||||
"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": {
|
||||
"IssueComment_issueId_Issue_id_fk": {
|
||||
"name": "IssueComment_issueId_Issue_id_fk",
|
||||
"tableFrom": "IssueComment",
|
||||
"tableTo": "Issue",
|
||||
"columnsFrom": [
|
||||
"issueId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"IssueComment_userId_User_id_fk": {
|
||||
"name": "IssueComment_userId_User_id_fk",
|
||||
"tableFrom": "IssueComment",
|
||||
"tableTo": "User",
|
||||
"columnsFrom": [
|
||||
"userId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"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(64)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"description": {
|
||||
"name": "description",
|
||||
"type": "varchar(1024)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"slug": {
|
||||
"name": "slug",
|
||||
"type": "varchar(64)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"iconURL": {
|
||||
"name": "iconURL",
|
||||
"type": "varchar(512)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"statuses": {
|
||||
"name": "statuses",
|
||||
"type": "json",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "'{\"TO DO\":\"#fafafa\",\"IN PROGRESS\":\"#f97316\",\"REVIEW\":\"#8952bc\",\"DONE\":\"#22c55e\",\"REJECTED\":\"#ef4444\",\"ARCHIVED\":\"#a1a1a1\",\"MERGED\":\"#a1a1a1\"}'::json"
|
||||
},
|
||||
"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
|
||||
}
|
||||
},
|
||||
"key": {
|
||||
"name": "key",
|
||||
"type": "varchar(4)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "varchar(64)",
|
||||
"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.Session": {
|
||||
"name": "Session",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "integer",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"identity": {
|
||||
"type": "always",
|
||||
"name": "Session_id_seq",
|
||||
"schema": "public",
|
||||
"increment": "1",
|
||||
"startWith": "1",
|
||||
"minValue": "1",
|
||||
"maxValue": "2147483647",
|
||||
"cache": "1",
|
||||
"cycle": false
|
||||
}
|
||||
},
|
||||
"userId": {
|
||||
"name": "userId",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"csrfToken": {
|
||||
"name": "csrfToken",
|
||||
"type": "varchar(64)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"expiresAt": {
|
||||
"name": "expiresAt",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"createdAt": {
|
||||
"name": "createdAt",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": "now()"
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"Session_userId_User_id_fk": {
|
||||
"name": "Session_userId_User_id_fk",
|
||||
"tableFrom": "Session",
|
||||
"tableTo": "User",
|
||||
"columnsFrom": [
|
||||
"userId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.Sprint": {
|
||||
"name": "Sprint",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "integer",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"identity": {
|
||||
"type": "always",
|
||||
"name": "Sprint_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
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "varchar(64)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"color": {
|
||||
"name": "color",
|
||||
"type": "varchar(7)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "'#a1a1a1'"
|
||||
},
|
||||
"startDate": {
|
||||
"name": "startDate",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"endDate": {
|
||||
"name": "endDate",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"createdAt": {
|
||||
"name": "createdAt",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": "now()"
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"Sprint_projectId_Project_id_fk": {
|
||||
"name": "Sprint_projectId_Project_id_fk",
|
||||
"tableFrom": "Sprint",
|
||||
"tableTo": "Project",
|
||||
"columnsFrom": [
|
||||
"projectId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.TimedSession": {
|
||||
"name": "TimedSession",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "integer",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"identity": {
|
||||
"type": "always",
|
||||
"name": "TimedSession_id_seq",
|
||||
"schema": "public",
|
||||
"increment": "1",
|
||||
"startWith": "1",
|
||||
"minValue": "1",
|
||||
"maxValue": "2147483647",
|
||||
"cache": "1",
|
||||
"cycle": false
|
||||
}
|
||||
},
|
||||
"userId": {
|
||||
"name": "userId",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"issueId": {
|
||||
"name": "issueId",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"timestamps": {
|
||||
"name": "timestamps",
|
||||
"type": "timestamp[]",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"endedAt": {
|
||||
"name": "endedAt",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"createdAt": {
|
||||
"name": "createdAt",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": "now()"
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"TimedSession_userId_User_id_fk": {
|
||||
"name": "TimedSession_userId_User_id_fk",
|
||||
"tableFrom": "TimedSession",
|
||||
"tableTo": "User",
|
||||
"columnsFrom": [
|
||||
"userId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"TimedSession_issueId_Issue_id_fk": {
|
||||
"name": "TimedSession_issueId_Issue_id_fk",
|
||||
"tableFrom": "TimedSession",
|
||||
"tableTo": "Issue",
|
||||
"columnsFrom": [
|
||||
"issueId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "set null",
|
||||
"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(64)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"username": {
|
||||
"name": "username",
|
||||
"type": "varchar(32)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"passwordHash": {
|
||||
"name": "passwordHash",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"avatarURL": {
|
||||
"name": "avatarURL",
|
||||
"type": "varchar(512)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"iconPreference": {
|
||||
"name": "iconPreference",
|
||||
"type": "varchar(10)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "'lucide'"
|
||||
},
|
||||
"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": {}
|
||||
}
|
||||
}
|
||||
@@ -148,6 +148,13 @@
|
||||
"when": 1768999911628,
|
||||
"tag": "0020_little_power_pack",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 21,
|
||||
"version": "7",
|
||||
"when": 1769021149911,
|
||||
"tag": "0021_skinny_sally_floyd",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from "./issue-comments";
|
||||
export * from "./issues";
|
||||
export * from "./organisations";
|
||||
export * from "./projects";
|
||||
|
||||
48
packages/backend/src/db/queries/issue-comments.ts
Normal file
48
packages/backend/src/db/queries/issue-comments.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import { Issue, IssueComment, type IssueCommentResponse, Project, User } from "@sprint/shared";
|
||||
import { asc, eq } from "drizzle-orm";
|
||||
import { db } from "../client";
|
||||
|
||||
export async function createIssueComment(issueId: number, userId: number, body: string) {
|
||||
const [comment] = await db
|
||||
.insert(IssueComment)
|
||||
.values({
|
||||
issueId,
|
||||
userId,
|
||||
body,
|
||||
})
|
||||
.returning();
|
||||
return comment;
|
||||
}
|
||||
|
||||
export async function deleteIssueComment(id: number) {
|
||||
return await db.delete(IssueComment).where(eq(IssueComment.id, id));
|
||||
}
|
||||
|
||||
export async function getIssueCommentById(id: number) {
|
||||
const [comment] = await db.select().from(IssueComment).where(eq(IssueComment.id, id));
|
||||
return comment;
|
||||
}
|
||||
|
||||
export async function getIssueCommentsByIssueId(issueId: number): Promise<IssueCommentResponse[]> {
|
||||
const comments = await db
|
||||
.select({
|
||||
Comment: IssueComment,
|
||||
User: User,
|
||||
})
|
||||
.from(IssueComment)
|
||||
.where(eq(IssueComment.issueId, issueId))
|
||||
.innerJoin(User, eq(IssueComment.userId, User.id))
|
||||
.orderBy(asc(IssueComment.createdAt));
|
||||
|
||||
return comments;
|
||||
}
|
||||
|
||||
export async function getIssueOrganisationId(issueId: number) {
|
||||
const [result] = await db
|
||||
.select({ organisationId: Project.organisationId })
|
||||
.from(Issue)
|
||||
.innerJoin(Project, eq(Issue.projectId, Project.id))
|
||||
.where(eq(Issue.id, issueId));
|
||||
|
||||
return result?.organisationId ?? null;
|
||||
}
|
||||
@@ -40,11 +40,14 @@ const main = async () => {
|
||||
"/issue/create": withCors(withAuth(withCSRF(routes.issueCreate))),
|
||||
"/issue/update": withCors(withAuth(withCSRF(routes.issueUpdate))),
|
||||
"/issue/delete": withCors(withAuth(withCSRF(routes.issueDelete))),
|
||||
"/issue-comment/create": withCors(withAuth(withCSRF(routes.issueCommentCreate))),
|
||||
"/issue-comment/delete": withCors(withAuth(withCSRF(routes.issueCommentDelete))),
|
||||
|
||||
"/issues/by-project": withCors(withAuth(routes.issuesByProject)),
|
||||
"/issues/replace-status": withCors(withAuth(withCSRF(routes.issuesReplaceStatus))),
|
||||
"/issues/status-count": withCors(withAuth(routes.issuesStatusCount)),
|
||||
"/issues/all": withCors(withAuth(routes.issues)),
|
||||
"/issue-comments/by-issue": withCors(withAuth(routes.issueCommentsByIssue)),
|
||||
|
||||
"/organisation/create": withCors(withAuth(withCSRF(routes.organisationCreate))),
|
||||
"/organisation/by-id": withCors(withAuth(routes.organisationById)),
|
||||
|
||||
@@ -5,6 +5,9 @@ import authRegister from "./auth/register";
|
||||
import issueCreate from "./issue/create";
|
||||
import issueDelete from "./issue/delete";
|
||||
import issueUpdate from "./issue/update";
|
||||
import issueCommentCreate from "./issue-comment/create";
|
||||
import issueCommentDelete from "./issue-comment/delete";
|
||||
import issueCommentsByIssue from "./issue-comments/by-issue";
|
||||
import issues from "./issues/all";
|
||||
import issuesByProject from "./issues/by-project";
|
||||
import issuesReplaceStatus from "./issues/replace-status";
|
||||
@@ -54,6 +57,10 @@ export const routes = {
|
||||
issueDelete,
|
||||
issueUpdate,
|
||||
|
||||
issueCommentCreate,
|
||||
issueCommentDelete,
|
||||
issueCommentsByIssue,
|
||||
|
||||
issuesByProject,
|
||||
issues,
|
||||
issuesReplaceStatus,
|
||||
|
||||
34
packages/backend/src/routes/issue-comment/create.ts
Normal file
34
packages/backend/src/routes/issue-comment/create.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { IssueCommentCreateRequestSchema } from "@sprint/shared";
|
||||
import type { AuthedRequest } from "../../auth/middleware";
|
||||
import {
|
||||
createIssueComment,
|
||||
getIssueByID,
|
||||
getIssueOrganisationId,
|
||||
getOrganisationMemberRole,
|
||||
} from "../../db/queries";
|
||||
import { errorResponse, parseJsonBody } from "../../validation";
|
||||
|
||||
export default async function issueCommentCreate(req: AuthedRequest) {
|
||||
const parsed = await parseJsonBody(req, IssueCommentCreateRequestSchema);
|
||||
if ("error" in parsed) return parsed.error;
|
||||
|
||||
const { issueId, body } = parsed.data;
|
||||
|
||||
const issue = await getIssueByID(issueId);
|
||||
if (!issue) {
|
||||
return errorResponse(`issue not found: ${issueId}`, "ISSUE_NOT_FOUND", 404);
|
||||
}
|
||||
|
||||
const organisationId = await getIssueOrganisationId(issueId);
|
||||
if (!organisationId) {
|
||||
return errorResponse(`organisation not found for issue ${issueId}`, "ORG_NOT_FOUND", 404);
|
||||
}
|
||||
|
||||
const member = await getOrganisationMemberRole(organisationId, req.userId);
|
||||
if (!member) {
|
||||
return errorResponse("forbidden", "FORBIDDEN", 403);
|
||||
}
|
||||
|
||||
const comment = await createIssueComment(issueId, req.userId, body);
|
||||
return Response.json(comment);
|
||||
}
|
||||
39
packages/backend/src/routes/issue-comment/delete.ts
Normal file
39
packages/backend/src/routes/issue-comment/delete.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { IssueCommentDeleteRequestSchema } from "@sprint/shared";
|
||||
import type { AuthedRequest } from "../../auth/middleware";
|
||||
import {
|
||||
deleteIssueComment,
|
||||
getIssueCommentById,
|
||||
getIssueOrganisationId,
|
||||
getOrganisationMemberRole,
|
||||
} from "../../db/queries";
|
||||
import { errorResponse, parseJsonBody } from "../../validation";
|
||||
|
||||
export default async function issueCommentDelete(req: AuthedRequest) {
|
||||
const parsed = await parseJsonBody(req, IssueCommentDeleteRequestSchema);
|
||||
if ("error" in parsed) return parsed.error;
|
||||
|
||||
const { id } = parsed.data;
|
||||
|
||||
const comment = await getIssueCommentById(id);
|
||||
if (!comment) {
|
||||
return errorResponse(`comment not found: ${id}`, "COMMENT_NOT_FOUND", 404);
|
||||
}
|
||||
|
||||
if (comment.userId !== req.userId) {
|
||||
return errorResponse("forbidden", "FORBIDDEN", 403);
|
||||
}
|
||||
|
||||
const organisationId = await getIssueOrganisationId(comment.issueId);
|
||||
if (!organisationId) {
|
||||
return errorResponse("organisation not found", "ORG_NOT_FOUND", 404);
|
||||
}
|
||||
|
||||
const member = await getOrganisationMemberRole(organisationId, req.userId);
|
||||
if (!member) {
|
||||
return errorResponse("forbidden", "FORBIDDEN", 403);
|
||||
}
|
||||
|
||||
await deleteIssueComment(id);
|
||||
|
||||
return Response.json({ success: true });
|
||||
}
|
||||
35
packages/backend/src/routes/issue-comments/by-issue.ts
Normal file
35
packages/backend/src/routes/issue-comments/by-issue.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { IssueCommentsByIssueQuerySchema } from "@sprint/shared";
|
||||
import type { AuthedRequest } from "../../auth/middleware";
|
||||
import {
|
||||
getIssueByID,
|
||||
getIssueCommentsByIssueId,
|
||||
getIssueOrganisationId,
|
||||
getOrganisationMemberRole,
|
||||
} from "../../db/queries";
|
||||
import { errorResponse, parseQueryParams } from "../../validation";
|
||||
|
||||
export default async function issueCommentsByIssue(req: AuthedRequest) {
|
||||
const url = new URL(req.url);
|
||||
const parsed = parseQueryParams(url, IssueCommentsByIssueQuerySchema);
|
||||
if ("error" in parsed) return parsed.error;
|
||||
|
||||
const { issueId } = parsed.data;
|
||||
|
||||
const issue = await getIssueByID(issueId);
|
||||
if (!issue) {
|
||||
return errorResponse(`issue not found: ${issueId}`, "ISSUE_NOT_FOUND", 404);
|
||||
}
|
||||
|
||||
const organisationId = await getIssueOrganisationId(issueId);
|
||||
if (!organisationId) {
|
||||
return errorResponse(`organisation not found for issue ${issueId}`, "ORG_NOT_FOUND", 404);
|
||||
}
|
||||
|
||||
const member = await getOrganisationMemberRole(organisationId, req.userId);
|
||||
if (!member) {
|
||||
return errorResponse("forbidden", "FORBIDDEN", 403);
|
||||
}
|
||||
|
||||
const comments = await getIssueCommentsByIssueId(issueId);
|
||||
return Response.json(comments);
|
||||
}
|
||||
Reference in New Issue
Block a user