feat: tag management
This commit is contained in:
@@ -184,6 +184,18 @@ export class DatabaseConnection {
|
||||
CREATE INDEX IF NOT EXISTS idx_post_links_source ON post_links(source_post_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_post_links_target ON post_links(target_post_id);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS posts_project_slug_idx ON posts(project_id, slug);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS tags (
|
||||
id TEXT PRIMARY KEY,
|
||||
project_id TEXT NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
color TEXT,
|
||||
created_at INTEGER NOT NULL,
|
||||
updated_at INTEGER NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_tags_project_id ON tags(project_id);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS tags_project_name_idx ON tags(project_id, name);
|
||||
`);
|
||||
|
||||
// Check if project_id column exists in posts table, add if missing (migration)
|
||||
@@ -368,6 +380,27 @@ export class DatabaseConnection {
|
||||
console.log('FTS table migrated - rebuild index required');
|
||||
}
|
||||
|
||||
// Migration: Ensure tags table exists (for databases created before tags feature)
|
||||
const tagsTableExists = await this.localClient.execute(
|
||||
"SELECT name FROM sqlite_master WHERE type='table' AND name='tags'"
|
||||
);
|
||||
if (tagsTableExists.rows.length === 0) {
|
||||
console.log('Creating tags table...');
|
||||
await this.localClient.execute(`
|
||||
CREATE TABLE tags (
|
||||
id TEXT PRIMARY KEY,
|
||||
project_id TEXT NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
color TEXT,
|
||||
created_at INTEGER NOT NULL,
|
||||
updated_at INTEGER NOT NULL
|
||||
)
|
||||
`);
|
||||
await this.localClient.execute('CREATE INDEX idx_tags_project_id ON tags(project_id)');
|
||||
await this.localClient.execute('CREATE UNIQUE INDEX tags_project_name_idx ON tags(project_id, name)');
|
||||
console.log('Tags table created successfully');
|
||||
}
|
||||
|
||||
// Create default project if none exists
|
||||
const existingProjects = await this.localClient.execute('SELECT COUNT(*) as count FROM projects');
|
||||
if (existingProjects.rows[0] && (existingProjects.rows[0].count as number) === 0) {
|
||||
|
||||
@@ -95,6 +95,19 @@ export const postLinks = sqliteTable('post_links', {
|
||||
createdAt: integer('created_at', { mode: 'timestamp' }).notNull(),
|
||||
});
|
||||
|
||||
// Tags table - stores tag metadata with optional colors
|
||||
export const tags = sqliteTable('tags', {
|
||||
id: text('id').primaryKey(),
|
||||
projectId: text('project_id').notNull(),
|
||||
name: text('name').notNull(),
|
||||
color: text('color'), // Optional hex color like #ff0000
|
||||
createdAt: integer('created_at', { mode: 'timestamp' }).notNull(),
|
||||
updatedAt: integer('updated_at', { mode: 'timestamp' }).notNull(),
|
||||
}, (table) => ({
|
||||
// Composite unique index: tag name must be unique within each project
|
||||
projectNameIdx: uniqueIndex('tags_project_name_idx').on(table.projectId, table.name),
|
||||
}));
|
||||
|
||||
// Types for TypeScript
|
||||
export type Project = typeof projects.$inferSelect;
|
||||
export type NewProject = typeof projects.$inferInsert;
|
||||
@@ -108,3 +121,5 @@ export type Setting = typeof settings.$inferSelect;
|
||||
export type NewSetting = typeof settings.$inferInsert;
|
||||
export type PostLink = typeof postLinks.$inferSelect;
|
||||
export type NewPostLink = typeof postLinks.$inferInsert;
|
||||
export type Tag = typeof tags.$inferSelect;
|
||||
export type NewTag = typeof tags.$inferInsert;
|
||||
|
||||
Reference in New Issue
Block a user