mirror of
https://github.com/hex248/sprint.git
synced 2026-02-08 02:33:01 +00:00
Project.blob -> Project.key
This commit is contained in:
1
packages/backend/drizzle/0006_wise_kree.sql
Normal file
1
packages/backend/drizzle/0006_wise_kree.sql
Normal file
@@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE "Project" RENAME COLUMN "blob" TO "key";
|
||||||
431
packages/backend/drizzle/meta/0006_snapshot.json
Normal file
431
packages/backend/drizzle/meta/0006_snapshot.json
Normal file
@@ -0,0 +1,431 @@
|
|||||||
|
{
|
||||||
|
"id": "e65db5fa-6d8f-43f1-aea1-154a085302bc",
|
||||||
|
"prevId": "c7a99155-1dc7-414d-88b6-8f485daa0c58",
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"key": {
|
||||||
|
"name": "key",
|
||||||
|
"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": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -43,6 +43,13 @@
|
|||||||
"when": 1766433489198,
|
"when": 1766433489198,
|
||||||
"tag": "0005_great_timeslip",
|
"tag": "0005_great_timeslip",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 6,
|
||||||
|
"version": "7",
|
||||||
|
"when": 1766988874206,
|
||||||
|
"tag": "0006_wise_kree",
|
||||||
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -2,11 +2,11 @@ import { Issue, Organisation, Project, User } from "@issue/shared";
|
|||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import { db } from "../client";
|
import { db } from "../client";
|
||||||
|
|
||||||
export async function createProject(blob: string, name: string, creatorId: number, organisationId: number) {
|
export async function createProject(key: string, name: string, creatorId: number, organisationId: number) {
|
||||||
const [project] = await db
|
const [project] = await db
|
||||||
.insert(Project)
|
.insert(Project)
|
||||||
.values({
|
.values({
|
||||||
blob,
|
key,
|
||||||
name,
|
name,
|
||||||
creatorId,
|
creatorId,
|
||||||
organisationId,
|
organisationId,
|
||||||
@@ -17,7 +17,7 @@ export async function createProject(blob: string, name: string, creatorId: numbe
|
|||||||
|
|
||||||
export async function updateProject(
|
export async function updateProject(
|
||||||
projectId: number,
|
projectId: number,
|
||||||
updates: { blob?: string; name?: string; creatorId?: number; organisationId?: number },
|
updates: { key?: string; name?: string; creatorId?: number; organisationId?: number },
|
||||||
) {
|
) {
|
||||||
const [project] = await db.update(Project).set(updates).where(eq(Project.id, projectId)).returning();
|
const [project] = await db.update(Project).set(updates).where(eq(Project.id, projectId)).returning();
|
||||||
return project;
|
return project;
|
||||||
@@ -35,8 +35,8 @@ export async function getProjectByID(projectId: number) {
|
|||||||
return project;
|
return project;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getProjectByBlob(projectBlob: string) {
|
export async function getProjectByKey(projectKey: string) {
|
||||||
const [project] = await db.select().from(Project).where(eq(Project.blob, projectBlob));
|
const [project] = await db.select().from(Project).where(eq(Project.key, projectKey));
|
||||||
return project;
|
return project;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ const main = async () => {
|
|||||||
"/issue/create": withCors(withAuth(routes.issueCreate)),
|
"/issue/create": withCors(withAuth(routes.issueCreate)),
|
||||||
"/issue/update": withCors(withAuth(routes.issueUpdate)),
|
"/issue/update": withCors(withAuth(routes.issueUpdate)),
|
||||||
"/issue/delete": withCors(withAuth(routes.issueDelete)),
|
"/issue/delete": withCors(withAuth(routes.issueDelete)),
|
||||||
"/issues/:projectBlob": withCors(withAuth(routes.issuesInProject)),
|
"/issues/:projectKey": withCors(withAuth(routes.issuesInProject)),
|
||||||
"/issues/all": withCors(withAuth(routes.issues)),
|
"/issues/all": withCors(withAuth(routes.issues)),
|
||||||
|
|
||||||
"/organisation/create": withCors(withAuth(routes.organisationCreate)),
|
"/organisation/create": withCors(withAuth(routes.organisationCreate)),
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import authRegister from "./auth/register";
|
|||||||
import issueCreate from "./issue/create";
|
import issueCreate from "./issue/create";
|
||||||
import issueDelete from "./issue/delete";
|
import issueDelete from "./issue/delete";
|
||||||
import issueUpdate from "./issue/update";
|
import issueUpdate from "./issue/update";
|
||||||
import issuesInProject from "./issues/[projectBlob]";
|
import issuesInProject from "./issues/[projectKey]";
|
||||||
import issues from "./issues/all";
|
import issues from "./issues/all";
|
||||||
import organisationAddMember from "./organisation/add-member";
|
import organisationAddMember from "./organisation/add-member";
|
||||||
import organisationById from "./organisation/by-id";
|
import organisationById from "./organisation/by-id";
|
||||||
|
|||||||
@@ -1,24 +1,24 @@
|
|||||||
import type { BunRequest } from "bun";
|
import type { BunRequest } from "bun";
|
||||||
import { createIssue, getProjectByID, getProjectByBlob } from "../../db/queries";
|
import { createIssue, getProjectByID, getProjectByKey } from "../../db/queries";
|
||||||
|
|
||||||
// /issue/create?projectId=1&title=Testing&description=Description
|
// /issue/create?projectId=1&title=Testing&description=Description
|
||||||
// OR
|
// OR
|
||||||
// /issue/create?projectBlob=projectBlob&title=Testing&description=Description
|
// /issue/create?projectKey=projectKey&title=Testing&description=Description
|
||||||
export default async function issueCreate(req: BunRequest) {
|
export default async function issueCreate(req: BunRequest) {
|
||||||
const url = new URL(req.url);
|
const url = new URL(req.url);
|
||||||
const projectId = url.searchParams.get("projectId");
|
const projectId = url.searchParams.get("projectId");
|
||||||
const projectBlob = url.searchParams.get("projectBlob");
|
const projectKey = url.searchParams.get("projectKey");
|
||||||
|
|
||||||
let project = null;
|
let project = null;
|
||||||
if (projectId) {
|
if (projectId) {
|
||||||
project = await getProjectByID(Number(projectId));
|
project = await getProjectByID(Number(projectId));
|
||||||
} else if (projectBlob) {
|
} else if (projectKey) {
|
||||||
project = await getProjectByBlob(projectBlob);
|
project = await getProjectByKey(projectKey);
|
||||||
} else {
|
} else {
|
||||||
return new Response("missing project blob or project id", { status: 400 });
|
return new Response("missing project key or project id", { status: 400 });
|
||||||
}
|
}
|
||||||
if (!project) {
|
if (!project) {
|
||||||
return new Response(`project not found: provided ${projectId ?? projectBlob}`, { status: 404 });
|
return new Response(`project not found: provided ${projectId ?? projectKey}`, { status: 404 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const title = url.searchParams.get("title") || "Untitled Issue";
|
const title = url.searchParams.get("title") || "Untitled Issue";
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import type { BunRequest } from "bun";
|
import type { BunRequest } from "bun";
|
||||||
import { getIssuesWithAssigneeByProject, getProjectByBlob } from "../../db/queries";
|
import { getIssuesWithAssigneeByProject, getProjectByKey } from "../../db/queries";
|
||||||
|
|
||||||
export default async function issuesInProject(req: BunRequest<"/issues/:projectBlob">) {
|
export default async function issuesInProject(req: BunRequest<"/issues/:projectKey">) {
|
||||||
const { projectBlob } = req.params;
|
const { projectKey } = req.params;
|
||||||
|
|
||||||
const project = await getProjectByBlob(projectBlob);
|
const project = await getProjectByKey(projectKey);
|
||||||
if (!project) {
|
if (!project) {
|
||||||
return new Response(`project not found: provided ${projectBlob}`, { status: 404 });
|
return new Response(`project not found: provided ${projectKey}`, { status: 404 });
|
||||||
}
|
}
|
||||||
const issues = await getIssuesWithAssigneeByProject(project.id);
|
const issues = await getIssuesWithAssigneeByProject(project.id);
|
||||||
|
|
||||||
@@ -1,25 +1,25 @@
|
|||||||
import type { BunRequest } from "bun";
|
import type { BunRequest } from "bun";
|
||||||
import { createProject, getProjectByBlob, getUserById } from "../../db/queries";
|
import { createProject, getProjectByKey, getUserById } from "../../db/queries";
|
||||||
|
|
||||||
// /project/create?blob=BLOB&name=Testing&creatorId=1&organisationId=1
|
// /project/create?key=KEY&name=Testing&creatorId=1&organisationId=1
|
||||||
export default async function projectCreate(req: BunRequest) {
|
export default async function projectCreate(req: BunRequest) {
|
||||||
const url = new URL(req.url);
|
const url = new URL(req.url);
|
||||||
const blob = url.searchParams.get("blob");
|
const key = url.searchParams.get("key");
|
||||||
const name = url.searchParams.get("name");
|
const name = url.searchParams.get("name");
|
||||||
const creatorId = url.searchParams.get("creatorId");
|
const creatorId = url.searchParams.get("creatorId");
|
||||||
const organisationId = url.searchParams.get("organisationId");
|
const organisationId = url.searchParams.get("organisationId");
|
||||||
|
|
||||||
if (!blob || !name || !creatorId || !organisationId) {
|
if (!key || !name || !creatorId || !organisationId) {
|
||||||
return new Response(
|
return new Response(
|
||||||
`missing parameters: ${!blob ? "blob " : ""}${!name ? "name " : ""}${!creatorId ? "creatorId " : ""}${!organisationId ? "organisationId" : ""}`,
|
`missing parameters: ${!key ? "key " : ""}${!name ? "name " : ""}${!creatorId ? "creatorId " : ""}${!organisationId ? "organisationId" : ""}`,
|
||||||
{ status: 400 },
|
{ status: 400 },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if project with blob already exists in the organisation
|
// check if project with key already exists in the organisation
|
||||||
const existingProject = await getProjectByBlob(blob);
|
const existingProject = await getProjectByKey(key);
|
||||||
if (existingProject?.organisationId === parseInt(organisationId, 10)) {
|
if (existingProject?.organisationId === parseInt(organisationId, 10)) {
|
||||||
return new Response(`project with blob ${blob} already exists`, { status: 400 });
|
return new Response(`project with key ${key} already exists`, { status: 400 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const creator = await getUserById(parseInt(creatorId, 10));
|
const creator = await getUserById(parseInt(creatorId, 10));
|
||||||
@@ -27,7 +27,7 @@ export default async function projectCreate(req: BunRequest) {
|
|||||||
return new Response(`creator with id ${creatorId} not found`, { status: 404 });
|
return new Response(`creator with id ${creatorId} not found`, { status: 404 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const project = await createProject(blob, name, creator.id, parseInt(organisationId, 10));
|
const project = await createProject(key, name, creator.id, parseInt(organisationId, 10));
|
||||||
|
|
||||||
return Response.json(project);
|
return Response.json(project);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import type { BunRequest } from "bun";
|
import type { BunRequest } from "bun";
|
||||||
import { getProjectByBlob, getProjectByID, getUserById, updateProject } from "../../db/queries";
|
import { getProjectByID, getProjectByKey, getUserById, updateProject } from "../../db/queries";
|
||||||
|
|
||||||
// /project/update?id=1&blob=NEW&name=new%20name&creatorId=1&organisationId=1
|
// /project/update?id=1&key=NEW&name=new%20name&creatorId=1&organisationId=1
|
||||||
export default async function projectUpdate(req: BunRequest) {
|
export default async function projectUpdate(req: BunRequest) {
|
||||||
const url = new URL(req.url);
|
const url = new URL(req.url);
|
||||||
const id = url.searchParams.get("id");
|
const id = url.searchParams.get("id");
|
||||||
const blob = url.searchParams.get("blob") || undefined;
|
const key = url.searchParams.get("key") || undefined;
|
||||||
const name = url.searchParams.get("name") || undefined;
|
const name = url.searchParams.get("name") || undefined;
|
||||||
const creatorId = url.searchParams.get("creatorId") || undefined;
|
const creatorId = url.searchParams.get("creatorId") || undefined;
|
||||||
const organisationId = url.searchParams.get("organisationId") || undefined;
|
const organisationId = url.searchParams.get("organisationId") || undefined;
|
||||||
@@ -19,15 +19,15 @@ export default async function projectUpdate(req: BunRequest) {
|
|||||||
return new Response(`project with id ${id} does not exist`, { status: 404 });
|
return new Response(`project with id ${id} does not exist`, { status: 404 });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!blob && !name && !creatorId && !organisationId) {
|
if (!key && !name && !creatorId && !organisationId) {
|
||||||
return new Response(`at least one of blob, name, creatorId, or organisationId must be provided`, {
|
return new Response(`at least one of key, name, creatorId, or organisationId must be provided`, {
|
||||||
status: 400,
|
status: 400,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const projectWithBlob = blob ? await getProjectByBlob(blob) : null;
|
const projectWithKey = key ? await getProjectByKey(key) : null;
|
||||||
if (projectWithBlob && projectWithBlob.id !== Number(id)) {
|
if (projectWithKey && projectWithKey.id !== Number(id)) {
|
||||||
return new Response(`a project with blob "${blob}" already exists`, { status: 400 });
|
return new Response(`a project with key "${key}" already exists`, { status: 400 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const newCreator = creatorId ? await getUserById(Number(creatorId)) : null;
|
const newCreator = creatorId ? await getUserById(Number(creatorId)) : null;
|
||||||
@@ -36,8 +36,8 @@ export default async function projectUpdate(req: BunRequest) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const project = await updateProject(Number(id), {
|
const project = await updateProject(Number(id), {
|
||||||
blob: blob,
|
key,
|
||||||
name: name,
|
name,
|
||||||
creatorId: newCreator?.id,
|
creatorId: newCreator?.id,
|
||||||
organisationId: organisationId ? Number(organisationId) : undefined,
|
organisationId: organisationId ? Number(organisationId) : undefined,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ function Index() {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!selectedProject) return;
|
if (!selectedProject) return;
|
||||||
|
|
||||||
fetch(`${serverURL}/issues/${selectedProject.Project.blob}`, { headers: getAuthHeaders() })
|
fetch(`${serverURL}/issues/${selectedProject.Project.key}`, { headers: getAuthHeaders() })
|
||||||
.then((res) => res.json())
|
.then((res) => res.json())
|
||||||
.then((data: IssueResponse[]) => {
|
.then((data: IssueResponse[]) => {
|
||||||
setIssues(data);
|
setIssues(data);
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import { Input } from "@/components/ui/input";
|
|||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { cn, getAuthHeaders } from "@/lib/utils";
|
import { cn, getAuthHeaders } from "@/lib/utils";
|
||||||
|
|
||||||
const blobify = (value: string) =>
|
const keyify = (value: string) =>
|
||||||
value
|
value
|
||||||
.toUpperCase()
|
.toUpperCase()
|
||||||
.replace(/[^A-Z0-9]/g, "")
|
.replace(/[^A-Z0-9]/g, "")
|
||||||
@@ -33,11 +33,11 @@ export function CreateProject({
|
|||||||
|
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [name, setName] = useState("");
|
const [name, setName] = useState("");
|
||||||
const [blob, setBlob] = useState("");
|
const [key, setKey] = useState("");
|
||||||
|
|
||||||
const [nameTouched, setNameTouched] = useState(false);
|
const [nameTouched, setNameTouched] = useState(false);
|
||||||
const [blobTouched, setBlobTouched] = useState(false);
|
const [keyTouched, setKeyTouched] = useState(false);
|
||||||
const [blobManuallyEdited, setBlobManuallyEdited] = useState(false);
|
const [keyManuallyEdited, setKeyManuallyEdited] = useState(false);
|
||||||
const [submitAttempted, setSubmitAttempted] = useState(false);
|
const [submitAttempted, setSubmitAttempted] = useState(false);
|
||||||
|
|
||||||
const [submitting, setSubmitting] = useState(false);
|
const [submitting, setSubmitting] = useState(false);
|
||||||
@@ -48,20 +48,20 @@ export function CreateProject({
|
|||||||
[nameTouched, submitAttempted, name],
|
[nameTouched, submitAttempted, name],
|
||||||
);
|
);
|
||||||
|
|
||||||
const blobInvalid = useMemo(() => {
|
const keyInvalid = useMemo(() => {
|
||||||
if (!(blobTouched || submitAttempted)) return "";
|
if (!(keyTouched || submitAttempted)) return "";
|
||||||
if (blob.trim() === "") return "Cannot be empty";
|
if (key.trim() === "") return "Cannot be empty";
|
||||||
if (blob.length > 4) return "Must be 4 or less characters";
|
if (key.length > 4) return "Must be 4 or less characters";
|
||||||
return "";
|
return "";
|
||||||
}, [blobTouched, submitAttempted, blob]);
|
}, [keyTouched, submitAttempted, key]);
|
||||||
|
|
||||||
const reset = () => {
|
const reset = () => {
|
||||||
setName("");
|
setName("");
|
||||||
setBlob("");
|
setKey("");
|
||||||
|
|
||||||
setNameTouched(false);
|
setNameTouched(false);
|
||||||
setBlobTouched(false);
|
setKeyTouched(false);
|
||||||
setBlobManuallyEdited(false);
|
setKeyManuallyEdited(false);
|
||||||
setSubmitAttempted(false);
|
setSubmitAttempted(false);
|
||||||
|
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
@@ -80,7 +80,7 @@ export function CreateProject({
|
|||||||
setError(null);
|
setError(null);
|
||||||
setSubmitAttempted(true);
|
setSubmitAttempted(true);
|
||||||
|
|
||||||
if (name.trim() === "" || blob.length > 4) {
|
if (name.trim() === "" || key.length > 4) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,7 +97,7 @@ export function CreateProject({
|
|||||||
setSubmitting(true);
|
setSubmitting(true);
|
||||||
try {
|
try {
|
||||||
const url = new URL(`${serverURL}/project/create`);
|
const url = new URL(`${serverURL}/project/create`);
|
||||||
url.searchParams.set("blob", blob);
|
url.searchParams.set("key", key);
|
||||||
url.searchParams.set("name", name.trim());
|
url.searchParams.set("name", name.trim());
|
||||||
url.searchParams.set("creatorId", `${userId}`);
|
url.searchParams.set("creatorId", `${userId}`);
|
||||||
url.searchParams.set("organisationId", `${organisationId}`);
|
url.searchParams.set("organisationId", `${organisationId}`);
|
||||||
@@ -161,8 +161,8 @@ export function CreateProject({
|
|||||||
const nextName = e.target.value;
|
const nextName = e.target.value;
|
||||||
setName(nextName);
|
setName(nextName);
|
||||||
|
|
||||||
if (!blobManuallyEdited) {
|
if (!keyManuallyEdited) {
|
||||||
setBlob(blobify(nextName));
|
setKey(keyify(nextName));
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
onBlur={() => setNameTouched(true)}
|
onBlur={() => setNameTouched(true)}
|
||||||
@@ -180,23 +180,23 @@ export function CreateProject({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid gap-2">
|
<div className="grid gap-2">
|
||||||
<Label htmlFor="project-blob">Blob</Label>
|
<Label htmlFor="project-key">Key</Label>
|
||||||
<Input
|
<Input
|
||||||
id="project-blob"
|
id="project-key"
|
||||||
name="blob"
|
name="key"
|
||||||
value={blob}
|
value={key}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setBlob(blobify(e.target.value));
|
setKey(keyify(e.target.value));
|
||||||
setBlobManuallyEdited(true);
|
setKeyManuallyEdited(true);
|
||||||
}}
|
}}
|
||||||
onBlur={() => setBlobTouched(true)}
|
onBlur={() => setKeyTouched(true)}
|
||||||
aria-invalid={blobInvalid !== ""}
|
aria-invalid={keyInvalid !== ""}
|
||||||
placeholder="DEMO"
|
placeholder="DEMO"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
<div className="flex items-end justify-end w-full text-xs -mb-4 -mt-2">
|
<div className="flex items-end justify-end w-full text-xs -mb-4 -mt-2">
|
||||||
{blobInvalid !== "" ? (
|
{keyInvalid !== "" ? (
|
||||||
<Label className="text-destructive text-sm">{blobInvalid}</Label>
|
<Label className="text-destructive text-sm">{keyInvalid}</Label>
|
||||||
) : (
|
) : (
|
||||||
<Label className="opacity-0 text-sm">a</Label>
|
<Label className="opacity-0 text-sm">a</Label>
|
||||||
)}
|
)}
|
||||||
@@ -219,7 +219,7 @@ export function CreateProject({
|
|||||||
</DialogClose>
|
</DialogClose>
|
||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={submitting || nameInvalid !== "" || blobInvalid !== ""}
|
disabled={submitting || nameInvalid !== "" || keyInvalid !== ""}
|
||||||
>
|
>
|
||||||
{submitting ? "Creating..." : "Create"}
|
{submitting ? "Creating..." : "Create"}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export function IssueDetailPane({
|
|||||||
<div className="flex flex-row items-center justify-end border-b h-[25px]">
|
<div className="flex flex-row items-center justify-end border-b h-[25px]">
|
||||||
<span className="w-full">
|
<span className="w-full">
|
||||||
<p className="text-sm w-fit px-1">
|
<p className="text-sm w-fit px-1">
|
||||||
{issueID(project.Project.blob, issueData.Issue.number)}
|
{issueID(project.Project.key, issueData.Issue.number)}
|
||||||
</p>
|
</p>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ export function cn(...inputs: ClassValue[]) {
|
|||||||
return twMerge(clsx(inputs));
|
return twMerge(clsx(inputs));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function issueID(blob: string, num: number) {
|
export function issueID(key: string, num: number) {
|
||||||
return `${blob}-${num.toString().padStart(3, "0")}`;
|
return `${key}-${num.toString().padStart(3, "0")}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getAuthHeaders(): HeadersInit {
|
export function getAuthHeaders(): HeadersInit {
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ export const OrganisationMember = pgTable("OrganisationMember", {
|
|||||||
|
|
||||||
export const Project = pgTable("Project", {
|
export const Project = pgTable("Project", {
|
||||||
id: integer().primaryKey().generatedAlwaysAsIdentity(),
|
id: integer().primaryKey().generatedAlwaysAsIdentity(),
|
||||||
blob: varchar({ length: 4 }).notNull(),
|
key: varchar({ length: 4 }).notNull(),
|
||||||
name: varchar({ length: 256 }).notNull(),
|
name: varchar({ length: 256 }).notNull(),
|
||||||
organisationId: integer()
|
organisationId: integer()
|
||||||
.notNull()
|
.notNull()
|
||||||
|
|||||||
Reference in New Issue
Block a user