mirror of
https://github.com/hex248/sprint.git
synced 2026-02-08 18:33:01 +00:00
Add 'packages/backend/' from commit 'acce648ee5e7e3a3006451e637c0db654820cc48'
git-subtree-dir: packages/backend git-subtree-mainline:d0babd62afgit-subtree-split:acce648ee5
This commit is contained in:
22
packages/backend/src/db/client.ts
Normal file
22
packages/backend/src/db/client.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import "dotenv/config";
|
||||
import { drizzle } from "drizzle-orm/node-postgres";
|
||||
|
||||
if (!process.env.DATABASE_URL) {
|
||||
throw new Error("DATABASE_URL is not set in environment variables");
|
||||
}
|
||||
|
||||
export const db = drizzle({
|
||||
connection: {
|
||||
connectionString: process.env.DATABASE_URL,
|
||||
},
|
||||
});
|
||||
|
||||
export const testDB = async () => {
|
||||
try {
|
||||
await db.execute("SELECT 1;");
|
||||
console.log("db connected");
|
||||
} catch (err) {
|
||||
console.log("db down");
|
||||
process.exit();
|
||||
}
|
||||
};
|
||||
3
packages/backend/src/db/queries/index.ts
Normal file
3
packages/backend/src/db/queries/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from "./users";
|
||||
export * from "./projects";
|
||||
export * from "./issues";
|
||||
59
packages/backend/src/db/queries/issues.ts
Normal file
59
packages/backend/src/db/queries/issues.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { eq, sql, and } from "drizzle-orm";
|
||||
import { db } from "../client";
|
||||
import { Issue } from "../schema";
|
||||
|
||||
export async function createIssue(projectId: number, title: string, description: string) {
|
||||
// prevents two issues with the same unique number
|
||||
return await db.transaction(async (tx) => {
|
||||
// raw sql for speed
|
||||
// most recent issue from project
|
||||
const [lastIssue] = await tx
|
||||
.select({ max: sql<number>`MAX(${Issue.number})` })
|
||||
.from(Issue)
|
||||
.where(eq(Issue.projectId, projectId));
|
||||
|
||||
const nextNumber = (lastIssue?.max || 0) + 1;
|
||||
|
||||
// 2. create new issue
|
||||
const [newIssue] = await tx
|
||||
.insert(Issue)
|
||||
.values({
|
||||
projectId,
|
||||
title,
|
||||
description,
|
||||
number: nextNumber,
|
||||
})
|
||||
.returning();
|
||||
|
||||
return newIssue;
|
||||
});
|
||||
}
|
||||
|
||||
export async function deleteIssue(id: number) {
|
||||
return await db.delete(Issue).where(eq(Issue.id, id));
|
||||
}
|
||||
|
||||
export async function updateIssue(id: number, updates: { title?: string; description?: string }) {
|
||||
return await db.update(Issue).set(updates).where(eq(Issue.id, id)).returning();
|
||||
}
|
||||
|
||||
export async function getIssues() {
|
||||
return await db.select().from(Issue);
|
||||
}
|
||||
|
||||
export async function getIssuesByProject(projectId: number) {
|
||||
return await db.select().from(Issue).where(eq(Issue.projectId, projectId));
|
||||
}
|
||||
|
||||
export async function getIssueByID(id: number) {
|
||||
const [issue] = await db.select().from(Issue).where(eq(Issue.id, id));
|
||||
return issue;
|
||||
}
|
||||
|
||||
export async function getIssueByNumber(projectId: number, number: number) {
|
||||
const [issue] = await db
|
||||
.select()
|
||||
.from(Issue)
|
||||
.where(and(eq(Issue.projectId, projectId), eq(Issue.number, number)));
|
||||
return issue;
|
||||
}
|
||||
40
packages/backend/src/db/queries/projects.ts
Normal file
40
packages/backend/src/db/queries/projects.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import { eq } from "drizzle-orm";
|
||||
import { db } from "../client";
|
||||
import { Issue, Project, User } from "../schema";
|
||||
|
||||
export async function createProject(blob: string, name: string, ownerId: number) {
|
||||
const [project] = await db
|
||||
.insert(Project)
|
||||
.values({
|
||||
blob,
|
||||
name,
|
||||
ownerId,
|
||||
})
|
||||
.returning();
|
||||
return project;
|
||||
}
|
||||
|
||||
export async function updateProject(
|
||||
projectId: number,
|
||||
updates: { blob?: string; name?: string; ownerId?: number },
|
||||
) {
|
||||
const [project] = await db.update(Project).set(updates).where(eq(Project.id, projectId)).returning();
|
||||
return project;
|
||||
}
|
||||
|
||||
export async function deleteProject(projectId: number) {
|
||||
// delete all of the project's issues first
|
||||
await db.delete(Issue).where(eq(Issue.projectId, projectId));
|
||||
// delete actual project
|
||||
await db.delete(Project).where(eq(Project.id, projectId));
|
||||
}
|
||||
|
||||
export async function getProjectByID(projectId: number) {
|
||||
const [project] = await db.select().from(Project).where(eq(Project.id, projectId));
|
||||
return project;
|
||||
}
|
||||
|
||||
export async function getProjectByBlob(projectBlob: string) {
|
||||
const [project] = await db.select().from(Project).where(eq(Project.blob, projectBlob));
|
||||
return project;
|
||||
}
|
||||
18
packages/backend/src/db/queries/users.ts
Normal file
18
packages/backend/src/db/queries/users.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { eq } from "drizzle-orm";
|
||||
import { db } from "../client";
|
||||
import { User } from "../schema";
|
||||
|
||||
export async function createUser(name: string, username: string) {
|
||||
const [user] = await db.insert(User).values({ name, username }).returning();
|
||||
return user;
|
||||
}
|
||||
|
||||
export async function getUserById(id: number) {
|
||||
const [user] = await db.select().from(User).where(eq(User.id, id));
|
||||
return user;
|
||||
}
|
||||
|
||||
export async function getUserByUsername(username: string) {
|
||||
const [user] = await db.select().from(User).where(eq(User.username, username));
|
||||
return user;
|
||||
}
|
||||
36
packages/backend/src/db/schema.ts
Normal file
36
packages/backend/src/db/schema.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { integer, pgTable, uniqueIndex, varchar } from "drizzle-orm/pg-core";
|
||||
|
||||
export const User = pgTable("User", {
|
||||
id: integer().primaryKey().generatedAlwaysAsIdentity(),
|
||||
name: varchar({ length: 256 }).notNull(),
|
||||
username: varchar({ length: 32 }).notNull().unique(),
|
||||
});
|
||||
|
||||
export const Project = pgTable("Project", {
|
||||
id: integer().primaryKey().generatedAlwaysAsIdentity(),
|
||||
blob: varchar({ length: 4 }).notNull(),
|
||||
name: varchar({ length: 256 }).notNull(),
|
||||
ownerId: integer()
|
||||
.notNull()
|
||||
.references(() => User.id),
|
||||
});
|
||||
|
||||
export const Issue = pgTable(
|
||||
"Issue",
|
||||
{
|
||||
id: integer().primaryKey().generatedAlwaysAsIdentity(),
|
||||
projectId: integer()
|
||||
.notNull()
|
||||
.references(() => Project.id),
|
||||
|
||||
number: integer("number").notNull(),
|
||||
|
||||
title: varchar({ length: 256 }).notNull(),
|
||||
description: varchar({ length: 2048 }).notNull(),
|
||||
},
|
||||
(t) => [
|
||||
// ensures unique numbers per project
|
||||
// you can have Issue 1 in PROJ and Issue 1 in TEST, but not two Issue 1s in PROJ
|
||||
uniqueIndex("unique_project_issue_number").on(t.projectId, t.number),
|
||||
],
|
||||
);
|
||||
Reference in New Issue
Block a user