From 6de73a24ec367cadef8f2a96ac7a7cee25b337d2 Mon Sep 17 00:00:00 2001 From: alexfriesen Date: Wed, 25 Dec 2024 21:35:10 +0100 Subject: [PATCH] feat(database): add db schema --- apps/timer/server/database/common.ts | 10 ++++ .../server/database/organisation-member.ts | 24 ++++++++++ apps/timer/server/database/organisation.ts | 16 +++++++ apps/timer/server/database/project.ts | 27 +++++++++++ apps/timer/server/database/schema.ts | 8 ++++ apps/timer/server/database/session.ts | 21 ++++++++ apps/timer/server/database/time.ts | 48 +++++++++++++++++++ apps/timer/server/database/user.ts | 31 ++++++++++++ 8 files changed, 185 insertions(+) create mode 100644 apps/timer/server/database/common.ts create mode 100644 apps/timer/server/database/organisation-member.ts create mode 100644 apps/timer/server/database/organisation.ts create mode 100644 apps/timer/server/database/project.ts create mode 100644 apps/timer/server/database/schema.ts create mode 100644 apps/timer/server/database/session.ts create mode 100644 apps/timer/server/database/time.ts create mode 100644 apps/timer/server/database/user.ts diff --git a/apps/timer/server/database/common.ts b/apps/timer/server/database/common.ts new file mode 100644 index 0000000..a690837 --- /dev/null +++ b/apps/timer/server/database/common.ts @@ -0,0 +1,10 @@ +import { timestamp } from 'drizzle-orm/pg-core'; + +export const timestampColumns = () => ({ + createdAt: timestamp('created_at', { mode: 'date', withTimezone: true }) + .notNull() + .defaultNow(), + updatedAt: timestamp('updated_at', { mode: 'date', withTimezone: true }) + .notNull() + .$onUpdate(() => new Date()), +}); diff --git a/apps/timer/server/database/organisation-member.ts b/apps/timer/server/database/organisation-member.ts new file mode 100644 index 0000000..22564b0 --- /dev/null +++ b/apps/timer/server/database/organisation-member.ts @@ -0,0 +1,24 @@ +import { pgTable, primaryKey, uuid, varchar } from 'drizzle-orm/pg-core'; +import { timestampColumns } from './common'; +import { organisations } from './organisation'; +import { users } from './user'; + +export const organisationMembers = pgTable( + 'organisationMember', + { + userId: uuid('user_id') + .notNull() + .references(() => users.id, { onDelete: 'cascade' }), + organisationId: uuid('organisation_id') + .notNull() + .references(() => organisations.id, { onDelete: 'cascade' }), + + role: varchar('role', { length: 10 }).notNull(), + + ...timestampColumns(), + }, + (table) => [primaryKey({ columns: [table.userId, table.organisationId] })] +); + +export type OrganisationMember = typeof organisationMembers.$inferSelect; // return type when queried +export type NewOrganisationMember = typeof organisationMembers.$inferInsert; // insert type diff --git a/apps/timer/server/database/organisation.ts b/apps/timer/server/database/organisation.ts new file mode 100644 index 0000000..095e990 --- /dev/null +++ b/apps/timer/server/database/organisation.ts @@ -0,0 +1,16 @@ +import { index, pgTable, uuid, varchar } from 'drizzle-orm/pg-core'; +import { timestampColumns } from './common'; + +export const organisations = pgTable( + 'organisation', + { + id: uuid('id').defaultRandom().notNull().primaryKey(), + name: varchar('name', { length: 150 }).notNull(), + + ...timestampColumns(), + }, + (table) => [index().on(table.name)] +); + +export type Organisation = typeof organisations.$inferSelect; // return type when queried +export type NewOrganisation = typeof organisations.$inferInsert; // insert type diff --git a/apps/timer/server/database/project.ts b/apps/timer/server/database/project.ts new file mode 100644 index 0000000..964bc4c --- /dev/null +++ b/apps/timer/server/database/project.ts @@ -0,0 +1,27 @@ +import { index, pgTable, text, uuid, varchar } from 'drizzle-orm/pg-core'; +import { timestampColumns } from './common'; +import { users } from './user'; +import { organisations } from './organisation'; + +export const projects = pgTable( + 'project', + { + id: uuid('id').defaultRandom().notNull().primaryKey(), + + name: varchar('name', { length: 150 }).notNull(), + notes: text('notes').notNull().default(''), + + organisationId: uuid('organisation_id') + .notNull() + .references(() => organisations.id, { onDelete: 'cascade' }), + userId: uuid('user_id') + .notNull() + .references(() => users.id, { onDelete: 'cascade' }), + + ...timestampColumns(), + }, + (table) => [index('name_idx').on(table.name)] +); + +export type Project = typeof projects.$inferSelect; // return type when queried +export type NewProject = typeof projects.$inferInsert; // insert type diff --git a/apps/timer/server/database/schema.ts b/apps/timer/server/database/schema.ts new file mode 100644 index 0000000..71001ae --- /dev/null +++ b/apps/timer/server/database/schema.ts @@ -0,0 +1,8 @@ +export { sessions } from './session'; +export { users } from './user'; + +export { times } from './time'; +export { projects } from './project'; + +export { organisations } from './organisation'; +export { organisationMembers } from './organisation-member'; diff --git a/apps/timer/server/database/session.ts b/apps/timer/server/database/session.ts new file mode 100644 index 0000000..71dceac --- /dev/null +++ b/apps/timer/server/database/session.ts @@ -0,0 +1,21 @@ +import { pgTable, timestamp, uuid, varchar } from 'drizzle-orm/pg-core'; +import { timestampColumns } from './common'; +import { users } from './user'; + +export const sessions = pgTable('session', { + id: varchar('id').primaryKey(), + expiresAt: timestamp('expires_at', { + withTimezone: true, + mode: 'date', + }).notNull(), + browser: varchar('browser', { length: 150 }), + + userId: uuid('user_id') + .notNull() + .references(() => users.id, { onDelete: 'cascade' }), + + createdAt: timestampColumns().createdAt, +}); + +export type Session = typeof sessions.$inferSelect; // return type when queried +export type NewSession = typeof sessions.$inferInsert; // insert type diff --git a/apps/timer/server/database/time.ts b/apps/timer/server/database/time.ts new file mode 100644 index 0000000..5571c8f --- /dev/null +++ b/apps/timer/server/database/time.ts @@ -0,0 +1,48 @@ +import { + index, + integer, + pgTable, + serial, + text, + timestamp, + uuid, +} from 'drizzle-orm/pg-core'; +import { timestampColumns } from './common'; +import { projects } from './project'; +import { users } from './user'; +import { organisations } from './organisation'; + +export const times = pgTable( + 'time', + { + id: serial('id').primaryKey(), + + start: timestamp('start', { + withTimezone: true, + mode: 'date', + }).notNull(), + end: timestamp('end', { + withTimezone: true, + mode: 'date', + }).notNull(), + duration: integer('duration').notNull(), + notes: text('notes').notNull().default(''), + + projectId: uuid('project_id').references(() => projects.id, { + onDelete: 'cascade', + }), + + organisationId: uuid('organisation_id') + .notNull() + .references(() => organisations.id, { onDelete: 'cascade' }), + userId: uuid('user_id') + .notNull() + .references(() => users.id, { onDelete: 'cascade' }), + + ...timestampColumns(), + }, + (table) => [index().on(table.start), index().on(table.end)] +); + +export type Time = typeof times.$inferSelect; // return type when queried +export type NewTime = typeof times.$inferInsert; // insert type diff --git a/apps/timer/server/database/user.ts b/apps/timer/server/database/user.ts new file mode 100644 index 0000000..722a870 --- /dev/null +++ b/apps/timer/server/database/user.ts @@ -0,0 +1,31 @@ +import { + text, + pgTable, + uuid, + varchar, + boolean, + uniqueIndex, +} from 'drizzle-orm/pg-core'; +import { timestampColumns } from './common'; + +export const users = pgTable( + 'user', + { + id: uuid('id').defaultRandom().notNull().primaryKey(), + + name: varchar('name', { length: 150 }).notNull(), + description: text('description').notNull().default(''), + + email: text('email').notNull().unique(), + emailVerified: boolean('email_verified').notNull().default(false), + + passwordHash: text('password_hash').notNull(), + passwordSalt: text('password_salt').notNull(), + + ...timestampColumns(), + }, + (table) => [uniqueIndex().on(table.email)] +); + +export type User = typeof users.$inferSelect; +export type NewUser = typeof users.$inferInsert;