feat: implement psql and improvements

This commit is contained in:
Levente Orban
2025-08-27 08:47:12 +02:00
parent 36dc19933a
commit 3d133a6539
20 changed files with 1379 additions and 283 deletions

View File

@@ -0,0 +1,97 @@
import {
pgTable,
varchar,
integer,
date,
time,
timestamp,
uuid,
pgEnum,
index,
uniqueIndex,
check
} from 'drizzle-orm/pg-core';
import { sql } from 'drizzle-orm';
import type { InferInsertModel, InferSelectModel } from 'drizzle-orm';
// --- Enums (matching the SQL CHECK constraints)
export const eventTypeEnum = pgEnum('event_type', ['limited', 'unlimited']);
export const visibilityEnum = pgEnum('visibility', ['public', 'private']);
// --- Events table
export const events = pgTable(
'events',
{
id: varchar('id', { length: 8 }).primaryKey(),
name: varchar('name', { length: 100 }).notNull(),
date: date('date', { mode: 'string' }).notNull(), // ISO 'YYYY-MM-DD'
time: time('time', { withTimezone: false }).notNull(), // 'HH:MM:SS'
location: varchar('location', { length: 200 }).notNull(),
type: eventTypeEnum('type').notNull(),
attendeeLimit: integer('attendee_limit'), // nullable in SQL
userId: varchar('user_id', { length: 100 }).notNull(),
visibility: visibilityEnum('visibility').notNull().default('public'),
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow(),
updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow()
},
(t) => ({
// Primary key already indexes id
idxEventsId: index('idx_events_id').on(t.id),
idxEventsUserId: index('idx_events_user_id').on(t.userId),
idxEventsVisibility: index('idx_events_visibility').on(t.visibility),
// CHECK (attendee_limit > 0) like in SQL (still allows NULL)
attendeeLimitPositive: check(
'events_attendee_limit_positive',
sql`${t.attendeeLimit} IS NULL OR ${t.attendeeLimit} > 0`
)
})
);
// --- RSVPs table
export const rsvps = pgTable(
'rsvps',
{
id: uuid('id').defaultRandom().primaryKey(), // gen_random_uuid()
eventId: varchar('event_id', { length: 8 })
.notNull()
.references(() => events.id, { onDelete: 'cascade' }),
name: varchar('name', { length: 50 }).notNull(),
userId: varchar('user_id', { length: 100 }).notNull(),
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow()
},
(t) => ({
idxRsvpsEventId: index('idx_rsvps_event_id').on(t.eventId),
idxRsvpsUserId: index('idx_rsvps_user_id').on(t.userId),
idxRsvpsCreatedAt: index('idx_rsvps_created_at').on(t.createdAt),
// UNIQUE(event_id, name) constraint
uqEventName: uniqueIndex('rsvps_event_id_name_unique').on(t.eventId, t.name)
})
);
// --- Relations (optional but handy for type safety)
import { relations } from 'drizzle-orm';
export const eventsRelations = relations(events, ({ many }) => ({
rsvps: many(rsvps)
}));
export const rsvpsRelations = relations(rsvps, ({ one }) => ({
event: one(events, {
fields: [rsvps.eventId],
references: [events.id]
})
}));
// --- Inferred types for use in the application
export type Event = InferSelectModel<typeof events>;
export type NewEvent = InferInsertModel<typeof events>;
export type Rsvp = InferSelectModel<typeof rsvps>;
export type NewRsvp = InferInsertModel<typeof rsvps>;
// --- Additional utility types
export type EventWithRsvps = Event & {
rsvps: Rsvp[];
};
export type CreateEventData = Omit<NewEvent, 'id' | 'createdAt' | 'updatedAt'>;
export type CreateRsvpData = Omit<NewRsvp, 'id' | 'createdAt'>;