Files
Final-Year-Project/Backend/db/schema.ts

204 lines
10 KiB
TypeScript

import { pgTable, timestamp, uuid, varchar, text, boolean, integer, jsonb, unique } from 'drizzle-orm/pg-core';
export const users = pgTable('users', {
id: uuid('id').defaultRandom().primaryKey(),
email: varchar('email', { length: 255 }).notNull().unique(),
name: varchar('name', { length: 255 }).notNull(),
passwordHash: varchar('password_hash', { length: 255 }).notNull(),
emailVerified: boolean('email_verified').default(false).notNull(),
image: text('image'),
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow().notNull(),
});
export const devices = pgTable('devices', {
id: uuid('id').defaultRandom().primaryKey(),
userId: uuid('user_id').notNull().references(() => users.id),
name: varchar('name', { length: 255 }),
role: varchar('role', { length: 32 }).default('client').notNull(),
platform: varchar('platform', { length: 32 }),
appVersion: varchar('app_version', { length: 64 }),
pushToken: text('push_token'),
status: varchar('status', { length: 32 }).default('offline').notNull(),
isCamera: boolean('is_camera').default(false).notNull(),
lastSeenAt: timestamp('last_seen_at', { withTimezone: true }),
updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow().notNull(),
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
});
export const deviceLinks = pgTable(
'device_links',
{
id: uuid('id').defaultRandom().primaryKey(),
ownerUserId: uuid('owner_user_id').notNull().references(() => users.id),
cameraDeviceId: uuid('camera_device_id').notNull().references(() => devices.id),
clientDeviceId: uuid('client_device_id').notNull().references(() => devices.id),
status: varchar('status', { length: 32 }).default('active').notNull(),
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow().notNull(),
},
(table) => ({
uniqueDevicePair: unique('device_links_camera_client_unique').on(table.cameraDeviceId, table.clientDeviceId),
}),
);
export const deviceCommands = pgTable('device_commands', {
id: uuid('id').defaultRandom().primaryKey(),
ownerUserId: uuid('owner_user_id').notNull().references(() => users.id),
sourceDeviceId: uuid('source_device_id').notNull().references(() => devices.id),
targetDeviceId: uuid('target_device_id').notNull().references(() => devices.id),
commandType: varchar('command_type', { length: 64 }).notNull(),
payload: jsonb('payload').$type<Record<string, unknown> | null>().default(null),
status: varchar('status', { length: 32 }).default('queued').notNull(),
retryCount: integer('retry_count').default(0).notNull(),
lastDispatchedAt: timestamp('last_dispatched_at', { withTimezone: true }),
acknowledgedAt: timestamp('acknowledged_at', { withTimezone: true }),
error: text('error'),
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow().notNull(),
});
export const streamSessions = pgTable('stream_sessions', {
id: uuid('id').defaultRandom().primaryKey(),
ownerUserId: uuid('owner_user_id').notNull().references(() => users.id),
cameraDeviceId: uuid('camera_device_id').notNull().references(() => devices.id),
requesterDeviceId: uuid('requester_device_id').notNull().references(() => devices.id),
status: varchar('status', { length: 32 }).default('requested').notNull(),
reason: varchar('reason', { length: 32 }).default('on_demand').notNull(),
mediaProvider: varchar('media_provider', { length: 32 }).default('mock').notNull(),
mediaSessionId: varchar('media_session_id', { length: 255 }),
publishEndpoint: text('publish_endpoint'),
subscribeEndpoint: text('subscribe_endpoint'),
streamKey: varchar('stream_key', { length: 255 }),
startedAt: timestamp('started_at', { withTimezone: true }),
endedAt: timestamp('ended_at', { withTimezone: true }),
metadata: jsonb('metadata').$type<Record<string, unknown> | null>().default(null),
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow().notNull(),
});
export const recordings = pgTable('recordings', {
id: uuid('id').defaultRandom().primaryKey(),
ownerUserId: uuid('owner_user_id').notNull().references(() => users.id),
streamSessionId: uuid('stream_session_id').notNull().references(() => streamSessions.id),
cameraDeviceId: uuid('camera_device_id').notNull().references(() => devices.id),
requesterDeviceId: uuid('requester_device_id').notNull().references(() => devices.id),
eventId: uuid('event_id').references(() => events.id),
objectKey: varchar('object_key', { length: 1024 }),
bucket: varchar('bucket', { length: 255 }),
durationSeconds: integer('duration_seconds'),
sizeBytes: integer('size_bytes'),
status: varchar('status', { length: 32 }).default('awaiting_upload').notNull(),
availableAt: timestamp('available_at', { withTimezone: true }),
error: text('error'),
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow().notNull(),
});
export const events = pgTable('events', {
id: uuid('id').defaultRandom().primaryKey(),
userId: uuid('user_id').notNull().references(() => users.id),
deviceId: uuid('device_id').references(() => devices.id),
title: varchar('title', { length: 255 }),
triggeredBy: varchar('triggered_by', { length: 64 }).default('motion'),
status: varchar('status', { length: 32 }).default('recording').notNull(),
startedAt: timestamp('started_at', { withTimezone: true }).notNull(),
endedAt: timestamp('ended_at', { withTimezone: true }),
notifiedAt: timestamp('notified_at', { withTimezone: true }),
videoUrl: varchar('video_url', { length: 1024 }).unique(),
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow().notNull(),
});
export const videos = pgTable('videos', {
id: uuid('id').defaultRandom().primaryKey(),
eventId: uuid('event_id').references(() => events.id),
userId: uuid('user_id').notNull().references(() => users.id),
deviceId: uuid('device_id').notNull().references(() => devices.id),
objectKey: varchar('object_key', { length: 1024 }).notNull().unique(),
bucket: varchar('bucket', { length: 255 }).notNull(),
uploadUrl: text('upload_url').notNull(),
downloadUrl: text('download_url'),
status: varchar('status', { length: 32 }).notNull().default('pending'),
expiresAt: timestamp('expires_at', { withTimezone: true }),
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow().notNull(),
});
export const notifications = pgTable('notifications', {
id: uuid('id').defaultRandom().primaryKey(),
eventId: uuid('event_id').references(() => events.id).notNull(),
userId: uuid('user_id').references(() => users.id).notNull(),
sentAt: timestamp('sent_at', { withTimezone: true }).defaultNow().notNull(),
channel: varchar('channel', { length: 32 }).notNull(),
status: varchar('status', { length: 32 }).default('queued').notNull(),
isRead: boolean('is_read').default(false).notNull(),
});
export const pushNotifications = pgTable('push_notifications', {
id: uuid('id').defaultRandom().primaryKey(),
ownerUserId: uuid('owner_user_id').notNull().references(() => users.id),
recipientDeviceId: uuid('recipient_device_id').notNull().references(() => devices.id),
type: varchar('type', { length: 64 }).notNull(),
payload: jsonb('payload').$type<Record<string, unknown> | null>().default(null),
status: varchar('status', { length: 32 }).default('queued').notNull(),
attempts: integer('attempts').default(0).notNull(),
lastError: text('last_error'),
sentAt: timestamp('sent_at', { withTimezone: true }),
nextAttemptAt: timestamp('next_attempt_at', { withTimezone: true }).defaultNow().notNull(),
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow().notNull(),
});
export const accounts = pgTable('account', {
id: uuid('id').defaultRandom().primaryKey(),
userId: uuid('user_id').notNull().references(() => users.id),
accountId: text('account_id').notNull(),
providerId: text('provider_id').notNull(),
accessToken: text('access_token'),
refreshToken: text('refresh_token'),
accessTokenExpiresAt: timestamp('access_token_expires_at', { withTimezone: true }),
refreshTokenExpiresAt: timestamp('refresh_token_expires_at', { withTimezone: true }),
idToken: text('id_token'),
scope: text('scope'),
password: text('password'),
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow().notNull(),
});
export const sessions = pgTable('session', {
id: uuid('id').defaultRandom().primaryKey(),
userId: uuid('user_id').notNull().references(() => users.id),
token: text('token').notNull().unique(),
expiresAt: timestamp('expires_at', { withTimezone: true }).notNull(),
ipAddress: text('ip_address'),
userAgent: text('user_agent'),
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow().notNull(),
});
export const verifications = pgTable('verification', {
id: uuid('id').defaultRandom().primaryKey(),
identifier: text('identifier').notNull(),
value: text('value').notNull(),
expiresAt: timestamp('expires_at', { withTimezone: true }).notNull(),
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow().notNull(),
});
export const schema = {
users,
devices,
deviceLinks,
deviceCommands,
streamSessions,
recordings,
events,
videos,
notifications,
pushNotifications,
accounts,
sessions,
verifications,
};