mirror of
https://github.com/bretzel-app/crumbs.git
synced 2026-10-12 12:06:57 +02:00
- Add `api_keys` table with userId foreign key to schema - Add `@modelcontextprotocol/sdk` and `zod` dependencies - Add auto-create SQL and test helpers for api_keys table Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
142 lines
4.6 KiB
TypeScript
142 lines
4.6 KiB
TypeScript
import { sqliteTable, text, integer, uniqueIndex, index } from 'drizzle-orm/sqlite-core';
|
|
|
|
export const users = sqliteTable('users', {
|
|
id: integer('id').primaryKey({ autoIncrement: true }),
|
|
email: text('email').notNull().default(''),
|
|
displayName: text('display_name').notNull().default(''),
|
|
role: text('role', { enum: ['admin', 'user'] }).notNull().default('user'),
|
|
passwordHash: text('password_hash'),
|
|
authProvider: text('auth_provider').notNull().default('password'),
|
|
providerId: text('provider_id'),
|
|
createdAt: integer('created_at', { mode: 'timestamp' }).notNull()
|
|
});
|
|
|
|
export const sessions = sqliteTable('sessions', {
|
|
id: text('id').primaryKey(),
|
|
userId: integer('user_id')
|
|
.references(() => users.id)
|
|
.notNull(),
|
|
expiresAt: integer('expires_at', { mode: 'timestamp' }).notNull(),
|
|
createdAt: integer('created_at', { mode: 'timestamp' }),
|
|
userAgent: text('user_agent'),
|
|
ip: text('ip'),
|
|
lastUsedAt: integer('last_used_at', { mode: 'timestamp' })
|
|
});
|
|
|
|
export const notes = sqliteTable(
|
|
'notes',
|
|
{
|
|
id: text('id').primaryKey(),
|
|
userId: integer('user_id')
|
|
.references(() => users.id)
|
|
.notNull()
|
|
.default(0),
|
|
title: text('title').default('').notNull(),
|
|
content: text('content').default('').notNull(),
|
|
color: text('color').default('default').notNull(),
|
|
pinned: integer('pinned', { mode: 'boolean' }).default(false).notNull(),
|
|
archived: integer('archived', { mode: 'boolean' }).default(false).notNull(),
|
|
trashed: integer('trashed', { mode: 'boolean' }).default(false).notNull(),
|
|
trashedAt: integer('trashed_at', { mode: 'timestamp' }),
|
|
checklistMode: integer('checklist_mode', { mode: 'boolean' }).default(false).notNull(),
|
|
sortOrder: integer('sort_order').default(0).notNull(),
|
|
createdAt: integer('created_at', { mode: 'timestamp' }).notNull(),
|
|
updatedAt: integer('updated_at', { mode: 'timestamp' }).notNull(),
|
|
version: integer('version').default(1).notNull()
|
|
},
|
|
(table) => [
|
|
index('notes_user_id_idx').on(table.userId),
|
|
index('notes_trashed_archived_idx').on(table.trashed, table.archived),
|
|
index('notes_updated_at_idx').on(table.updatedAt)
|
|
]
|
|
);
|
|
|
|
export const tags = sqliteTable(
|
|
'tags',
|
|
{
|
|
id: integer('id').primaryKey({ autoIncrement: true }),
|
|
userId: integer('user_id')
|
|
.references(() => users.id)
|
|
.notNull()
|
|
.default(0),
|
|
name: text('name').notNull()
|
|
},
|
|
(table) => [uniqueIndex('tags_name_user_unique').on(table.name, table.userId)]
|
|
);
|
|
|
|
export const noteTags = sqliteTable(
|
|
'note_tags',
|
|
{
|
|
noteId: text('note_id')
|
|
.references(() => notes.id, { onDelete: 'cascade' })
|
|
.notNull(),
|
|
tagId: integer('tag_id')
|
|
.references(() => tags.id, { onDelete: 'cascade' })
|
|
.notNull()
|
|
},
|
|
(table) => [
|
|
index('note_tags_note_id_idx').on(table.noteId),
|
|
index('note_tags_tag_id_idx').on(table.tagId)
|
|
]
|
|
);
|
|
|
|
export const attachments = sqliteTable('attachments', {
|
|
id: text('id').primaryKey(),
|
|
userId: integer('user_id')
|
|
.references(() => users.id)
|
|
.notNull()
|
|
.default(0),
|
|
noteId: text('note_id')
|
|
.references(() => notes.id, { onDelete: 'cascade' })
|
|
.notNull(),
|
|
filename: text('filename').notNull(),
|
|
mimeType: text('mime_type').notNull(),
|
|
size: integer('size').notNull(),
|
|
path: text('path').notNull(),
|
|
thumbnailPath: text('thumbnail_path'),
|
|
featured: integer('featured', { mode: 'boolean' }).default(false).notNull(),
|
|
createdAt: integer('created_at', { mode: 'timestamp' }).notNull()
|
|
});
|
|
|
|
export const apiKeys = sqliteTable('api_keys', {
|
|
id: text('id').primaryKey(),
|
|
userId: integer('user_id')
|
|
.references(() => users.id)
|
|
.notNull(),
|
|
name: text('name').notNull(),
|
|
keyHash: text('key_hash').notNull(),
|
|
keyPrefix: text('key_prefix').notNull(),
|
|
createdAt: integer('created_at', { mode: 'timestamp' }).notNull(),
|
|
lastUsedAt: integer('last_used_at', { mode: 'timestamp' })
|
|
});
|
|
|
|
export const syncLog = sqliteTable(
|
|
'sync_log',
|
|
{
|
|
id: integer('id').primaryKey({ autoIncrement: true }),
|
|
userId: integer('user_id')
|
|
.references(() => users.id)
|
|
.notNull()
|
|
.default(0),
|
|
noteId: text('note_id')
|
|
.references(() => notes.id)
|
|
.notNull(),
|
|
operation: text('operation').notNull(),
|
|
timestamp: integer('timestamp', { mode: 'timestamp' }).notNull(),
|
|
clientId: text('client_id').notNull()
|
|
},
|
|
(table) => [index('sync_log_timestamp_idx').on(table.timestamp)]
|
|
);
|
|
|
|
export const loginAttempts = sqliteTable(
|
|
'login_attempts',
|
|
{
|
|
id: integer('id').primaryKey({ autoIncrement: true }),
|
|
ip: text('ip').notNull(),
|
|
email: text('email').notNull(),
|
|
success: integer('success', { mode: 'boolean' }).notNull(),
|
|
timestamp: integer('timestamp', { mode: 'timestamp' }).notNull()
|
|
},
|
|
(table) => [index('login_attempts_ip_timestamp_idx').on(table.ip, table.timestamp)]
|
|
);
|