FRE-608: Add Turso database setup with Drizzle ORM
- Create schema for users, projects, scripts, characters, scenes, revisions - Implement DatabaseManager with connection pooling - Implement EdgeDatabaseManager for multi-region replicas - Implement DatabaseBackupManager with automated scheduling - Generate initial migration with 9 tables - Add seed script and documentation - Configure Drizzle Kit for migration management - Add NPM scripts for database operations Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
13
.env.example
Normal file
13
.env.example
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# Turso Database Configuration
|
||||||
|
TURSO_DATABASE_URL=libsql://<region>-<project>.turso.io
|
||||||
|
TURSO_AUTH_TOKEN=<auth-token>
|
||||||
|
|
||||||
|
# Backup Configuration (optional)
|
||||||
|
BACKUP_INTERVAL_MS=86400000
|
||||||
|
BACKUP_RETENTION_DAYS=30
|
||||||
|
BACKUP_REGION=us-east
|
||||||
|
|
||||||
|
# Clerk Authentication
|
||||||
|
VITE_CLERK_PUBLISHABLE_KEY=pk_<your-publishable-key>
|
||||||
|
VITE_CLERK_SIGN_IN_URL=/sign-in
|
||||||
|
VITE_CLERK_SIGN_UP_URL=/sign-up
|
||||||
11
drizzle.config.ts
Normal file
11
drizzle.config.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { defineConfig } from "drizzle-kit";
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
schema: "./src/db/schema/index.ts",
|
||||||
|
out: "./src/db/migrations",
|
||||||
|
dialect: "turso",
|
||||||
|
dbCredentials: {
|
||||||
|
url: process.env.TURSO_DATABASE_URL!,
|
||||||
|
authToken: process.env.TURSO_AUTH_TOKEN!,
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -21,10 +21,14 @@
|
|||||||
"tauri:build": "tauri build",
|
"tauri:build": "tauri build",
|
||||||
"tauri:build:macos": "TAURI_TARGET=x86_64-apple-darwin tauri build",
|
"tauri:build:macos": "TAURI_TARGET=x86_64-apple-darwin tauri build",
|
||||||
"tauri:build:windows": "TAURI_TARGET=x86_64-pc-windows-msvc tauri build",
|
"tauri:build:windows": "TAURI_TARGET=x86_64-pc-windows-msvc tauri build",
|
||||||
"tauri:build:linux": "TAURI_TARGET=x86_64-unknown-linux-gnu tauri build"
|
"tauri:build:linux": "TAURI_TARGET=x86_64-unknown-linux-gnu tauri build",
|
||||||
|
"tauri:test": "cargo test --manifest-path src-tauri/Cargo.toml",
|
||||||
|
"tauri:icons": "bash src-tauri/generate-icons.sh"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@clerk/clerk-js": "^6.7.5",
|
||||||
"@libsql/client": "^0.17.3",
|
"@libsql/client": "^0.17.3",
|
||||||
|
"@solidjs/router": "^0.16.1",
|
||||||
"@tanstack/react-query": "^5.100.1",
|
"@tanstack/react-query": "^5.100.1",
|
||||||
"@tanstack/solid-query": "^5.100.1",
|
"@tanstack/solid-query": "^5.100.1",
|
||||||
"@trpc/client": "^11.16.0",
|
"@trpc/client": "^11.16.0",
|
||||||
|
|||||||
118
src/db/README.md
Normal file
118
src/db/README.md
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
# Database Setup
|
||||||
|
|
||||||
|
Turso (SQLite at edge) with Drizzle ORM for type-safe database access.
|
||||||
|
|
||||||
|
## Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
src/db/
|
||||||
|
├── schema/ # Database schema definitions
|
||||||
|
│ ├── index.ts # Schema exports
|
||||||
|
│ ├── users.ts # User accounts
|
||||||
|
│ ├── projects.ts # Projects
|
||||||
|
│ ├── scripts.ts # Scripts
|
||||||
|
│ ├── characters.ts # Characters
|
||||||
|
│ └── scenes.ts # Scenes with character relationships
|
||||||
|
├── config/ # Database configuration
|
||||||
|
│ ├── database.ts # Primary database manager
|
||||||
|
│ ├── edge-database.ts # Edge replica manager
|
||||||
|
│ └── migrations.ts # Drizzle ORM setup
|
||||||
|
└── migrations/ # Migration files (generated)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Environment Variables
|
||||||
|
|
||||||
|
```bash
|
||||||
|
TURSO_DATABASE_URL="libsql://<region>-<project>.turso.io"
|
||||||
|
TURSO_AUTH_TOKEN="<auth-token>"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install @libsql/client drizzle-orm drizzle-kit
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Primary Database
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { db } from "./config/migrations";
|
||||||
|
import { users } from "../schema";
|
||||||
|
|
||||||
|
// Query
|
||||||
|
const allUsers = await db.select().from(users);
|
||||||
|
|
||||||
|
// Insert
|
||||||
|
const newUser = await db.insert(users).values({
|
||||||
|
email: "user@example.com",
|
||||||
|
username: "johndoe",
|
||||||
|
role: "editor",
|
||||||
|
}).returning();
|
||||||
|
```
|
||||||
|
|
||||||
|
### Edge Database
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { createEdgeDatabaseManager } from "./config/edge-database";
|
||||||
|
|
||||||
|
const edgeDb = createEdgeDatabaseManager({
|
||||||
|
primaryRegion: {
|
||||||
|
region: "primary",
|
||||||
|
url: "libsql://primary.turso.io",
|
||||||
|
authToken: process.env.TURSO_AUTH_TOKEN,
|
||||||
|
isPrimary: true,
|
||||||
|
},
|
||||||
|
edgeReplicas: [
|
||||||
|
{
|
||||||
|
region: "us-east",
|
||||||
|
url: "libsql://us-east.turso.io",
|
||||||
|
authToken: process.env.TURSO_AUTH_TOKEN,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
region: "eu-west",
|
||||||
|
url: "libsql://eu-west.turso.io",
|
||||||
|
authToken: process.env.TURSO_AUTH_TOKEN,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
// Query on edge
|
||||||
|
const users = await edgeDb.queryOnDefaultEdge<User>("SELECT * FROM users");
|
||||||
|
```
|
||||||
|
|
||||||
|
## Migrations
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Generate migrations
|
||||||
|
npx drizzle-kit generate
|
||||||
|
|
||||||
|
# Push schema changes
|
||||||
|
npx drizzle-kit push
|
||||||
|
|
||||||
|
# Run migrations
|
||||||
|
npx drizzle-kit migrate
|
||||||
|
```
|
||||||
|
|
||||||
|
## Schema Overview
|
||||||
|
|
||||||
|
### users
|
||||||
|
- User accounts with roles (admin, editor, viewer)
|
||||||
|
- Authentication and authorization
|
||||||
|
|
||||||
|
### projects
|
||||||
|
- Project containers owned by users
|
||||||
|
- Public/private visibility
|
||||||
|
|
||||||
|
### scripts
|
||||||
|
- Screenplay documents within projects
|
||||||
|
- Version tracking and status management
|
||||||
|
|
||||||
|
### characters
|
||||||
|
- Character definitions for scripts
|
||||||
|
- Role-based categorization
|
||||||
|
|
||||||
|
### scenes
|
||||||
|
- Scene content with character relationships
|
||||||
|
- Many-to-many relationship between scenes and characters
|
||||||
2
src/db/backup/index.ts
Normal file
2
src/db/backup/index.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
// Backup exports
|
||||||
|
export { DatabaseBackupManager, createDatabaseBackupManager } from "../config/backup";
|
||||||
109
src/db/config/backup.ts
Normal file
109
src/db/config/backup.ts
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
import { DatabaseManager } from "./database";
|
||||||
|
|
||||||
|
interface BackupConfig {
|
||||||
|
backupIntervalMs: number;
|
||||||
|
retentionDays: number;
|
||||||
|
backupRegion: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DatabaseBackupManager {
|
||||||
|
private dbManager: DatabaseManager;
|
||||||
|
private config: BackupConfig;
|
||||||
|
private backupTimer: NodeJS.Timeout | null = null;
|
||||||
|
|
||||||
|
constructor(dbManager: DatabaseManager, config: BackupConfig) {
|
||||||
|
this.dbManager = dbManager;
|
||||||
|
this.config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
start(): void {
|
||||||
|
console.log(`Starting database backup every ${this.config.backupIntervalMs / (1000 * 60)} minutes`);
|
||||||
|
|
||||||
|
this.backupTimer = setInterval(async () => {
|
||||||
|
await this.performBackup();
|
||||||
|
}, this.config.backupIntervalMs);
|
||||||
|
}
|
||||||
|
|
||||||
|
stop(): void {
|
||||||
|
if (this.backupTimer) {
|
||||||
|
clearInterval(this.backupTimer);
|
||||||
|
this.backupTimer = null;
|
||||||
|
console.log("Database backup stopped");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async performBackup(): Promise<void> {
|
||||||
|
try {
|
||||||
|
console.log(`Performing database backup to ${this.config.backupRegion}...`);
|
||||||
|
|
||||||
|
// Get all tables
|
||||||
|
const tables = await this.dbManager.query<string>(
|
||||||
|
"SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'"
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const table of tables) {
|
||||||
|
const data = await this.dbManager.query<Record<string, unknown>>(
|
||||||
|
`SELECT * FROM ${table}`
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(`Backed up ${table}: ${data.length} rows`);
|
||||||
|
|
||||||
|
// Store backup with timestamp
|
||||||
|
const timestamp = new Date().toISOString();
|
||||||
|
await this.dbManager.execute(
|
||||||
|
`INSERT INTO backups (table_name, data, backup_time, region) VALUES (?, ?, ?, ?)`,
|
||||||
|
[table, JSON.stringify(data), timestamp, this.config.backupRegion]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.cleanupOldBackups();
|
||||||
|
console.log("Database backup completed successfully");
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Database backup failed:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async cleanupOldBackups(): Promise<void> {
|
||||||
|
const cutoffDate = new Date();
|
||||||
|
cutoffDate.setDate(cutoffDate.getDate() - this.config.retentionDays);
|
||||||
|
|
||||||
|
await this.dbManager.execute(
|
||||||
|
"DELETE FROM backups WHERE backup_time < ?",
|
||||||
|
[cutoffDate.toISOString()]
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(`Cleaned up backups older than ${this.config.retentionDays} days`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async restoreFromBackup(backupTime: string): Promise<void> {
|
||||||
|
console.log(`Restoring database from backup at ${backupTime}...`);
|
||||||
|
|
||||||
|
const backups = await this.dbManager.query<{
|
||||||
|
table_name: string;
|
||||||
|
data: string;
|
||||||
|
}>(
|
||||||
|
"SELECT table_name, data FROM backups WHERE backup_time = ? ORDER BY table_name",
|
||||||
|
[backupTime]
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const backup of backups) {
|
||||||
|
const data = JSON.parse(backup.data) as Record<string, unknown>[];
|
||||||
|
console.log(`Restoring ${backup.table_name}: ${data.length} rows`);
|
||||||
|
|
||||||
|
// Note: This is a simplified restore. In production, you'd want to:
|
||||||
|
// 1. Clear the table first
|
||||||
|
// 2. Handle foreign key constraints
|
||||||
|
// 3. Use transactions
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("Database restore completed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createDatabaseBackupManager(
|
||||||
|
dbManager: DatabaseManager,
|
||||||
|
config: BackupConfig
|
||||||
|
): DatabaseBackupManager {
|
||||||
|
return new DatabaseBackupManager(dbManager, config);
|
||||||
|
}
|
||||||
64
src/db/config/database.ts
Normal file
64
src/db/config/database.ts
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
import { createClient, type Client as LibSQLClient } from "@libsql/client";
|
||||||
|
|
||||||
|
interface DatabaseConfig {
|
||||||
|
url: string;
|
||||||
|
authToken?: string;
|
||||||
|
concurrentConnections?: number;
|
||||||
|
connectTimeoutMs?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DatabaseManager {
|
||||||
|
private static instance: DatabaseManager;
|
||||||
|
private client: LibSQLClient | null = null;
|
||||||
|
private config: DatabaseConfig;
|
||||||
|
|
||||||
|
private constructor(config: DatabaseConfig) {
|
||||||
|
this.config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
static getInstance(config: DatabaseConfig): DatabaseManager {
|
||||||
|
if (!DatabaseManager.instance) {
|
||||||
|
DatabaseManager.instance = new DatabaseManager(config);
|
||||||
|
}
|
||||||
|
return DatabaseManager.instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
initialize(): LibSQLClient {
|
||||||
|
if (!this.client) {
|
||||||
|
this.client = createClient({
|
||||||
|
url: this.config.url,
|
||||||
|
authToken: this.config.authToken,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return this.client;
|
||||||
|
}
|
||||||
|
|
||||||
|
getClient(): LibSQLClient {
|
||||||
|
if (!this.client) {
|
||||||
|
return this.initialize();
|
||||||
|
}
|
||||||
|
return this.client;
|
||||||
|
}
|
||||||
|
|
||||||
|
async close(): Promise<void> {
|
||||||
|
if (this.client) {
|
||||||
|
await this.client.close();
|
||||||
|
this.client = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async execute(query: string, params?: unknown[]): Promise<void> {
|
||||||
|
const client = this.getClient();
|
||||||
|
await client.execute(query, params as any);
|
||||||
|
}
|
||||||
|
|
||||||
|
async query<T>(query: string, params?: unknown[]): Promise<T[]> {
|
||||||
|
const client = this.getClient();
|
||||||
|
const result = await client.execute(query, params as any);
|
||||||
|
return result.rows as T[];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createDatabaseManager(config: DatabaseConfig): DatabaseManager {
|
||||||
|
return DatabaseManager.getInstance(config);
|
||||||
|
}
|
||||||
85
src/db/config/edge-database.ts
Normal file
85
src/db/config/edge-database.ts
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
import { createDatabaseManager, type DatabaseManager } from "./database";
|
||||||
|
|
||||||
|
interface EdgeRegion {
|
||||||
|
region: string;
|
||||||
|
url: string;
|
||||||
|
authToken?: string;
|
||||||
|
isPrimary?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface EdgeConfig {
|
||||||
|
primaryRegion: EdgeRegion;
|
||||||
|
edgeReplicas: EdgeRegion[];
|
||||||
|
fallbackRegion?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class EdgeDatabaseManager {
|
||||||
|
private primaryManager: DatabaseManager;
|
||||||
|
private edgeManagers: Map<string, DatabaseManager>;
|
||||||
|
private config: EdgeConfig;
|
||||||
|
|
||||||
|
constructor(config: EdgeConfig) {
|
||||||
|
this.config = config;
|
||||||
|
this.edgeManagers = new Map();
|
||||||
|
|
||||||
|
this.primaryManager = createDatabaseManager({
|
||||||
|
url: config.primaryRegion.url,
|
||||||
|
authToken: config.primaryRegion.authToken,
|
||||||
|
});
|
||||||
|
|
||||||
|
config.edgeReplicas.forEach((replica) => {
|
||||||
|
this.edgeManagers.set(replica.region, createDatabaseManager({
|
||||||
|
url: replica.url,
|
||||||
|
authToken: replica.authToken,
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getPrimary(): DatabaseManager {
|
||||||
|
return this.primaryManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
getEdge(region: string): DatabaseManager {
|
||||||
|
const manager = this.edgeManagers.get(region);
|
||||||
|
if (!manager) {
|
||||||
|
throw new Error(`Edge region ${region} not found`);
|
||||||
|
}
|
||||||
|
return manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
getDefaultEdge(): DatabaseManager {
|
||||||
|
if (this.config.edgeReplicas.length > 0 && this.config.edgeReplicas[0]) {
|
||||||
|
const region = this.config.edgeReplicas[0].region;
|
||||||
|
const manager = this.edgeManagers.get(region);
|
||||||
|
if (manager) {
|
||||||
|
return manager;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this.primaryManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
async close(): Promise<void> {
|
||||||
|
await this.primaryManager.close();
|
||||||
|
for (const manager of this.edgeManagers.values()) {
|
||||||
|
await manager.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async executeOnPrimary(query: string, params?: unknown[]): Promise<void> {
|
||||||
|
await this.primaryManager.execute(query, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
async queryOnEdge<T>(region: string, query: string, params?: unknown[]): Promise<T[]> {
|
||||||
|
const manager = this.getEdge(region);
|
||||||
|
return manager.query<T>(query, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
async queryOnDefaultEdge<T>(query: string, params?: unknown[]): Promise<T[]> {
|
||||||
|
const manager = this.getDefaultEdge();
|
||||||
|
return manager.query<T>(query, params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createEdgeDatabaseManager(config: EdgeConfig): EdgeDatabaseManager {
|
||||||
|
return new EdgeDatabaseManager(config);
|
||||||
|
}
|
||||||
12
src/db/config/migrations.ts
Normal file
12
src/db/config/migrations.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { drizzle } from "drizzle-orm/libsql";
|
||||||
|
import { createClient } from "@libsql/client";
|
||||||
|
import * as schema from "../schema";
|
||||||
|
|
||||||
|
const client = createClient({
|
||||||
|
url: process.env.TURSO_DATABASE_URL!,
|
||||||
|
authToken: process.env.TURSO_AUTH_TOKEN!,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const db = drizzle(client, { schema });
|
||||||
|
|
||||||
|
export type DrizzleDB = typeof db;
|
||||||
7
src/db/index.ts
Normal file
7
src/db/index.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
// Database exports
|
||||||
|
export { db, type DrizzleDB } from "./config/migrations";
|
||||||
|
export { DatabaseManager, createDatabaseManager } from "./config/database";
|
||||||
|
export { EdgeDatabaseManager, createEdgeDatabaseManager } from "./config/edge-database";
|
||||||
|
|
||||||
|
// Schema exports
|
||||||
|
export * from "./schema";
|
||||||
139
src/db/migrations/0000_complex_donald_blake.sql
Normal file
139
src/db/migrations/0000_complex_donald_blake.sql
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
CREATE TABLE `character_relationships` (
|
||||||
|
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||||
|
`character_a_id` integer NOT NULL,
|
||||||
|
`character_b_id` integer NOT NULL,
|
||||||
|
`relationship_type` text NOT NULL,
|
||||||
|
`description` text,
|
||||||
|
`strength` integer DEFAULT 50 NOT NULL,
|
||||||
|
`is_antagonistic` integer DEFAULT false NOT NULL,
|
||||||
|
`created_at` integer,
|
||||||
|
`updated_at` integer,
|
||||||
|
FOREIGN KEY (`character_a_id`) REFERENCES `characters`(`id`) ON UPDATE no action ON DELETE no action,
|
||||||
|
FOREIGN KEY (`character_b_id`) REFERENCES `characters`(`id`) ON UPDATE no action ON DELETE no action
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE UNIQUE INDEX `character_relationships_unique_pair` ON `character_relationships` (`character_a_id`,`character_b_id`);--> statement-breakpoint
|
||||||
|
CREATE TABLE `characters` (
|
||||||
|
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||||
|
`project_id` integer NOT NULL,
|
||||||
|
`name` text NOT NULL,
|
||||||
|
`slug` text NOT NULL,
|
||||||
|
`role` text DEFAULT 'supporting' NOT NULL,
|
||||||
|
`bio` text,
|
||||||
|
`description` text,
|
||||||
|
`arc` text,
|
||||||
|
`arc_type` text,
|
||||||
|
`age` integer,
|
||||||
|
`gender` text,
|
||||||
|
`voice` text,
|
||||||
|
`traits` text,
|
||||||
|
`motivation` text,
|
||||||
|
`conflict` text,
|
||||||
|
`secret` text,
|
||||||
|
`image_url` text,
|
||||||
|
`created_at` integer,
|
||||||
|
`updated_at` integer,
|
||||||
|
FOREIGN KEY (`project_id`) REFERENCES `projects`(`id`) ON UPDATE no action ON DELETE no action
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE TABLE `projects` (
|
||||||
|
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||||
|
`name` text NOT NULL,
|
||||||
|
`description` text,
|
||||||
|
`owner_id` integer NOT NULL,
|
||||||
|
`is_public` integer DEFAULT false NOT NULL,
|
||||||
|
`theme` text,
|
||||||
|
`created_at` integer DEFAULT '"2026-04-24T14:30:03.715Z"' NOT NULL,
|
||||||
|
`updated_at` integer DEFAULT '"2026-04-24T14:30:03.715Z"' NOT NULL,
|
||||||
|
FOREIGN KEY (`owner_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE TABLE `revision_changes` (
|
||||||
|
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||||
|
`revision_id` integer NOT NULL,
|
||||||
|
`change_type` text NOT NULL,
|
||||||
|
`element_type` text,
|
||||||
|
`old_content` text,
|
||||||
|
`new_content` text,
|
||||||
|
`scene_number` integer,
|
||||||
|
`line_number` integer,
|
||||||
|
`page_number` integer,
|
||||||
|
`created_at` integer NOT NULL,
|
||||||
|
FOREIGN KEY (`revision_id`) REFERENCES `revisions`(`id`) ON UPDATE no action ON DELETE no action
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE INDEX `revision_changes_revision_idx` ON `revision_changes` (`revision_id`);--> statement-breakpoint
|
||||||
|
CREATE INDEX `revision_changes_type_idx` ON `revision_changes` (`change_type`);--> statement-breakpoint
|
||||||
|
CREATE TABLE `revisions` (
|
||||||
|
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||||
|
`script_id` integer NOT NULL,
|
||||||
|
`version_number` integer NOT NULL,
|
||||||
|
`branch_name` text DEFAULT 'main' NOT NULL,
|
||||||
|
`parent_revision_id` integer,
|
||||||
|
`title` text NOT NULL,
|
||||||
|
`summary` text,
|
||||||
|
`content` text NOT NULL,
|
||||||
|
`author_id` integer NOT NULL,
|
||||||
|
`status` text DEFAULT 'draft' NOT NULL,
|
||||||
|
`reviewed_by_id` integer,
|
||||||
|
`reviewed_at` integer,
|
||||||
|
`created_at` integer NOT NULL,
|
||||||
|
`updated_at` integer NOT NULL,
|
||||||
|
FOREIGN KEY (`script_id`) REFERENCES `scripts`(`id`) ON UPDATE no action ON DELETE no action,
|
||||||
|
FOREIGN KEY (`author_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action,
|
||||||
|
FOREIGN KEY (`reviewed_by_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE INDEX `revisions_script_version_idx` ON `revisions` (`script_id`,`version_number`);--> statement-breakpoint
|
||||||
|
CREATE INDEX `revisions_script_branch_idx` ON `revisions` (`script_id`,`branch_name`);--> statement-breakpoint
|
||||||
|
CREATE INDEX `revisions_author_idx` ON `revisions` (`author_id`);--> statement-breakpoint
|
||||||
|
CREATE TABLE `scene_characters` (
|
||||||
|
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||||
|
`scene_id` integer NOT NULL,
|
||||||
|
`character_id` integer NOT NULL,
|
||||||
|
`screen_time` integer,
|
||||||
|
`dialogue_lines` integer DEFAULT 0,
|
||||||
|
FOREIGN KEY (`scene_id`) REFERENCES `scenes`(`id`) ON UPDATE no action ON DELETE no action,
|
||||||
|
FOREIGN KEY (`character_id`) REFERENCES `characters`(`id`) ON UPDATE no action ON DELETE no action
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE TABLE `scenes` (
|
||||||
|
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||||
|
`project_id` integer NOT NULL,
|
||||||
|
`title` text NOT NULL,
|
||||||
|
`content` text DEFAULT '' NOT NULL,
|
||||||
|
`order` integer DEFAULT 0 NOT NULL,
|
||||||
|
`created_at` integer,
|
||||||
|
`updated_at` integer,
|
||||||
|
FOREIGN KEY (`project_id`) REFERENCES `projects`(`id`) ON UPDATE no action ON DELETE no action
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE TABLE `scripts` (
|
||||||
|
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||||
|
`project_id` integer NOT NULL,
|
||||||
|
`title` text NOT NULL,
|
||||||
|
`slug` text NOT NULL,
|
||||||
|
`genre` text,
|
||||||
|
`logline` text,
|
||||||
|
`status` text DEFAULT 'draft' NOT NULL,
|
||||||
|
`current_version` integer DEFAULT 1 NOT NULL,
|
||||||
|
`created_at` integer DEFAULT '"2026-04-24T14:30:03.720Z"' NOT NULL,
|
||||||
|
`updated_at` integer DEFAULT '"2026-04-24T14:30:03.720Z"' NOT NULL,
|
||||||
|
FOREIGN KEY (`project_id`) REFERENCES `projects`(`id`) ON UPDATE no action ON DELETE no action
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE TABLE `users` (
|
||||||
|
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||||
|
`email` text NOT NULL,
|
||||||
|
`username` text NOT NULL,
|
||||||
|
`full_name` text,
|
||||||
|
`avatar_url` text,
|
||||||
|
`role` text DEFAULT 'viewer' NOT NULL,
|
||||||
|
`is_active` integer DEFAULT true NOT NULL,
|
||||||
|
`last_login_at` integer,
|
||||||
|
`created_at` integer DEFAULT '"2026-04-24T14:30:03.711Z"' NOT NULL,
|
||||||
|
`updated_at` integer DEFAULT '"2026-04-24T14:30:03.711Z"' NOT NULL
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE UNIQUE INDEX `users_email_unique` ON `users` (`email`);--> statement-breakpoint
|
||||||
|
CREATE UNIQUE INDEX `users_username_unique` ON `users` (`username`);
|
||||||
22
src/db/migrations/0001_tan_machine_man.sql
Normal file
22
src/db/migrations/0001_tan_machine_man.sql
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
DROP INDEX "character_relationships_unique_pair";--> statement-breakpoint
|
||||||
|
DROP INDEX "revision_changes_revision_idx";--> statement-breakpoint
|
||||||
|
DROP INDEX "revision_changes_type_idx";--> statement-breakpoint
|
||||||
|
DROP INDEX "revisions_script_version_idx";--> statement-breakpoint
|
||||||
|
DROP INDEX "revisions_script_branch_idx";--> statement-breakpoint
|
||||||
|
DROP INDEX "revisions_author_idx";--> statement-breakpoint
|
||||||
|
DROP INDEX "users_email_unique";--> statement-breakpoint
|
||||||
|
DROP INDEX "users_username_unique";--> statement-breakpoint
|
||||||
|
ALTER TABLE `projects` ALTER COLUMN "created_at" TO "created_at" integer NOT NULL DEFAULT '"2026-04-24T15:28:03.755Z"';--> statement-breakpoint
|
||||||
|
CREATE UNIQUE INDEX `character_relationships_unique_pair` ON `character_relationships` (`character_a_id`,`character_b_id`);--> statement-breakpoint
|
||||||
|
CREATE INDEX `revision_changes_revision_idx` ON `revision_changes` (`revision_id`);--> statement-breakpoint
|
||||||
|
CREATE INDEX `revision_changes_type_idx` ON `revision_changes` (`change_type`);--> statement-breakpoint
|
||||||
|
CREATE INDEX `revisions_script_version_idx` ON `revisions` (`script_id`,`version_number`);--> statement-breakpoint
|
||||||
|
CREATE INDEX `revisions_script_branch_idx` ON `revisions` (`script_id`,`branch_name`);--> statement-breakpoint
|
||||||
|
CREATE INDEX `revisions_author_idx` ON `revisions` (`author_id`);--> statement-breakpoint
|
||||||
|
CREATE UNIQUE INDEX `users_email_unique` ON `users` (`email`);--> statement-breakpoint
|
||||||
|
CREATE UNIQUE INDEX `users_username_unique` ON `users` (`username`);--> statement-breakpoint
|
||||||
|
ALTER TABLE `projects` ALTER COLUMN "updated_at" TO "updated_at" integer NOT NULL DEFAULT '"2026-04-24T15:28:03.755Z"';--> statement-breakpoint
|
||||||
|
ALTER TABLE `scripts` ALTER COLUMN "created_at" TO "created_at" integer NOT NULL DEFAULT '"2026-04-24T15:28:03.757Z"';--> statement-breakpoint
|
||||||
|
ALTER TABLE `scripts` ALTER COLUMN "updated_at" TO "updated_at" integer NOT NULL DEFAULT '"2026-04-24T15:28:03.757Z"';--> statement-breakpoint
|
||||||
|
ALTER TABLE `users` ALTER COLUMN "created_at" TO "created_at" integer NOT NULL DEFAULT '"2026-04-24T15:28:03.752Z"';--> statement-breakpoint
|
||||||
|
ALTER TABLE `users` ALTER COLUMN "updated_at" TO "updated_at" integer NOT NULL DEFAULT '"2026-04-24T15:28:03.752Z"';
|
||||||
998
src/db/migrations/meta/0000_snapshot.json
Normal file
998
src/db/migrations/meta/0000_snapshot.json
Normal file
@@ -0,0 +1,998 @@
|
|||||||
|
{
|
||||||
|
"version": "6",
|
||||||
|
"dialect": "sqlite",
|
||||||
|
"id": "0b85a4db-4b45-48e9-8e11-d2d423ac9ac8",
|
||||||
|
"prevId": "00000000-0000-0000-0000-000000000000",
|
||||||
|
"tables": {
|
||||||
|
"character_relationships": {
|
||||||
|
"name": "character_relationships",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": true
|
||||||
|
},
|
||||||
|
"character_a_id": {
|
||||||
|
"name": "character_a_id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"character_b_id": {
|
||||||
|
"name": "character_b_id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"relationship_type": {
|
||||||
|
"name": "relationship_type",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"name": "description",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"strength": {
|
||||||
|
"name": "strength",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": 50
|
||||||
|
},
|
||||||
|
"is_antagonistic": {
|
||||||
|
"name": "is_antagonistic",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"updated_at": {
|
||||||
|
"name": "updated_at",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {
|
||||||
|
"character_relationships_unique_pair": {
|
||||||
|
"name": "character_relationships_unique_pair",
|
||||||
|
"columns": [
|
||||||
|
"character_a_id",
|
||||||
|
"character_b_id"
|
||||||
|
],
|
||||||
|
"isUnique": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"foreignKeys": {
|
||||||
|
"character_relationships_character_a_id_characters_id_fk": {
|
||||||
|
"name": "character_relationships_character_a_id_characters_id_fk",
|
||||||
|
"tableFrom": "character_relationships",
|
||||||
|
"tableTo": "characters",
|
||||||
|
"columnsFrom": [
|
||||||
|
"character_a_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
},
|
||||||
|
"character_relationships_character_b_id_characters_id_fk": {
|
||||||
|
"name": "character_relationships_character_b_id_characters_id_fk",
|
||||||
|
"tableFrom": "character_relationships",
|
||||||
|
"tableTo": "characters",
|
||||||
|
"columnsFrom": [
|
||||||
|
"character_b_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
|
},
|
||||||
|
"characters": {
|
||||||
|
"name": "characters",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": true
|
||||||
|
},
|
||||||
|
"project_id": {
|
||||||
|
"name": "project_id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"name": "name",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"slug": {
|
||||||
|
"name": "slug",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"role": {
|
||||||
|
"name": "role",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "'supporting'"
|
||||||
|
},
|
||||||
|
"bio": {
|
||||||
|
"name": "bio",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"name": "description",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"arc": {
|
||||||
|
"name": "arc",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"arc_type": {
|
||||||
|
"name": "arc_type",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"age": {
|
||||||
|
"name": "age",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"gender": {
|
||||||
|
"name": "gender",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"voice": {
|
||||||
|
"name": "voice",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"traits": {
|
||||||
|
"name": "traits",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"motivation": {
|
||||||
|
"name": "motivation",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"conflict": {
|
||||||
|
"name": "conflict",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"secret": {
|
||||||
|
"name": "secret",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"image_url": {
|
||||||
|
"name": "image_url",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"updated_at": {
|
||||||
|
"name": "updated_at",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"characters_project_id_projects_id_fk": {
|
||||||
|
"name": "characters_project_id_projects_id_fk",
|
||||||
|
"tableFrom": "characters",
|
||||||
|
"tableTo": "projects",
|
||||||
|
"columnsFrom": [
|
||||||
|
"project_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
|
},
|
||||||
|
"projects": {
|
||||||
|
"name": "projects",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": true
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"name": "name",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"name": "description",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"owner_id": {
|
||||||
|
"name": "owner_id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"is_public": {
|
||||||
|
"name": "is_public",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"theme": {
|
||||||
|
"name": "theme",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "'\"2026-04-24T14:30:03.715Z\"'"
|
||||||
|
},
|
||||||
|
"updated_at": {
|
||||||
|
"name": "updated_at",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "'\"2026-04-24T14:30:03.715Z\"'"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"projects_owner_id_users_id_fk": {
|
||||||
|
"name": "projects_owner_id_users_id_fk",
|
||||||
|
"tableFrom": "projects",
|
||||||
|
"tableTo": "users",
|
||||||
|
"columnsFrom": [
|
||||||
|
"owner_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
|
},
|
||||||
|
"revision_changes": {
|
||||||
|
"name": "revision_changes",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": true
|
||||||
|
},
|
||||||
|
"revision_id": {
|
||||||
|
"name": "revision_id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"change_type": {
|
||||||
|
"name": "change_type",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"element_type": {
|
||||||
|
"name": "element_type",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"old_content": {
|
||||||
|
"name": "old_content",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"new_content": {
|
||||||
|
"name": "new_content",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"scene_number": {
|
||||||
|
"name": "scene_number",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"line_number": {
|
||||||
|
"name": "line_number",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"page_number": {
|
||||||
|
"name": "page_number",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {
|
||||||
|
"revision_changes_revision_idx": {
|
||||||
|
"name": "revision_changes_revision_idx",
|
||||||
|
"columns": [
|
||||||
|
"revision_id"
|
||||||
|
],
|
||||||
|
"isUnique": false
|
||||||
|
},
|
||||||
|
"revision_changes_type_idx": {
|
||||||
|
"name": "revision_changes_type_idx",
|
||||||
|
"columns": [
|
||||||
|
"change_type"
|
||||||
|
],
|
||||||
|
"isUnique": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"foreignKeys": {
|
||||||
|
"revision_changes_revision_id_revisions_id_fk": {
|
||||||
|
"name": "revision_changes_revision_id_revisions_id_fk",
|
||||||
|
"tableFrom": "revision_changes",
|
||||||
|
"tableTo": "revisions",
|
||||||
|
"columnsFrom": [
|
||||||
|
"revision_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
|
},
|
||||||
|
"revisions": {
|
||||||
|
"name": "revisions",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": true
|
||||||
|
},
|
||||||
|
"script_id": {
|
||||||
|
"name": "script_id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"version_number": {
|
||||||
|
"name": "version_number",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"branch_name": {
|
||||||
|
"name": "branch_name",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "'main'"
|
||||||
|
},
|
||||||
|
"parent_revision_id": {
|
||||||
|
"name": "parent_revision_id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"title": {
|
||||||
|
"name": "title",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"summary": {
|
||||||
|
"name": "summary",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"content": {
|
||||||
|
"name": "content",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"author_id": {
|
||||||
|
"name": "author_id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"name": "status",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "'draft'"
|
||||||
|
},
|
||||||
|
"reviewed_by_id": {
|
||||||
|
"name": "reviewed_by_id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"reviewed_at": {
|
||||||
|
"name": "reviewed_at",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"updated_at": {
|
||||||
|
"name": "updated_at",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {
|
||||||
|
"revisions_script_version_idx": {
|
||||||
|
"name": "revisions_script_version_idx",
|
||||||
|
"columns": [
|
||||||
|
"script_id",
|
||||||
|
"version_number"
|
||||||
|
],
|
||||||
|
"isUnique": false
|
||||||
|
},
|
||||||
|
"revisions_script_branch_idx": {
|
||||||
|
"name": "revisions_script_branch_idx",
|
||||||
|
"columns": [
|
||||||
|
"script_id",
|
||||||
|
"branch_name"
|
||||||
|
],
|
||||||
|
"isUnique": false
|
||||||
|
},
|
||||||
|
"revisions_author_idx": {
|
||||||
|
"name": "revisions_author_idx",
|
||||||
|
"columns": [
|
||||||
|
"author_id"
|
||||||
|
],
|
||||||
|
"isUnique": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"foreignKeys": {
|
||||||
|
"revisions_script_id_scripts_id_fk": {
|
||||||
|
"name": "revisions_script_id_scripts_id_fk",
|
||||||
|
"tableFrom": "revisions",
|
||||||
|
"tableTo": "scripts",
|
||||||
|
"columnsFrom": [
|
||||||
|
"script_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
},
|
||||||
|
"revisions_author_id_users_id_fk": {
|
||||||
|
"name": "revisions_author_id_users_id_fk",
|
||||||
|
"tableFrom": "revisions",
|
||||||
|
"tableTo": "users",
|
||||||
|
"columnsFrom": [
|
||||||
|
"author_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
},
|
||||||
|
"revisions_reviewed_by_id_users_id_fk": {
|
||||||
|
"name": "revisions_reviewed_by_id_users_id_fk",
|
||||||
|
"tableFrom": "revisions",
|
||||||
|
"tableTo": "users",
|
||||||
|
"columnsFrom": [
|
||||||
|
"reviewed_by_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
|
},
|
||||||
|
"scene_characters": {
|
||||||
|
"name": "scene_characters",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": true
|
||||||
|
},
|
||||||
|
"scene_id": {
|
||||||
|
"name": "scene_id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"character_id": {
|
||||||
|
"name": "character_id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"screen_time": {
|
||||||
|
"name": "screen_time",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"dialogue_lines": {
|
||||||
|
"name": "dialogue_lines",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"scene_characters_scene_id_scenes_id_fk": {
|
||||||
|
"name": "scene_characters_scene_id_scenes_id_fk",
|
||||||
|
"tableFrom": "scene_characters",
|
||||||
|
"tableTo": "scenes",
|
||||||
|
"columnsFrom": [
|
||||||
|
"scene_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
},
|
||||||
|
"scene_characters_character_id_characters_id_fk": {
|
||||||
|
"name": "scene_characters_character_id_characters_id_fk",
|
||||||
|
"tableFrom": "scene_characters",
|
||||||
|
"tableTo": "characters",
|
||||||
|
"columnsFrom": [
|
||||||
|
"character_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
|
},
|
||||||
|
"scenes": {
|
||||||
|
"name": "scenes",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": true
|
||||||
|
},
|
||||||
|
"project_id": {
|
||||||
|
"name": "project_id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"title": {
|
||||||
|
"name": "title",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"content": {
|
||||||
|
"name": "content",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "''"
|
||||||
|
},
|
||||||
|
"order": {
|
||||||
|
"name": "order",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": 0
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"updated_at": {
|
||||||
|
"name": "updated_at",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"scenes_project_id_projects_id_fk": {
|
||||||
|
"name": "scenes_project_id_projects_id_fk",
|
||||||
|
"tableFrom": "scenes",
|
||||||
|
"tableTo": "projects",
|
||||||
|
"columnsFrom": [
|
||||||
|
"project_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"name": "scripts",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": true
|
||||||
|
},
|
||||||
|
"project_id": {
|
||||||
|
"name": "project_id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"title": {
|
||||||
|
"name": "title",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"slug": {
|
||||||
|
"name": "slug",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"genre": {
|
||||||
|
"name": "genre",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"logline": {
|
||||||
|
"name": "logline",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"name": "status",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "'draft'"
|
||||||
|
},
|
||||||
|
"current_version": {
|
||||||
|
"name": "current_version",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": 1
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "'\"2026-04-24T14:30:03.720Z\"'"
|
||||||
|
},
|
||||||
|
"updated_at": {
|
||||||
|
"name": "updated_at",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "'\"2026-04-24T14:30:03.720Z\"'"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"scripts_project_id_projects_id_fk": {
|
||||||
|
"name": "scripts_project_id_projects_id_fk",
|
||||||
|
"tableFrom": "scripts",
|
||||||
|
"tableTo": "projects",
|
||||||
|
"columnsFrom": [
|
||||||
|
"project_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
|
},
|
||||||
|
"users": {
|
||||||
|
"name": "users",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": true
|
||||||
|
},
|
||||||
|
"email": {
|
||||||
|
"name": "email",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"username": {
|
||||||
|
"name": "username",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"full_name": {
|
||||||
|
"name": "full_name",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"avatar_url": {
|
||||||
|
"name": "avatar_url",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"role": {
|
||||||
|
"name": "role",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "'viewer'"
|
||||||
|
},
|
||||||
|
"is_active": {
|
||||||
|
"name": "is_active",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
"last_login_at": {
|
||||||
|
"name": "last_login_at",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "'\"2026-04-24T14:30:03.711Z\"'"
|
||||||
|
},
|
||||||
|
"updated_at": {
|
||||||
|
"name": "updated_at",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "'\"2026-04-24T14:30:03.711Z\"'"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {
|
||||||
|
"users_email_unique": {
|
||||||
|
"name": "users_email_unique",
|
||||||
|
"columns": [
|
||||||
|
"email"
|
||||||
|
],
|
||||||
|
"isUnique": true
|
||||||
|
},
|
||||||
|
"users_username_unique": {
|
||||||
|
"name": "users_username_unique",
|
||||||
|
"columns": [
|
||||||
|
"username"
|
||||||
|
],
|
||||||
|
"isUnique": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"views": {},
|
||||||
|
"enums": {},
|
||||||
|
"_meta": {
|
||||||
|
"schemas": {},
|
||||||
|
"tables": {},
|
||||||
|
"columns": {}
|
||||||
|
},
|
||||||
|
"internal": {
|
||||||
|
"indexes": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
998
src/db/migrations/meta/0001_snapshot.json
Normal file
998
src/db/migrations/meta/0001_snapshot.json
Normal file
@@ -0,0 +1,998 @@
|
|||||||
|
{
|
||||||
|
"version": "6",
|
||||||
|
"dialect": "sqlite",
|
||||||
|
"id": "14d8455a-98a3-4c7a-806c-10a91f25630f",
|
||||||
|
"prevId": "0b85a4db-4b45-48e9-8e11-d2d423ac9ac8",
|
||||||
|
"tables": {
|
||||||
|
"character_relationships": {
|
||||||
|
"name": "character_relationships",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": true
|
||||||
|
},
|
||||||
|
"character_a_id": {
|
||||||
|
"name": "character_a_id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"character_b_id": {
|
||||||
|
"name": "character_b_id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"relationship_type": {
|
||||||
|
"name": "relationship_type",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"name": "description",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"strength": {
|
||||||
|
"name": "strength",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": 50
|
||||||
|
},
|
||||||
|
"is_antagonistic": {
|
||||||
|
"name": "is_antagonistic",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"updated_at": {
|
||||||
|
"name": "updated_at",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {
|
||||||
|
"character_relationships_unique_pair": {
|
||||||
|
"name": "character_relationships_unique_pair",
|
||||||
|
"columns": [
|
||||||
|
"character_a_id",
|
||||||
|
"character_b_id"
|
||||||
|
],
|
||||||
|
"isUnique": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"foreignKeys": {
|
||||||
|
"character_relationships_character_a_id_characters_id_fk": {
|
||||||
|
"name": "character_relationships_character_a_id_characters_id_fk",
|
||||||
|
"tableFrom": "character_relationships",
|
||||||
|
"tableTo": "characters",
|
||||||
|
"columnsFrom": [
|
||||||
|
"character_a_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
},
|
||||||
|
"character_relationships_character_b_id_characters_id_fk": {
|
||||||
|
"name": "character_relationships_character_b_id_characters_id_fk",
|
||||||
|
"tableFrom": "character_relationships",
|
||||||
|
"tableTo": "characters",
|
||||||
|
"columnsFrom": [
|
||||||
|
"character_b_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
|
},
|
||||||
|
"characters": {
|
||||||
|
"name": "characters",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": true
|
||||||
|
},
|
||||||
|
"project_id": {
|
||||||
|
"name": "project_id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"name": "name",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"slug": {
|
||||||
|
"name": "slug",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"role": {
|
||||||
|
"name": "role",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "'supporting'"
|
||||||
|
},
|
||||||
|
"bio": {
|
||||||
|
"name": "bio",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"name": "description",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"arc": {
|
||||||
|
"name": "arc",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"arc_type": {
|
||||||
|
"name": "arc_type",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"age": {
|
||||||
|
"name": "age",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"gender": {
|
||||||
|
"name": "gender",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"voice": {
|
||||||
|
"name": "voice",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"traits": {
|
||||||
|
"name": "traits",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"motivation": {
|
||||||
|
"name": "motivation",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"conflict": {
|
||||||
|
"name": "conflict",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"secret": {
|
||||||
|
"name": "secret",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"image_url": {
|
||||||
|
"name": "image_url",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"updated_at": {
|
||||||
|
"name": "updated_at",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"characters_project_id_projects_id_fk": {
|
||||||
|
"name": "characters_project_id_projects_id_fk",
|
||||||
|
"tableFrom": "characters",
|
||||||
|
"tableTo": "projects",
|
||||||
|
"columnsFrom": [
|
||||||
|
"project_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
|
},
|
||||||
|
"projects": {
|
||||||
|
"name": "projects",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": true
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"name": "name",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"name": "description",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"owner_id": {
|
||||||
|
"name": "owner_id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"is_public": {
|
||||||
|
"name": "is_public",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"theme": {
|
||||||
|
"name": "theme",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "'\"2026-04-24T15:28:03.755Z\"'"
|
||||||
|
},
|
||||||
|
"updated_at": {
|
||||||
|
"name": "updated_at",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "'\"2026-04-24T15:28:03.755Z\"'"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"projects_owner_id_users_id_fk": {
|
||||||
|
"name": "projects_owner_id_users_id_fk",
|
||||||
|
"tableFrom": "projects",
|
||||||
|
"tableTo": "users",
|
||||||
|
"columnsFrom": [
|
||||||
|
"owner_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
|
},
|
||||||
|
"revision_changes": {
|
||||||
|
"name": "revision_changes",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": true
|
||||||
|
},
|
||||||
|
"revision_id": {
|
||||||
|
"name": "revision_id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"change_type": {
|
||||||
|
"name": "change_type",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"element_type": {
|
||||||
|
"name": "element_type",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"old_content": {
|
||||||
|
"name": "old_content",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"new_content": {
|
||||||
|
"name": "new_content",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"scene_number": {
|
||||||
|
"name": "scene_number",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"line_number": {
|
||||||
|
"name": "line_number",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"page_number": {
|
||||||
|
"name": "page_number",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {
|
||||||
|
"revision_changes_revision_idx": {
|
||||||
|
"name": "revision_changes_revision_idx",
|
||||||
|
"columns": [
|
||||||
|
"revision_id"
|
||||||
|
],
|
||||||
|
"isUnique": false
|
||||||
|
},
|
||||||
|
"revision_changes_type_idx": {
|
||||||
|
"name": "revision_changes_type_idx",
|
||||||
|
"columns": [
|
||||||
|
"change_type"
|
||||||
|
],
|
||||||
|
"isUnique": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"foreignKeys": {
|
||||||
|
"revision_changes_revision_id_revisions_id_fk": {
|
||||||
|
"name": "revision_changes_revision_id_revisions_id_fk",
|
||||||
|
"tableFrom": "revision_changes",
|
||||||
|
"tableTo": "revisions",
|
||||||
|
"columnsFrom": [
|
||||||
|
"revision_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
|
},
|
||||||
|
"revisions": {
|
||||||
|
"name": "revisions",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": true
|
||||||
|
},
|
||||||
|
"script_id": {
|
||||||
|
"name": "script_id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"version_number": {
|
||||||
|
"name": "version_number",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"branch_name": {
|
||||||
|
"name": "branch_name",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "'main'"
|
||||||
|
},
|
||||||
|
"parent_revision_id": {
|
||||||
|
"name": "parent_revision_id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"title": {
|
||||||
|
"name": "title",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"summary": {
|
||||||
|
"name": "summary",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"content": {
|
||||||
|
"name": "content",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"author_id": {
|
||||||
|
"name": "author_id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"name": "status",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "'draft'"
|
||||||
|
},
|
||||||
|
"reviewed_by_id": {
|
||||||
|
"name": "reviewed_by_id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"reviewed_at": {
|
||||||
|
"name": "reviewed_at",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"updated_at": {
|
||||||
|
"name": "updated_at",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {
|
||||||
|
"revisions_script_version_idx": {
|
||||||
|
"name": "revisions_script_version_idx",
|
||||||
|
"columns": [
|
||||||
|
"script_id",
|
||||||
|
"version_number"
|
||||||
|
],
|
||||||
|
"isUnique": false
|
||||||
|
},
|
||||||
|
"revisions_script_branch_idx": {
|
||||||
|
"name": "revisions_script_branch_idx",
|
||||||
|
"columns": [
|
||||||
|
"script_id",
|
||||||
|
"branch_name"
|
||||||
|
],
|
||||||
|
"isUnique": false
|
||||||
|
},
|
||||||
|
"revisions_author_idx": {
|
||||||
|
"name": "revisions_author_idx",
|
||||||
|
"columns": [
|
||||||
|
"author_id"
|
||||||
|
],
|
||||||
|
"isUnique": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"foreignKeys": {
|
||||||
|
"revisions_script_id_scripts_id_fk": {
|
||||||
|
"name": "revisions_script_id_scripts_id_fk",
|
||||||
|
"tableFrom": "revisions",
|
||||||
|
"tableTo": "scripts",
|
||||||
|
"columnsFrom": [
|
||||||
|
"script_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
},
|
||||||
|
"revisions_author_id_users_id_fk": {
|
||||||
|
"name": "revisions_author_id_users_id_fk",
|
||||||
|
"tableFrom": "revisions",
|
||||||
|
"tableTo": "users",
|
||||||
|
"columnsFrom": [
|
||||||
|
"author_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
},
|
||||||
|
"revisions_reviewed_by_id_users_id_fk": {
|
||||||
|
"name": "revisions_reviewed_by_id_users_id_fk",
|
||||||
|
"tableFrom": "revisions",
|
||||||
|
"tableTo": "users",
|
||||||
|
"columnsFrom": [
|
||||||
|
"reviewed_by_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
|
},
|
||||||
|
"scene_characters": {
|
||||||
|
"name": "scene_characters",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": true
|
||||||
|
},
|
||||||
|
"scene_id": {
|
||||||
|
"name": "scene_id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"character_id": {
|
||||||
|
"name": "character_id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"screen_time": {
|
||||||
|
"name": "screen_time",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"dialogue_lines": {
|
||||||
|
"name": "dialogue_lines",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"scene_characters_scene_id_scenes_id_fk": {
|
||||||
|
"name": "scene_characters_scene_id_scenes_id_fk",
|
||||||
|
"tableFrom": "scene_characters",
|
||||||
|
"tableTo": "scenes",
|
||||||
|
"columnsFrom": [
|
||||||
|
"scene_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
},
|
||||||
|
"scene_characters_character_id_characters_id_fk": {
|
||||||
|
"name": "scene_characters_character_id_characters_id_fk",
|
||||||
|
"tableFrom": "scene_characters",
|
||||||
|
"tableTo": "characters",
|
||||||
|
"columnsFrom": [
|
||||||
|
"character_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
|
},
|
||||||
|
"scenes": {
|
||||||
|
"name": "scenes",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": true
|
||||||
|
},
|
||||||
|
"project_id": {
|
||||||
|
"name": "project_id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"title": {
|
||||||
|
"name": "title",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"content": {
|
||||||
|
"name": "content",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "''"
|
||||||
|
},
|
||||||
|
"order": {
|
||||||
|
"name": "order",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": 0
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"updated_at": {
|
||||||
|
"name": "updated_at",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"scenes_project_id_projects_id_fk": {
|
||||||
|
"name": "scenes_project_id_projects_id_fk",
|
||||||
|
"tableFrom": "scenes",
|
||||||
|
"tableTo": "projects",
|
||||||
|
"columnsFrom": [
|
||||||
|
"project_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"name": "scripts",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": true
|
||||||
|
},
|
||||||
|
"project_id": {
|
||||||
|
"name": "project_id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"title": {
|
||||||
|
"name": "title",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"slug": {
|
||||||
|
"name": "slug",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"genre": {
|
||||||
|
"name": "genre",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"logline": {
|
||||||
|
"name": "logline",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"name": "status",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "'draft'"
|
||||||
|
},
|
||||||
|
"current_version": {
|
||||||
|
"name": "current_version",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": 1
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "'\"2026-04-24T15:28:03.757Z\"'"
|
||||||
|
},
|
||||||
|
"updated_at": {
|
||||||
|
"name": "updated_at",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "'\"2026-04-24T15:28:03.757Z\"'"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"scripts_project_id_projects_id_fk": {
|
||||||
|
"name": "scripts_project_id_projects_id_fk",
|
||||||
|
"tableFrom": "scripts",
|
||||||
|
"tableTo": "projects",
|
||||||
|
"columnsFrom": [
|
||||||
|
"project_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
|
},
|
||||||
|
"users": {
|
||||||
|
"name": "users",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": true
|
||||||
|
},
|
||||||
|
"email": {
|
||||||
|
"name": "email",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"username": {
|
||||||
|
"name": "username",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"full_name": {
|
||||||
|
"name": "full_name",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"avatar_url": {
|
||||||
|
"name": "avatar_url",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"role": {
|
||||||
|
"name": "role",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "'viewer'"
|
||||||
|
},
|
||||||
|
"is_active": {
|
||||||
|
"name": "is_active",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
"last_login_at": {
|
||||||
|
"name": "last_login_at",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "'\"2026-04-24T15:28:03.752Z\"'"
|
||||||
|
},
|
||||||
|
"updated_at": {
|
||||||
|
"name": "updated_at",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "'\"2026-04-24T15:28:03.752Z\"'"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {
|
||||||
|
"users_email_unique": {
|
||||||
|
"name": "users_email_unique",
|
||||||
|
"columns": [
|
||||||
|
"email"
|
||||||
|
],
|
||||||
|
"isUnique": true
|
||||||
|
},
|
||||||
|
"users_username_unique": {
|
||||||
|
"name": "users_username_unique",
|
||||||
|
"columns": [
|
||||||
|
"username"
|
||||||
|
],
|
||||||
|
"isUnique": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"views": {},
|
||||||
|
"enums": {},
|
||||||
|
"_meta": {
|
||||||
|
"schemas": {},
|
||||||
|
"tables": {},
|
||||||
|
"columns": {}
|
||||||
|
},
|
||||||
|
"internal": {
|
||||||
|
"indexes": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
20
src/db/migrations/meta/_journal.json
Normal file
20
src/db/migrations/meta/_journal.json
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"version": "7",
|
||||||
|
"dialect": "sqlite",
|
||||||
|
"entries": [
|
||||||
|
{
|
||||||
|
"idx": 0,
|
||||||
|
"version": "6",
|
||||||
|
"when": 1777041003742,
|
||||||
|
"tag": "0000_complex_donald_blake",
|
||||||
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 1,
|
||||||
|
"version": "6",
|
||||||
|
"when": 1777044483775,
|
||||||
|
"tag": "0001_tan_machine_man",
|
||||||
|
"breakpoints": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
18
src/db/schema/projects.ts
Normal file
18
src/db/schema/projects.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core";
|
||||||
|
import { users } from "./users";
|
||||||
|
|
||||||
|
export const projects = sqliteTable("projects", {
|
||||||
|
id: integer("id").primaryKey({ autoIncrement: true }),
|
||||||
|
name: text("name").notNull(),
|
||||||
|
description: text("description"),
|
||||||
|
ownerId: integer("owner_id")
|
||||||
|
.notNull()
|
||||||
|
.references(() => users.id),
|
||||||
|
isPublic: integer("is_public", { mode: "boolean" }).notNull().default(false),
|
||||||
|
theme: text("theme"),
|
||||||
|
createdAt: integer("created_at", { mode: "timestamp" }).notNull().default(new Date()),
|
||||||
|
updatedAt: integer("updated_at", { mode: "timestamp" }).notNull().default(new Date()),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type Project = typeof projects.$inferSelect;
|
||||||
|
export type NewProject = typeof projects.$inferInsert;
|
||||||
32
src/db/schema/scenes.ts
Normal file
32
src/db/schema/scenes.ts
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core";
|
||||||
|
import { projects } from "./projects";
|
||||||
|
import { characters } from "./characters";
|
||||||
|
|
||||||
|
export const scenes = sqliteTable("scenes", {
|
||||||
|
id: integer("id").primaryKey({ autoIncrement: true }),
|
||||||
|
projectId: integer("project_id")
|
||||||
|
.notNull()
|
||||||
|
.references(() => projects.id),
|
||||||
|
title: text("title").notNull(),
|
||||||
|
content: text("content").notNull().default(""),
|
||||||
|
order: integer("order").notNull().default(0),
|
||||||
|
createdAt: integer("created_at", { mode: "timestamp" }).$defaultFn(() => new Date()),
|
||||||
|
updatedAt: integer("updated_at", { mode: "timestamp" }).$defaultFn(() => new Date()),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const sceneCharacters = sqliteTable("scene_characters", {
|
||||||
|
id: integer("id").primaryKey({ autoIncrement: true }),
|
||||||
|
sceneId: integer("scene_id")
|
||||||
|
.notNull()
|
||||||
|
.references(() => scenes.id),
|
||||||
|
characterId: integer("character_id")
|
||||||
|
.notNull()
|
||||||
|
.references(() => characters.id),
|
||||||
|
screenTime: integer("screen_time"),
|
||||||
|
dialogueLines: integer("dialogue_lines").default(0),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type Scene = typeof scenes.$inferSelect;
|
||||||
|
export type NewScene = typeof scenes.$inferInsert;
|
||||||
|
export type SceneCharacter = typeof sceneCharacters.$inferSelect;
|
||||||
|
export type NewSceneCharacter = typeof sceneCharacters.$inferInsert;
|
||||||
20
src/db/schema/scripts.ts
Normal file
20
src/db/schema/scripts.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core";
|
||||||
|
import { projects } from "./projects";
|
||||||
|
|
||||||
|
export const scripts = sqliteTable("scripts", {
|
||||||
|
id: integer("id").primaryKey({ autoIncrement: true }),
|
||||||
|
projectId: integer("project_id")
|
||||||
|
.notNull()
|
||||||
|
.references(() => projects.id),
|
||||||
|
title: text("title").notNull(),
|
||||||
|
slug: text("slug").notNull(),
|
||||||
|
genre: text("genre"),
|
||||||
|
logline: text("logline"),
|
||||||
|
status: text("status", { enum: ["draft", "revision", "final", "published"] }).notNull().default("draft"),
|
||||||
|
currentVersion: integer("current_version").notNull().default(1),
|
||||||
|
createdAt: integer("created_at", { mode: "timestamp" }).notNull().default(new Date()),
|
||||||
|
updatedAt: integer("updated_at", { mode: "timestamp" }).notNull().default(new Date()),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type Script = typeof scripts.$inferSelect;
|
||||||
|
export type NewScript = typeof scripts.$inferInsert;
|
||||||
17
src/db/schema/users.ts
Normal file
17
src/db/schema/users.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core";
|
||||||
|
|
||||||
|
export const users = sqliteTable("users", {
|
||||||
|
id: integer("id").primaryKey({ autoIncrement: true }),
|
||||||
|
email: text("email").notNull().unique(),
|
||||||
|
username: text("username").notNull().unique(),
|
||||||
|
fullName: text("full_name"),
|
||||||
|
avatarUrl: text("avatar_url"),
|
||||||
|
role: text("role", { enum: ["admin", "editor", "viewer"] }).notNull().default("viewer"),
|
||||||
|
isActive: integer("is_active", { mode: "boolean" }).notNull().default(true),
|
||||||
|
lastLoginAt: integer("last_login_at", { mode: "timestamp" }),
|
||||||
|
createdAt: integer("created_at", { mode: "timestamp" }).notNull().default(new Date()),
|
||||||
|
updatedAt: integer("updated_at", { mode: "timestamp" }).notNull().default(new Date()),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type User = typeof users.$inferSelect;
|
||||||
|
export type NewUser = typeof users.$inferInsert;
|
||||||
93
src/db/seed.ts
Normal file
93
src/db/seed.ts
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
import { db } from "./config/migrations";
|
||||||
|
import { users } from "./schema/users";
|
||||||
|
import { projects } from "./schema/projects";
|
||||||
|
import { scripts } from "./schema/scripts";
|
||||||
|
import { characters } from "./schema/characters";
|
||||||
|
import { scenes, sceneCharacters } from "./schema/scenes";
|
||||||
|
|
||||||
|
export async function seedDatabase() {
|
||||||
|
console.log("Seeding database...");
|
||||||
|
|
||||||
|
// Create admin user
|
||||||
|
const admin = await db.insert(users).values({
|
||||||
|
email: "admin@frenocorp.com",
|
||||||
|
username: "admin",
|
||||||
|
fullName: "Admin User",
|
||||||
|
role: "admin",
|
||||||
|
}).returning();
|
||||||
|
|
||||||
|
if (!admin[0]) throw new Error("Failed to create admin user");
|
||||||
|
|
||||||
|
// Create test project
|
||||||
|
const project = await db.insert(projects).values({
|
||||||
|
name: "Test Project",
|
||||||
|
description: "A test project for development",
|
||||||
|
ownerId: admin[0].id,
|
||||||
|
isPublic: true,
|
||||||
|
}).returning();
|
||||||
|
|
||||||
|
if (!project[0]) throw new Error("Failed to create project");
|
||||||
|
|
||||||
|
// Create test script
|
||||||
|
const script = await db.insert(scripts).values({
|
||||||
|
projectId: project[0].id,
|
||||||
|
title: "Test Screenplay",
|
||||||
|
slug: "test-screenplay",
|
||||||
|
genre: "Drama",
|
||||||
|
logline: "A test screenplay for development purposes",
|
||||||
|
status: "draft",
|
||||||
|
}).returning();
|
||||||
|
|
||||||
|
if (!script[0]) throw new Error("Failed to create script");
|
||||||
|
|
||||||
|
// Create test character
|
||||||
|
const character = await db.insert(characters).values({
|
||||||
|
scriptId: script[0].id,
|
||||||
|
name: "John Doe",
|
||||||
|
role: "protagonist",
|
||||||
|
description: "The main character",
|
||||||
|
age: 30,
|
||||||
|
gender: "male",
|
||||||
|
}).returning();
|
||||||
|
|
||||||
|
if (!character[0]) throw new Error("Failed to create character");
|
||||||
|
|
||||||
|
// Create test scene
|
||||||
|
const scene = await db.insert(scenes).values({
|
||||||
|
scriptId: script[0].id,
|
||||||
|
sceneNumber: 1,
|
||||||
|
actNumber: 1,
|
||||||
|
slugline: "INT. OFFICE - DAY",
|
||||||
|
location: "Office",
|
||||||
|
timeOfDay: "DAY",
|
||||||
|
content: "John sits at his desk, contemplating his next move.",
|
||||||
|
summary: "John in his office",
|
||||||
|
}).returning();
|
||||||
|
|
||||||
|
if (!scene[0]) throw new Error("Failed to create scene");
|
||||||
|
|
||||||
|
// Link character to scene
|
||||||
|
await db.insert(sceneCharacters).values({
|
||||||
|
sceneId: scene[0].id,
|
||||||
|
characterId: character[0].id,
|
||||||
|
screenTime: 5,
|
||||||
|
dialogueLines: 3,
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("Database seeded successfully!");
|
||||||
|
console.log({
|
||||||
|
user: admin[0],
|
||||||
|
project: project[0],
|
||||||
|
script: script[0],
|
||||||
|
character: character[0],
|
||||||
|
scene: scene[0],
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
user: admin[0],
|
||||||
|
project: project[0],
|
||||||
|
script: script[0],
|
||||||
|
character: character[0],
|
||||||
|
scene: scene[0],
|
||||||
|
};
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user