refactor: migrate to monorepo structure with Docker, PocketBase, and e2e tests
- Reorganize into apps/, packages/, docs/, e2e/, pocketbase/ directories - Add Dockerfiles for web, worker, and PocketBase services - Add docker-compose.yml for local orchestration - Add turbo.json for monorepo task management - Add Playwright e2e test infrastructure - Add PocketBase backend with migrations - Remove Vite/Next.js/ESLint/PostCSS config files - Update package.json with workspace dependencies - Add .env.example and .dockerignore
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
module.exports = {
|
||||
up: async (app) => {
|
||||
const collection = new app.models.Collection({
|
||||
name: 'domains',
|
||||
type: 'base',
|
||||
schema: app.models.NewSchema([
|
||||
app.models.NewSchemaField({
|
||||
name: 'name',
|
||||
type: 'text',
|
||||
required: true,
|
||||
options: { maxSize: 255 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'color',
|
||||
type: 'text',
|
||||
options: { maxSize: 255 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'icon',
|
||||
type: 'text',
|
||||
options: { maxSize: 255 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'sort_order',
|
||||
type: 'number',
|
||||
options: {}
|
||||
})
|
||||
]),
|
||||
indexes: [
|
||||
'CREATE UNIQUE INDEX idx_domains_name ON domains (name)'
|
||||
]
|
||||
});
|
||||
return app.save(collection);
|
||||
},
|
||||
down: async (app) => {
|
||||
const collection = await app.findCollectionByNameOrId('domains');
|
||||
return app.delete(collection);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
module.exports = {
|
||||
up: async (app) => {
|
||||
const collection = new app.models.Collection({
|
||||
name: 'tags',
|
||||
type: 'base',
|
||||
schema: app.models.NewSchema([
|
||||
app.models.NewSchemaField({
|
||||
name: 'name',
|
||||
type: 'text',
|
||||
required: true,
|
||||
options: { maxSize: 255 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'color',
|
||||
type: 'text',
|
||||
options: { maxSize: 255 }
|
||||
})
|
||||
]),
|
||||
indexes: [
|
||||
'CREATE UNIQUE INDEX idx_tags_name ON tags (name)'
|
||||
]
|
||||
});
|
||||
return app.save(collection);
|
||||
},
|
||||
down: async (app) => {
|
||||
const collection = await app.findCollectionByNameOrId('tags');
|
||||
return app.delete(collection);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,72 @@
|
||||
module.exports = {
|
||||
up: async (app) => {
|
||||
const collection = new app.models.Collection({
|
||||
name: 'projects',
|
||||
type: 'base',
|
||||
schema: app.models.NewSchema([
|
||||
app.models.NewSchemaField({
|
||||
name: 'name',
|
||||
type: 'text',
|
||||
required: true,
|
||||
options: { maxSize: 255 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'description',
|
||||
type: 'text',
|
||||
options: {}
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'color',
|
||||
type: 'text',
|
||||
options: { maxSize: 255 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'icon',
|
||||
type: 'text',
|
||||
options: { maxSize: 255 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'domain',
|
||||
type: 'relation',
|
||||
options: { collectionId: 'domains', maxSelect: 1, cascadeDelete: false }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'status',
|
||||
type: 'select',
|
||||
options: { values: ['active', 'paused', 'archived'], maxSelect: 1 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'tags',
|
||||
type: 'relation',
|
||||
options: { collectionId: 'tags', maxSelect: 10, cascadeDelete: false }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'goal',
|
||||
type: 'text',
|
||||
options: {}
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'deadline',
|
||||
type: 'date',
|
||||
options: {}
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'progress',
|
||||
type: 'number',
|
||||
options: { min: 0, max: 100 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'members',
|
||||
type: 'json',
|
||||
options: {}
|
||||
})
|
||||
]),
|
||||
indexes: []
|
||||
});
|
||||
return app.save(collection);
|
||||
},
|
||||
down: async (app) => {
|
||||
const collection = await app.findCollectionByNameOrId('projects');
|
||||
return app.delete(collection);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,79 @@
|
||||
module.exports = {
|
||||
up: async (app) => {
|
||||
const collection = new app.models.Collection({
|
||||
name: 'project_settings',
|
||||
type: 'base',
|
||||
schema: app.models.NewSchema([
|
||||
app.models.NewSchemaField({
|
||||
name: 'project_id',
|
||||
type: 'relation',
|
||||
required: true,
|
||||
options: { collectionId: 'projects', maxSelect: 1, cascadeDelete: true }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'default_priority',
|
||||
type: 'select',
|
||||
options: { values: ['low', 'medium', 'high', 'urgent'], maxSelect: 1 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'default_domain',
|
||||
type: 'relation',
|
||||
options: { collectionId: 'domains', maxSelect: 1, cascadeDelete: false }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'default_tags',
|
||||
type: 'relation',
|
||||
options: { collectionId: 'tags', maxSelect: 10, cascadeDelete: false }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'auto_archive_completed_after_days',
|
||||
type: 'number',
|
||||
options: {}
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'default_view',
|
||||
type: 'select',
|
||||
options: { values: ['kanban', 'list'], maxSelect: 1 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'habit_reminder_time_default',
|
||||
type: 'text',
|
||||
options: { maxSize: 255 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'pomodoro_focus_minutes',
|
||||
type: 'number',
|
||||
options: { min: 1, max: 120 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'pomodoro_break_minutes',
|
||||
type: 'number',
|
||||
options: { min: 1, max: 60 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'accent_color_override',
|
||||
type: 'text',
|
||||
options: { maxSize: 255 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'milestone_dependency_enforcement',
|
||||
type: 'bool',
|
||||
options: {}
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'custom_fields',
|
||||
type: 'json',
|
||||
options: {}
|
||||
})
|
||||
]),
|
||||
indexes: [
|
||||
'CREATE UNIQUE INDEX idx_project_settings_project_id ON project_settings (project_id)'
|
||||
]
|
||||
});
|
||||
return app.save(collection);
|
||||
},
|
||||
down: async (app) => {
|
||||
const collection = await app.findCollectionByNameOrId('project_settings');
|
||||
return app.delete(collection);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,50 @@
|
||||
module.exports = {
|
||||
up: async (app) => {
|
||||
const collection = new app.models.Collection({
|
||||
name: 'milestones',
|
||||
type: 'base',
|
||||
schema: app.models.NewSchema([
|
||||
app.models.NewSchemaField({
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
required: true,
|
||||
options: { maxSize: 255 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'description',
|
||||
type: 'text',
|
||||
options: {}
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'project_id',
|
||||
type: 'relation',
|
||||
required: true,
|
||||
options: { collectionId: 'projects', maxSelect: 1, cascadeDelete: true }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'status',
|
||||
type: 'select',
|
||||
options: { values: ['planned', 'in_progress', 'complete'], maxSelect: 1 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'due_date',
|
||||
type: 'date',
|
||||
options: {}
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'sort_order',
|
||||
type: 'number',
|
||||
options: {}
|
||||
})
|
||||
]),
|
||||
indexes: [
|
||||
'CREATE INDEX idx_milestones_project_id ON milestones (project_id)'
|
||||
]
|
||||
});
|
||||
return app.save(collection);
|
||||
},
|
||||
down: async (app) => {
|
||||
const collection = await app.findCollectionByNameOrId('milestones');
|
||||
return app.delete(collection);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
module.exports = {
|
||||
up: async (app) => {
|
||||
const collection = new app.models.Collection({
|
||||
name: 'milestone_dependencies',
|
||||
type: 'base',
|
||||
schema: app.models.NewSchema([
|
||||
app.models.NewSchemaField({
|
||||
name: 'milestone_id',
|
||||
type: 'relation',
|
||||
required: true,
|
||||
options: { collectionId: 'milestones', maxSelect: 1, cascadeDelete: true }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'depends_on_milestone_id',
|
||||
type: 'relation',
|
||||
required: true,
|
||||
options: { collectionId: 'milestones', maxSelect: 1, cascadeDelete: true }
|
||||
})
|
||||
]),
|
||||
indexes: [
|
||||
'CREATE INDEX idx_milestone_dependencies_milestone_id ON milestone_dependencies (milestone_id)',
|
||||
'CREATE INDEX idx_milestone_dependencies_depends_on ON milestone_dependencies (depends_on_milestone_id)'
|
||||
]
|
||||
});
|
||||
return app.save(collection);
|
||||
},
|
||||
down: async (app) => {
|
||||
const collection = await app.findCollectionByNameOrId('milestone_dependencies');
|
||||
return app.delete(collection);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
module.exports = {
|
||||
up: async (app) => {
|
||||
const collection = new app.models.Collection({
|
||||
name: 'milestone_templates',
|
||||
type: 'base',
|
||||
schema: app.models.NewSchema([
|
||||
app.models.NewSchemaField({
|
||||
name: 'name',
|
||||
type: 'text',
|
||||
required: true,
|
||||
options: { maxSize: 255 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'description',
|
||||
type: 'text',
|
||||
options: {}
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'milestones',
|
||||
type: 'json',
|
||||
options: {}
|
||||
})
|
||||
]),
|
||||
indexes: []
|
||||
});
|
||||
return app.save(collection);
|
||||
},
|
||||
down: async (app) => {
|
||||
const collection = await app.findCollectionByNameOrId('milestone_templates');
|
||||
return app.delete(collection);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,44 @@
|
||||
module.exports = {
|
||||
up: async (app) => {
|
||||
const collection = new app.models.Collection({
|
||||
name: 'milestone_history',
|
||||
type: 'base',
|
||||
schema: app.models.NewSchema([
|
||||
app.models.NewSchemaField({
|
||||
name: 'milestone_id',
|
||||
type: 'relation',
|
||||
required: true,
|
||||
options: { collectionId: 'milestones', maxSelect: 1, cascadeDelete: true }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'old_status',
|
||||
type: 'text',
|
||||
options: { maxSize: 255 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'new_status',
|
||||
type: 'text',
|
||||
options: { maxSize: 255 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'changed_by',
|
||||
type: 'text',
|
||||
options: { maxSize: 255 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'changed_at',
|
||||
type: 'date',
|
||||
options: {}
|
||||
})
|
||||
]),
|
||||
indexes: [
|
||||
'CREATE INDEX idx_milestone_history_milestone_id ON milestone_history (milestone_id)'
|
||||
]
|
||||
});
|
||||
return app.save(collection);
|
||||
},
|
||||
down: async (app) => {
|
||||
const collection = await app.findCollectionByNameOrId('milestone_history');
|
||||
return app.delete(collection);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,112 @@
|
||||
module.exports = {
|
||||
up: async (app) => {
|
||||
const collection = new app.models.Collection({
|
||||
name: 'tasks',
|
||||
type: 'base',
|
||||
schema: app.models.NewSchema([
|
||||
app.models.NewSchemaField({
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
required: true,
|
||||
options: { maxSize: 255 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'description',
|
||||
type: 'text',
|
||||
options: {}
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'status',
|
||||
type: 'select',
|
||||
options: { values: ['todo', 'in_progress', 'done', 'cancelled'], maxSelect: 1 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'priority',
|
||||
type: 'select',
|
||||
options: { values: ['low', 'medium', 'high', 'urgent'], maxSelect: 1 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'due_date',
|
||||
type: 'date',
|
||||
options: {}
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'project_id',
|
||||
type: 'relation',
|
||||
options: { collectionId: 'projects', maxSelect: 1, cascadeDelete: false }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'milestone_id',
|
||||
type: 'relation',
|
||||
options: { collectionId: 'milestones', maxSelect: 1, cascadeDelete: false }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'tags',
|
||||
type: 'relation',
|
||||
options: { collectionId: 'tags', maxSelect: 10, cascadeDelete: false }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'domain',
|
||||
type: 'relation',
|
||||
options: { collectionId: 'domains', maxSelect: 1, cascadeDelete: false }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'assignee',
|
||||
type: 'text',
|
||||
options: { maxSize: 255 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'estimate',
|
||||
type: 'number',
|
||||
options: { min: 0 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'time_spent',
|
||||
type: 'number',
|
||||
options: { min: 0 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'recurring_config',
|
||||
type: 'json',
|
||||
options: {}
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'attachments',
|
||||
type: 'json',
|
||||
options: {}
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'dependencies',
|
||||
type: 'json',
|
||||
options: {}
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'subtasks',
|
||||
type: 'json',
|
||||
options: {}
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'custom_fields',
|
||||
type: 'json',
|
||||
options: {}
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'completed_at',
|
||||
type: 'date',
|
||||
options: {}
|
||||
})
|
||||
]),
|
||||
indexes: [
|
||||
'CREATE INDEX idx_tasks_project_id ON tasks (project_id)',
|
||||
'CREATE INDEX idx_tasks_milestone_id ON tasks (milestone_id)',
|
||||
'CREATE INDEX idx_tasks_status ON tasks (status)',
|
||||
'CREATE INDEX idx_tasks_priority ON tasks (priority)'
|
||||
]
|
||||
});
|
||||
return app.save(collection);
|
||||
},
|
||||
down: async (app) => {
|
||||
const collection = await app.findCollectionByNameOrId('tasks');
|
||||
return app.delete(collection);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,40 @@
|
||||
module.exports = {
|
||||
up: async (app) => {
|
||||
const collection = new app.models.Collection({
|
||||
name: 'task_subtasks',
|
||||
type: 'base',
|
||||
schema: app.models.NewSchema([
|
||||
app.models.NewSchemaField({
|
||||
name: 'task_id',
|
||||
type: 'relation',
|
||||
required: true,
|
||||
options: { collectionId: 'tasks', maxSelect: 1, cascadeDelete: true }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
required: true,
|
||||
options: { maxSize: 255 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'done',
|
||||
type: 'bool',
|
||||
options: {}
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'sort_order',
|
||||
type: 'number',
|
||||
options: {}
|
||||
})
|
||||
]),
|
||||
indexes: [
|
||||
'CREATE INDEX idx_task_subtasks_task_id ON task_subtasks (task_id)'
|
||||
]
|
||||
});
|
||||
return app.save(collection);
|
||||
},
|
||||
down: async (app) => {
|
||||
const collection = await app.findCollectionByNameOrId('task_subtasks');
|
||||
return app.delete(collection);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
module.exports = {
|
||||
up: async (app) => {
|
||||
const collection = new app.models.Collection({
|
||||
name: 'task_dependencies',
|
||||
type: 'base',
|
||||
schema: app.models.NewSchema([
|
||||
app.models.NewSchemaField({
|
||||
name: 'task_id',
|
||||
type: 'relation',
|
||||
required: true,
|
||||
options: { collectionId: 'tasks', maxSelect: 1, cascadeDelete: true }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'depends_on_task_id',
|
||||
type: 'relation',
|
||||
required: true,
|
||||
options: { collectionId: 'tasks', maxSelect: 1, cascadeDelete: true }
|
||||
})
|
||||
]),
|
||||
indexes: [
|
||||
'CREATE INDEX idx_task_dependencies_task_id ON task_dependencies (task_id)',
|
||||
'CREATE INDEX idx_task_dependencies_depends_on ON task_dependencies (depends_on_task_id)'
|
||||
]
|
||||
});
|
||||
return app.save(collection);
|
||||
},
|
||||
down: async (app) => {
|
||||
const collection = await app.findCollectionByNameOrId('task_dependencies');
|
||||
return app.delete(collection);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,45 @@
|
||||
module.exports = {
|
||||
up: async (app) => {
|
||||
const collection = new app.models.Collection({
|
||||
name: 'task_attachments',
|
||||
type: 'base',
|
||||
schema: app.models.NewSchema([
|
||||
app.models.NewSchemaField({
|
||||
name: 'task_id',
|
||||
type: 'relation',
|
||||
required: true,
|
||||
options: { collectionId: 'tasks', maxSelect: 1, cascadeDelete: true }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'file',
|
||||
type: 'file',
|
||||
required: true,
|
||||
options: { maxSelect: 1, maxSize: 5242880, mimeTypes: [] }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'filename',
|
||||
type: 'text',
|
||||
options: { maxSize: 255 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'mime_type',
|
||||
type: 'text',
|
||||
options: { maxSize: 255 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'size',
|
||||
type: 'number',
|
||||
options: { min: 0 }
|
||||
})
|
||||
]),
|
||||
indexes: [
|
||||
'CREATE INDEX idx_task_attachments_task_id ON task_attachments (task_id)'
|
||||
]
|
||||
});
|
||||
return app.save(collection);
|
||||
},
|
||||
down: async (app) => {
|
||||
const collection = await app.findCollectionByNameOrId('task_attachments');
|
||||
return app.delete(collection);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,50 @@
|
||||
module.exports = {
|
||||
up: async (app) => {
|
||||
const collection = new app.models.Collection({
|
||||
name: 'task_time_entries',
|
||||
type: 'base',
|
||||
schema: app.models.NewSchema([
|
||||
app.models.NewSchemaField({
|
||||
name: 'task_id',
|
||||
type: 'relation',
|
||||
required: true,
|
||||
options: { collectionId: 'tasks', maxSelect: 1, cascadeDelete: true }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'duration',
|
||||
type: 'number',
|
||||
required: true,
|
||||
options: { min: 0 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'started_at',
|
||||
type: 'date',
|
||||
options: {}
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'ended_at',
|
||||
type: 'date',
|
||||
options: {}
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'source',
|
||||
type: 'select',
|
||||
options: { values: ['manual', 'timer', 'pomodoro'], maxSelect: 1 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'notes',
|
||||
type: 'text',
|
||||
options: {}
|
||||
})
|
||||
]),
|
||||
indexes: [
|
||||
'CREATE INDEX idx_task_time_entries_task_id ON task_time_entries (task_id)'
|
||||
]
|
||||
});
|
||||
return app.save(collection);
|
||||
},
|
||||
down: async (app) => {
|
||||
const collection = await app.findCollectionByNameOrId('task_time_entries');
|
||||
return app.delete(collection);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,119 @@
|
||||
module.exports = {
|
||||
up: async (app) => {
|
||||
const collection = new app.models.Collection({
|
||||
name: 'habits',
|
||||
type: 'base',
|
||||
schema: app.models.NewSchema([
|
||||
app.models.NewSchemaField({
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
required: true,
|
||||
options: { maxSize: 255 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'description',
|
||||
type: 'text',
|
||||
options: {}
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'frequency',
|
||||
type: 'select',
|
||||
options: { values: ['daily', 'weekly', 'custom'], maxSelect: 1 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'cron_expression',
|
||||
type: 'text',
|
||||
options: { maxSize: 255 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'target_count',
|
||||
type: 'number',
|
||||
options: { min: 1 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'streak_current',
|
||||
type: 'number',
|
||||
options: { min: 0 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'streak_best',
|
||||
type: 'number',
|
||||
options: { min: 0 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'last_completed_at',
|
||||
type: 'date',
|
||||
options: {}
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'project_id',
|
||||
type: 'relation',
|
||||
options: { collectionId: 'projects', maxSelect: 1, cascadeDelete: false }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'tags',
|
||||
type: 'relation',
|
||||
options: { collectionId: 'tags', maxSelect: 10, cascadeDelete: false }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'domain',
|
||||
type: 'relation',
|
||||
options: { collectionId: 'domains', maxSelect: 1, cascadeDelete: false }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'difficulty',
|
||||
type: 'select',
|
||||
options: { values: ['easy', 'medium', 'hard'], maxSelect: 1 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'mood_tracking',
|
||||
type: 'bool',
|
||||
options: {}
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'reminder_times',
|
||||
type: 'json',
|
||||
options: {}
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'skip_days',
|
||||
type: 'json',
|
||||
options: {}
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'unit',
|
||||
type: 'text',
|
||||
options: { maxSize: 255 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'goal_per_period',
|
||||
type: 'number',
|
||||
options: { min: 0 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'score',
|
||||
type: 'number',
|
||||
options: { min: 0 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'score_config',
|
||||
type: 'json',
|
||||
options: {}
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'completion_mode',
|
||||
type: 'select',
|
||||
options: { values: ['quick', 'detailed'], maxSelect: 1 }
|
||||
})
|
||||
]),
|
||||
indexes: [
|
||||
'CREATE INDEX idx_habits_project_id ON habits (project_id)'
|
||||
]
|
||||
});
|
||||
return app.save(collection);
|
||||
},
|
||||
down: async (app) => {
|
||||
const collection = await app.findCollectionByNameOrId('habits');
|
||||
return app.delete(collection);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,46 @@
|
||||
module.exports = {
|
||||
up: async (app) => {
|
||||
const collection = new app.models.Collection({
|
||||
name: 'habit_logs',
|
||||
type: 'base',
|
||||
schema: app.models.NewSchema([
|
||||
app.models.NewSchemaField({
|
||||
name: 'habit_id',
|
||||
type: 'relation',
|
||||
required: true,
|
||||
options: { collectionId: 'habits', maxSelect: 1, cascadeDelete: true }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'date',
|
||||
type: 'date',
|
||||
required: true,
|
||||
options: {}
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'mood',
|
||||
type: 'text',
|
||||
options: { maxSize: 255 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'quantity',
|
||||
type: 'number',
|
||||
options: { min: 0 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'notes',
|
||||
type: 'text',
|
||||
options: {}
|
||||
})
|
||||
]),
|
||||
indexes: [
|
||||
'CREATE INDEX idx_habit_logs_habit_id ON habit_logs (habit_id)',
|
||||
'CREATE INDEX idx_habit_logs_date ON habit_logs (date)'
|
||||
]
|
||||
});
|
||||
return app.save(collection);
|
||||
},
|
||||
down: async (app) => {
|
||||
const collection = await app.findCollectionByNameOrId('habit_logs');
|
||||
return app.delete(collection);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,36 @@
|
||||
module.exports = {
|
||||
up: async (app) => {
|
||||
const collection = new app.models.Collection({
|
||||
name: 'habit_skip_days',
|
||||
type: 'base',
|
||||
schema: app.models.NewSchema([
|
||||
app.models.NewSchemaField({
|
||||
name: 'habit_id',
|
||||
type: 'relation',
|
||||
required: true,
|
||||
options: { collectionId: 'habits', maxSelect: 1, cascadeDelete: true }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'date',
|
||||
type: 'date',
|
||||
required: true,
|
||||
options: {}
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'reason',
|
||||
type: 'text',
|
||||
options: {}
|
||||
})
|
||||
]),
|
||||
indexes: [
|
||||
'CREATE INDEX idx_habit_skip_days_habit_id ON habit_skip_days (habit_id)',
|
||||
'CREATE INDEX idx_habit_skip_days_date ON habit_skip_days (date)'
|
||||
]
|
||||
});
|
||||
return app.save(collection);
|
||||
},
|
||||
down: async (app) => {
|
||||
const collection = await app.findCollectionByNameOrId('habit_skip_days');
|
||||
return app.delete(collection);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,72 @@
|
||||
module.exports = {
|
||||
up: async (app) => {
|
||||
const collection = new app.models.Collection({
|
||||
name: 'notes',
|
||||
type: 'base',
|
||||
schema: app.models.NewSchema([
|
||||
app.models.NewSchemaField({
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
required: true,
|
||||
options: { maxSize: 255 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'content',
|
||||
type: 'text',
|
||||
options: {}
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'domain',
|
||||
type: 'relation',
|
||||
options: { collectionId: 'domains', maxSelect: 1, cascadeDelete: false }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'tags',
|
||||
type: 'relation',
|
||||
options: { collectionId: 'tags', maxSelect: 10, cascadeDelete: false }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'linked_entities',
|
||||
type: 'json',
|
||||
options: {}
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'created_by',
|
||||
type: 'text',
|
||||
options: { maxSize: 255 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'ai_generated',
|
||||
type: 'bool',
|
||||
options: {}
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'bookmarked',
|
||||
type: 'bool',
|
||||
options: {}
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'pinned',
|
||||
type: 'bool',
|
||||
options: {}
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'frontmatter',
|
||||
type: 'json',
|
||||
options: {}
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'wikilinks',
|
||||
type: 'json',
|
||||
options: {}
|
||||
})
|
||||
]),
|
||||
indexes: []
|
||||
});
|
||||
return app.save(collection);
|
||||
},
|
||||
down: async (app) => {
|
||||
const collection = await app.findCollectionByNameOrId('notes');
|
||||
return app.delete(collection);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,36 @@
|
||||
module.exports = {
|
||||
up: async (app) => {
|
||||
const collection = new app.models.Collection({
|
||||
name: 'note_links',
|
||||
type: 'base',
|
||||
schema: app.models.NewSchema([
|
||||
app.models.NewSchemaField({
|
||||
name: 'source_note_id',
|
||||
type: 'relation',
|
||||
required: true,
|
||||
options: { collectionId: 'notes', maxSelect: 1, cascadeDelete: true }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'target_note_id',
|
||||
type: 'relation',
|
||||
required: true,
|
||||
options: { collectionId: 'notes', maxSelect: 1, cascadeDelete: true }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'link_text',
|
||||
type: 'text',
|
||||
options: { maxSize: 255 }
|
||||
})
|
||||
]),
|
||||
indexes: [
|
||||
'CREATE INDEX idx_note_links_source ON note_links (source_note_id)',
|
||||
'CREATE INDEX idx_note_links_target ON note_links (target_note_id)'
|
||||
]
|
||||
});
|
||||
return app.save(collection);
|
||||
},
|
||||
down: async (app) => {
|
||||
const collection = await app.findCollectionByNameOrId('note_links');
|
||||
return app.delete(collection);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,36 @@
|
||||
module.exports = {
|
||||
up: async (app) => {
|
||||
const collection = new app.models.Collection({
|
||||
name: 'note_task_links',
|
||||
type: 'base',
|
||||
schema: app.models.NewSchema([
|
||||
app.models.NewSchemaField({
|
||||
name: 'note_id',
|
||||
type: 'relation',
|
||||
required: true,
|
||||
options: { collectionId: 'notes', maxSelect: 1, cascadeDelete: true }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'task_id',
|
||||
type: 'relation',
|
||||
required: true,
|
||||
options: { collectionId: 'tasks', maxSelect: 1, cascadeDelete: true }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'checkbox_position',
|
||||
type: 'number',
|
||||
options: {}
|
||||
})
|
||||
]),
|
||||
indexes: [
|
||||
'CREATE INDEX idx_note_task_links_note_id ON note_task_links (note_id)',
|
||||
'CREATE INDEX idx_note_task_links_task_id ON note_task_links (task_id)'
|
||||
]
|
||||
});
|
||||
return app.save(collection);
|
||||
},
|
||||
down: async (app) => {
|
||||
const collection = await app.findCollectionByNameOrId('note_task_links');
|
||||
return app.delete(collection);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,42 @@
|
||||
module.exports = {
|
||||
up: async (app) => {
|
||||
const collection = new app.models.Collection({
|
||||
name: 'report_templates',
|
||||
type: 'base',
|
||||
schema: app.models.NewSchema([
|
||||
app.models.NewSchemaField({
|
||||
name: 'name',
|
||||
type: 'text',
|
||||
required: true,
|
||||
options: { maxSize: 255 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'description',
|
||||
type: 'text',
|
||||
options: {}
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'report_type',
|
||||
type: 'select',
|
||||
options: { values: ['weekly', 'monthly', 'project', 'habit', 'custom'], maxSelect: 1 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'content_template',
|
||||
type: 'text',
|
||||
options: {}
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'is_builtin',
|
||||
type: 'bool',
|
||||
options: {}
|
||||
})
|
||||
]),
|
||||
indexes: []
|
||||
});
|
||||
return app.save(collection);
|
||||
},
|
||||
down: async (app) => {
|
||||
const collection = await app.findCollectionByNameOrId('report_templates');
|
||||
return app.delete(collection);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,72 @@
|
||||
module.exports = {
|
||||
up: async (app) => {
|
||||
const collection = new app.models.Collection({
|
||||
name: 'reports',
|
||||
type: 'base',
|
||||
schema: app.models.NewSchema([
|
||||
app.models.NewSchemaField({
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
required: true,
|
||||
options: { maxSize: 255 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'content',
|
||||
type: 'text',
|
||||
options: {}
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'report_type',
|
||||
type: 'select',
|
||||
options: { values: ['weekly', 'monthly', 'project', 'habit', 'custom'], maxSelect: 1 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'date_range_start',
|
||||
type: 'date',
|
||||
options: {}
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'date_range_end',
|
||||
type: 'date',
|
||||
options: {}
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'generated_by',
|
||||
type: 'text',
|
||||
options: { maxSize: 255 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'ai_generated',
|
||||
type: 'bool',
|
||||
options: {}
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'linked_entities',
|
||||
type: 'json',
|
||||
options: {}
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'domain',
|
||||
type: 'relation',
|
||||
options: { collectionId: 'domains', maxSelect: 1, cascadeDelete: false }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'tags',
|
||||
type: 'relation',
|
||||
options: { collectionId: 'tags', maxSelect: 10, cascadeDelete: false }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'template_id',
|
||||
type: 'relation',
|
||||
options: { collectionId: 'report_templates', maxSelect: 1, cascadeDelete: false }
|
||||
})
|
||||
]),
|
||||
indexes: []
|
||||
});
|
||||
return app.save(collection);
|
||||
},
|
||||
down: async (app) => {
|
||||
const collection = await app.findCollectionByNameOrId('reports');
|
||||
return app.delete(collection);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,39 @@
|
||||
module.exports = {
|
||||
up: async (app) => {
|
||||
const collection = new app.models.Collection({
|
||||
name: 'canvases',
|
||||
type: 'base',
|
||||
schema: app.models.NewSchema([
|
||||
app.models.NewSchemaField({
|
||||
name: 'name',
|
||||
type: 'text',
|
||||
required: true,
|
||||
options: { maxSize: 255 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'mode',
|
||||
type: 'select',
|
||||
options: { values: ['freeform', 'graph'], maxSelect: 1 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'project_id',
|
||||
type: 'relation',
|
||||
options: { collectionId: 'projects', maxSelect: 1, cascadeDelete: false }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'state',
|
||||
type: 'json',
|
||||
options: {}
|
||||
})
|
||||
]),
|
||||
indexes: [
|
||||
'CREATE INDEX idx_canvases_project_id ON canvases (project_id)'
|
||||
]
|
||||
});
|
||||
return app.save(collection);
|
||||
},
|
||||
down: async (app) => {
|
||||
const collection = await app.findCollectionByNameOrId('canvases');
|
||||
return app.delete(collection);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,64 @@
|
||||
module.exports = {
|
||||
up: async (app) => {
|
||||
const collection = new app.models.Collection({
|
||||
name: 'canvas_cards',
|
||||
type: 'base',
|
||||
schema: app.models.NewSchema([
|
||||
app.models.NewSchemaField({
|
||||
name: 'canvas_id',
|
||||
type: 'relation',
|
||||
required: true,
|
||||
options: { collectionId: 'canvases', maxSelect: 1, cascadeDelete: true }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'card_type',
|
||||
type: 'select',
|
||||
options: { values: ['note', 'task', 'image', 'entity'], maxSelect: 1 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'entity_id',
|
||||
type: 'text',
|
||||
options: { maxSize: 255 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'entity_type',
|
||||
type: 'text',
|
||||
options: { maxSize: 255 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'position_x',
|
||||
type: 'number',
|
||||
options: {}
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'position_y',
|
||||
type: 'number',
|
||||
options: {}
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'width',
|
||||
type: 'number',
|
||||
options: { min: 1 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'height',
|
||||
type: 'number',
|
||||
options: { min: 1 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'content',
|
||||
type: 'json',
|
||||
options: {}
|
||||
})
|
||||
]),
|
||||
indexes: [
|
||||
'CREATE INDEX idx_canvas_cards_canvas_id ON canvas_cards (canvas_id)'
|
||||
]
|
||||
});
|
||||
return app.save(collection);
|
||||
},
|
||||
down: async (app) => {
|
||||
const collection = await app.findCollectionByNameOrId('canvas_cards');
|
||||
return app.delete(collection);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,65 @@
|
||||
module.exports = {
|
||||
up: async (app) => {
|
||||
const collection = new app.models.Collection({
|
||||
name: 'agents',
|
||||
type: 'base',
|
||||
schema: app.models.NewSchema([
|
||||
app.models.NewSchemaField({
|
||||
name: 'name',
|
||||
type: 'text',
|
||||
required: true,
|
||||
options: { maxSize: 255 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'api_key',
|
||||
type: 'text',
|
||||
required: true,
|
||||
options: { maxSize: 255 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'avatar',
|
||||
type: 'text',
|
||||
options: { maxSize: 255 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'description',
|
||||
type: 'text',
|
||||
options: {}
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'permission_tier',
|
||||
type: 'select',
|
||||
options: { values: ['full_access', 'read_only', 'content_creator', 'task_manager', 'custom'], maxSelect: 1 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'custom_permissions',
|
||||
type: 'json',
|
||||
options: {}
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'webhook_url',
|
||||
type: 'text',
|
||||
options: { maxSize: 255 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'last_activity_at',
|
||||
type: 'date',
|
||||
options: {}
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'status',
|
||||
type: 'select',
|
||||
options: { values: ['active', 'disabled'], maxSelect: 1 }
|
||||
})
|
||||
]),
|
||||
indexes: [
|
||||
'CREATE UNIQUE INDEX idx_agents_api_key ON agents (api_key)'
|
||||
]
|
||||
});
|
||||
return app.save(collection);
|
||||
},
|
||||
down: async (app) => {
|
||||
const collection = await app.findCollectionByNameOrId('agents');
|
||||
return app.delete(collection);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,59 @@
|
||||
module.exports = {
|
||||
up: async (app) => {
|
||||
const collection = new app.models.Collection({
|
||||
name: 'agent_activity',
|
||||
type: 'base',
|
||||
schema: app.models.NewSchema([
|
||||
app.models.NewSchemaField({
|
||||
name: 'agent_id',
|
||||
type: 'relation',
|
||||
required: true,
|
||||
options: { collectionId: 'agents', maxSelect: 1, cascadeDelete: true }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'action',
|
||||
type: 'text',
|
||||
options: { maxSize: 255 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'entity_type',
|
||||
type: 'text',
|
||||
options: { maxSize: 255 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'entity_id',
|
||||
type: 'text',
|
||||
options: { maxSize: 255 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'before_state',
|
||||
type: 'json',
|
||||
options: {}
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'after_state',
|
||||
type: 'json',
|
||||
options: {}
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'prompt',
|
||||
type: 'text',
|
||||
options: {}
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'response',
|
||||
type: 'text',
|
||||
options: {}
|
||||
})
|
||||
]),
|
||||
indexes: [
|
||||
'CREATE INDEX idx_agent_activity_agent_id ON agent_activity (agent_id)'
|
||||
]
|
||||
});
|
||||
return app.save(collection);
|
||||
},
|
||||
down: async (app) => {
|
||||
const collection = await app.findCollectionByNameOrId('agent_activity');
|
||||
return app.delete(collection);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,43 @@
|
||||
module.exports = {
|
||||
up: async (app) => {
|
||||
const collection = new app.models.Collection({
|
||||
name: 'webhooks',
|
||||
type: 'base',
|
||||
schema: app.models.NewSchema([
|
||||
app.models.NewSchemaField({
|
||||
name: 'name',
|
||||
type: 'text',
|
||||
required: true,
|
||||
options: { maxSize: 255 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'url',
|
||||
type: 'text',
|
||||
required: true,
|
||||
options: { maxSize: 255 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'events',
|
||||
type: 'json',
|
||||
options: {}
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'secret',
|
||||
type: 'text',
|
||||
options: { maxSize: 255 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'active',
|
||||
type: 'bool',
|
||||
options: {}
|
||||
})
|
||||
]),
|
||||
indexes: []
|
||||
});
|
||||
return app.save(collection);
|
||||
},
|
||||
down: async (app) => {
|
||||
const collection = await app.findCollectionByNameOrId('webhooks');
|
||||
return app.delete(collection);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,54 @@
|
||||
module.exports = {
|
||||
up: async (app) => {
|
||||
const collection = new app.models.Collection({
|
||||
name: 'webhook_deliveries',
|
||||
type: 'base',
|
||||
schema: app.models.NewSchema([
|
||||
app.models.NewSchemaField({
|
||||
name: 'webhook_id',
|
||||
type: 'relation',
|
||||
required: true,
|
||||
options: { collectionId: 'webhooks', maxSelect: 1, cascadeDelete: true }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'event_type',
|
||||
type: 'text',
|
||||
options: { maxSize: 255 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'payload',
|
||||
type: 'json',
|
||||
options: {}
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'response_status',
|
||||
type: 'number',
|
||||
options: {}
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'response_body',
|
||||
type: 'text',
|
||||
options: {}
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'success',
|
||||
type: 'bool',
|
||||
options: {}
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'attempts',
|
||||
type: 'number',
|
||||
options: { min: 0 }
|
||||
})
|
||||
]),
|
||||
indexes: [
|
||||
'CREATE INDEX idx_webhook_deliveries_webhook_id ON webhook_deliveries (webhook_id)'
|
||||
]
|
||||
});
|
||||
return app.save(collection);
|
||||
},
|
||||
down: async (app) => {
|
||||
const collection = await app.findCollectionByNameOrId('webhook_deliveries');
|
||||
return app.delete(collection);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,50 @@
|
||||
module.exports = {
|
||||
up: async (app) => {
|
||||
const collection = new app.models.Collection({
|
||||
name: 'agent_tasks',
|
||||
type: 'base',
|
||||
schema: app.models.NewSchema([
|
||||
app.models.NewSchemaField({
|
||||
name: 'agent_id',
|
||||
type: 'relation',
|
||||
required: true,
|
||||
options: { collectionId: 'agents', maxSelect: 1, cascadeDelete: true }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'entity_type',
|
||||
type: 'text',
|
||||
options: { maxSize: 255 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'entity_id',
|
||||
type: 'text',
|
||||
options: { maxSize: 255 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'instruction',
|
||||
type: 'text',
|
||||
options: {}
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'status',
|
||||
type: 'select',
|
||||
options: { values: ['pending', 'in_progress', 'completed', 'failed', 'delivery_failed'], maxSelect: 1 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'result',
|
||||
type: 'json',
|
||||
options: {}
|
||||
})
|
||||
]),
|
||||
indexes: [
|
||||
'CREATE INDEX idx_agent_tasks_agent_id ON agent_tasks (agent_id)',
|
||||
'CREATE INDEX idx_agent_tasks_status ON agent_tasks (status)'
|
||||
]
|
||||
});
|
||||
return app.save(collection);
|
||||
},
|
||||
down: async (app) => {
|
||||
const collection = await app.findCollectionByNameOrId('agent_tasks');
|
||||
return app.delete(collection);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,49 @@
|
||||
module.exports = {
|
||||
up: async (app) => {
|
||||
const collection = new app.models.Collection({
|
||||
name: 'notifications',
|
||||
type: 'base',
|
||||
schema: app.models.NewSchema([
|
||||
app.models.NewSchemaField({
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
required: true,
|
||||
options: { maxSize: 255 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'message',
|
||||
type: 'text',
|
||||
options: {}
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'type',
|
||||
type: 'select',
|
||||
options: { values: ['info', 'success', 'warning', 'error'], maxSelect: 1 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'entity_type',
|
||||
type: 'text',
|
||||
options: { maxSize: 255 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'entity_id',
|
||||
type: 'text',
|
||||
options: { maxSize: 255 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'read',
|
||||
type: 'bool',
|
||||
options: {}
|
||||
})
|
||||
]),
|
||||
indexes: [
|
||||
'CREATE INDEX idx_notifications_read ON notifications (read)'
|
||||
]
|
||||
});
|
||||
return app.save(collection);
|
||||
},
|
||||
down: async (app) => {
|
||||
const collection = await app.findCollectionByNameOrId('notifications');
|
||||
return app.delete(collection);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,44 @@
|
||||
module.exports = {
|
||||
up: async (app) => {
|
||||
const collection = new app.models.Collection({
|
||||
name: 'error_logs',
|
||||
type: 'base',
|
||||
schema: app.models.NewSchema([
|
||||
app.models.NewSchemaField({
|
||||
name: 'code',
|
||||
type: 'text',
|
||||
options: { maxSize: 255 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'message',
|
||||
type: 'text',
|
||||
options: {}
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'details',
|
||||
type: 'json',
|
||||
options: {}
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'stack_trace',
|
||||
type: 'text',
|
||||
options: {}
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'source',
|
||||
type: 'text',
|
||||
options: { maxSize: 255 }
|
||||
})
|
||||
]),
|
||||
indexes: [
|
||||
'CREATE INDEX idx_error_logs_code ON error_logs (code)',
|
||||
'CREATE INDEX idx_error_logs_source ON error_logs (source)'
|
||||
]
|
||||
});
|
||||
return app.save(collection);
|
||||
},
|
||||
down: async (app) => {
|
||||
const collection = await app.findCollectionByNameOrId('error_logs');
|
||||
return app.delete(collection);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,66 @@
|
||||
module.exports = {
|
||||
up: async (app) => {
|
||||
const collection = new app.models.Collection({
|
||||
name: 'queue_jobs',
|
||||
type: 'base',
|
||||
schema: app.models.NewSchema([
|
||||
app.models.NewSchemaField({
|
||||
name: 'job_type',
|
||||
type: 'text',
|
||||
required: true,
|
||||
options: { maxSize: 255 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'payload',
|
||||
type: 'json',
|
||||
options: {}
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'status',
|
||||
type: 'select',
|
||||
options: { values: ['pending', 'in_progress', 'completed', 'failed'], maxSelect: 1 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'attempts',
|
||||
type: 'number',
|
||||
options: { min: 0 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'max_attempts',
|
||||
type: 'number',
|
||||
options: { min: 1 }
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'error',
|
||||
type: 'text',
|
||||
options: {}
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'scheduled_at',
|
||||
type: 'date',
|
||||
options: {}
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'started_at',
|
||||
type: 'date',
|
||||
options: {}
|
||||
}),
|
||||
app.models.NewSchemaField({
|
||||
name: 'completed_at',
|
||||
type: 'date',
|
||||
options: {}
|
||||
})
|
||||
]),
|
||||
indexes: [
|
||||
'CREATE INDEX idx_queue_jobs_status ON queue_jobs (status)',
|
||||
'CREATE INDEX idx_queue_jobs_scheduled_at ON queue_jobs (scheduled_at)',
|
||||
'CREATE INDEX idx_queue_jobs_job_type ON queue_jobs (job_type)'
|
||||
]
|
||||
});
|
||||
return app.save(collection);
|
||||
},
|
||||
down: async (app) => {
|
||||
const collection = await app.findCollectionByNameOrId('queue_jobs');
|
||||
return app.delete(collection);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,498 @@
|
||||
// pocketbase/schema.ts
|
||||
// TypeScript type definitions for PocketBase collections
|
||||
// Used by API layer for type safety
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Helper types
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export interface RecurringConfig {
|
||||
/** RRULE string */
|
||||
rule: string;
|
||||
next_due?: string;
|
||||
}
|
||||
|
||||
export interface Attachment {
|
||||
id: string;
|
||||
filename: string;
|
||||
mime_type: string;
|
||||
size: number;
|
||||
url: string;
|
||||
}
|
||||
|
||||
export interface Subtask {
|
||||
id: string;
|
||||
title: string;
|
||||
done: boolean;
|
||||
sort_order: number;
|
||||
}
|
||||
|
||||
/** Base PocketBase record fields present on every collection row. */
|
||||
export interface BaseRecord {
|
||||
id: string;
|
||||
created: string;
|
||||
updated: string;
|
||||
collectionId: string;
|
||||
collectionName: string;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// System collections
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export interface Domain extends BaseRecord {
|
||||
name: string;
|
||||
color: string;
|
||||
icon: string;
|
||||
sort_order: number;
|
||||
}
|
||||
|
||||
export interface Tag extends BaseRecord {
|
||||
name: string;
|
||||
color?: string;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Project collections
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export interface Project extends BaseRecord {
|
||||
name: string;
|
||||
description?: string;
|
||||
color?: string;
|
||||
icon?: string;
|
||||
/** Relation → domains */
|
||||
domain: string;
|
||||
status: 'active' | 'paused' | 'archived';
|
||||
/** Relation → tags (multiple) */
|
||||
tags: string[];
|
||||
goal?: string;
|
||||
deadline?: string;
|
||||
progress: number;
|
||||
members?: unknown[];
|
||||
}
|
||||
|
||||
export interface ProjectSettings extends BaseRecord {
|
||||
/** Relation → projects */
|
||||
project_id: string;
|
||||
default_priority?: 'low' | 'medium' | 'high' | 'urgent';
|
||||
/** Relation → domains */
|
||||
default_domain?: string;
|
||||
/** Relation → tags (multiple) */
|
||||
default_tags?: string[];
|
||||
auto_archive_completed_after_days?: number;
|
||||
default_view?: 'kanban' | 'list';
|
||||
habit_reminder_time_default?: string;
|
||||
pomodoro_focus_minutes: number;
|
||||
pomodoro_break_minutes: number;
|
||||
accent_color_override?: string;
|
||||
milestone_dependency_enforcement: boolean;
|
||||
custom_fields?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export interface Milestone extends BaseRecord {
|
||||
title: string;
|
||||
description?: string;
|
||||
/** Relation → projects */
|
||||
project_id: string;
|
||||
status: 'planned' | 'in_progress' | 'complete';
|
||||
due_date?: string;
|
||||
sort_order: number;
|
||||
}
|
||||
|
||||
export interface MilestoneDependency extends BaseRecord {
|
||||
/** Relation → milestones */
|
||||
milestone_id: string;
|
||||
/** Relation → milestones */
|
||||
depends_on_milestone_id: string;
|
||||
}
|
||||
|
||||
export interface MilestoneTemplate extends BaseRecord {
|
||||
name: string;
|
||||
description?: string;
|
||||
milestones: unknown[];
|
||||
}
|
||||
|
||||
export interface MilestoneHistory extends BaseRecord {
|
||||
/** Relation → milestones */
|
||||
milestone_id: string;
|
||||
old_status: string;
|
||||
new_status: string;
|
||||
changed_by: string;
|
||||
changed_at: string;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Task collections
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export interface Task extends BaseRecord {
|
||||
title: string;
|
||||
description?: string;
|
||||
status: 'todo' | 'in_progress' | 'done' | 'cancelled';
|
||||
priority: 'low' | 'medium' | 'high' | 'urgent';
|
||||
due_date?: string;
|
||||
/** Relation → projects */
|
||||
project_id?: string;
|
||||
/** Relation → milestones */
|
||||
milestone_id?: string;
|
||||
/** Relation → tags (multiple) */
|
||||
tags: string[];
|
||||
/** Relation → domains */
|
||||
domain: string;
|
||||
assignee?: string;
|
||||
estimate?: number;
|
||||
time_spent: number;
|
||||
recurring_config?: RecurringConfig;
|
||||
attachments: Attachment[];
|
||||
dependencies: string[];
|
||||
subtasks: Subtask[];
|
||||
custom_fields?: Record<string, unknown>;
|
||||
completed_at?: string;
|
||||
}
|
||||
|
||||
export interface TaskSubtask extends BaseRecord {
|
||||
/** Relation → tasks */
|
||||
task_id: string;
|
||||
title: string;
|
||||
done: boolean;
|
||||
sort_order: number;
|
||||
}
|
||||
|
||||
export interface TaskDependency extends BaseRecord {
|
||||
/** Relation → tasks */
|
||||
task_id: string;
|
||||
/** Relation → tasks */
|
||||
depends_on_task_id: string;
|
||||
}
|
||||
|
||||
export interface TaskAttachment extends BaseRecord {
|
||||
/** Relation → tasks */
|
||||
task_id: string;
|
||||
/** File reference */
|
||||
file: string;
|
||||
filename?: string;
|
||||
mime_type?: string;
|
||||
size?: number;
|
||||
}
|
||||
|
||||
export interface TaskTimeEntry extends BaseRecord {
|
||||
/** Relation → tasks */
|
||||
task_id: string;
|
||||
duration: number;
|
||||
started_at?: string;
|
||||
ended_at?: string;
|
||||
source: 'manual' | 'timer' | 'pomodoro';
|
||||
notes?: string;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Habit collections
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export interface Habit extends BaseRecord {
|
||||
title: string;
|
||||
description?: string;
|
||||
frequency: 'daily' | 'weekly' | 'custom';
|
||||
cron_expression?: string;
|
||||
target_count: number;
|
||||
streak_current: number;
|
||||
streak_best: number;
|
||||
last_completed_at?: string;
|
||||
/** Relation → projects */
|
||||
project_id?: string;
|
||||
/** Relation → tags (multiple) */
|
||||
tags: string[];
|
||||
/** Relation → domains */
|
||||
domain: string;
|
||||
difficulty: 'easy' | 'medium' | 'hard';
|
||||
mood_tracking: boolean;
|
||||
reminder_times?: string[];
|
||||
skip_days?: string[];
|
||||
unit?: string;
|
||||
goal_per_period?: number;
|
||||
score: number;
|
||||
score_config?: Record<string, unknown>;
|
||||
completion_mode: 'quick' | 'detailed';
|
||||
}
|
||||
|
||||
export interface HabitLog extends BaseRecord {
|
||||
/** Relation → habits */
|
||||
habit_id: string;
|
||||
date: string;
|
||||
mood?: string;
|
||||
quantity?: number;
|
||||
notes?: string;
|
||||
}
|
||||
|
||||
export interface HabitSkipDay extends BaseRecord {
|
||||
/** Relation → habits */
|
||||
habit_id: string;
|
||||
date: string;
|
||||
reason?: string;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Note collections
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export interface Note extends BaseRecord {
|
||||
title: string;
|
||||
content?: string;
|
||||
/** Relation → domains */
|
||||
domain: string;
|
||||
/** Relation → tags (multiple) */
|
||||
tags: string[];
|
||||
linked_entities?: unknown[];
|
||||
created_by?: string;
|
||||
ai_generated: boolean;
|
||||
bookmarked: boolean;
|
||||
pinned: boolean;
|
||||
frontmatter?: Record<string, unknown>;
|
||||
wikilinks?: string[];
|
||||
}
|
||||
|
||||
export interface NoteLink extends BaseRecord {
|
||||
/** Relation → notes */
|
||||
source_note_id: string;
|
||||
/** Relation → notes */
|
||||
target_note_id: string;
|
||||
link_text?: string;
|
||||
}
|
||||
|
||||
export interface NoteTaskLink extends BaseRecord {
|
||||
/** Relation → notes */
|
||||
note_id: string;
|
||||
/** Relation → tasks */
|
||||
task_id: string;
|
||||
checkbox_position: number;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Report collections
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export interface ReportTemplate extends BaseRecord {
|
||||
name: string;
|
||||
description?: string;
|
||||
report_type: 'weekly' | 'monthly' | 'project' | 'habit' | 'custom';
|
||||
content_template?: string;
|
||||
is_builtin: boolean;
|
||||
}
|
||||
|
||||
export interface Report extends BaseRecord {
|
||||
title: string;
|
||||
content?: string;
|
||||
report_type: 'weekly' | 'monthly' | 'project' | 'habit' | 'custom';
|
||||
date_range_start?: string;
|
||||
date_range_end?: string;
|
||||
generated_by?: string;
|
||||
ai_generated: boolean;
|
||||
linked_entities?: unknown[];
|
||||
/** Relation → domains */
|
||||
domain: string;
|
||||
/** Relation → tags (multiple) */
|
||||
tags: string[];
|
||||
/** Relation → report_templates */
|
||||
template_id?: string;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Canvas collections
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export interface Canvas extends BaseRecord {
|
||||
name: string;
|
||||
mode: 'freeform' | 'graph';
|
||||
/** Relation → projects */
|
||||
project_id?: string;
|
||||
state?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export interface CanvasCard extends BaseRecord {
|
||||
/** Relation → canvases */
|
||||
canvas_id: string;
|
||||
card_type: 'note' | 'task' | 'image' | 'entity';
|
||||
entity_id?: string;
|
||||
entity_type?: string;
|
||||
position_x: number;
|
||||
position_y: number;
|
||||
width: number;
|
||||
height: number;
|
||||
content?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Agent collections
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export interface Agent extends BaseRecord {
|
||||
name: string;
|
||||
api_key: string;
|
||||
avatar?: string;
|
||||
description?: string;
|
||||
permission_tier:
|
||||
| 'full_access'
|
||||
| 'read_only'
|
||||
| 'content_creator'
|
||||
| 'task_manager'
|
||||
| 'custom';
|
||||
custom_permissions?: Record<string, unknown>;
|
||||
webhook_url?: string;
|
||||
last_activity_at?: string;
|
||||
status: 'active' | 'disabled';
|
||||
}
|
||||
|
||||
export interface AgentActivity extends BaseRecord {
|
||||
/** Relation → agents */
|
||||
agent_id: string;
|
||||
action: string;
|
||||
entity_type: string;
|
||||
entity_id: string;
|
||||
before_state?: Record<string, unknown>;
|
||||
after_state?: Record<string, unknown>;
|
||||
prompt?: string;
|
||||
response?: string;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Webhook collections
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export interface Webhook extends BaseRecord {
|
||||
name: string;
|
||||
url: string;
|
||||
events: string[];
|
||||
secret?: string;
|
||||
active: boolean;
|
||||
}
|
||||
|
||||
export interface WebhookDelivery extends BaseRecord {
|
||||
/** Relation → webhooks */
|
||||
webhook_id: string;
|
||||
event_type: string;
|
||||
payload?: Record<string, unknown>;
|
||||
response_status?: number;
|
||||
response_body?: string;
|
||||
success: boolean;
|
||||
attempts: number;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Agent task collection
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export interface AgentTask extends BaseRecord {
|
||||
/** Relation → agents */
|
||||
agent_id: string;
|
||||
entity_type: string;
|
||||
entity_id: string;
|
||||
instruction: string;
|
||||
status: 'pending' | 'in_progress' | 'completed' | 'failed' | 'delivery_failed';
|
||||
result?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// System collections
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export interface Notification extends BaseRecord {
|
||||
title: string;
|
||||
message?: string;
|
||||
type: 'info' | 'success' | 'warning' | 'error';
|
||||
entity_type?: string;
|
||||
entity_id?: string;
|
||||
read: boolean;
|
||||
}
|
||||
|
||||
export interface ErrorLog extends BaseRecord {
|
||||
code?: string;
|
||||
message?: string;
|
||||
details?: Record<string, unknown>;
|
||||
stack_trace?: string;
|
||||
source?: string;
|
||||
}
|
||||
|
||||
export interface QueueJob extends BaseRecord {
|
||||
job_type: string;
|
||||
payload?: Record<string, unknown>;
|
||||
status: 'pending' | 'in_progress' | 'completed' | 'failed';
|
||||
attempts: number;
|
||||
max_attempts: number;
|
||||
error?: string;
|
||||
scheduled_at?: string;
|
||||
started_at?: string;
|
||||
completed_at?: string;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Collection names & type map
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export type CollectionName =
|
||||
| 'domains'
|
||||
| 'tags'
|
||||
| 'projects'
|
||||
| 'project_settings'
|
||||
| 'milestones'
|
||||
| 'milestone_dependencies'
|
||||
| 'milestone_templates'
|
||||
| 'milestone_history'
|
||||
| 'tasks'
|
||||
| 'task_subtasks'
|
||||
| 'task_dependencies'
|
||||
| 'task_attachments'
|
||||
| 'task_time_entries'
|
||||
| 'habits'
|
||||
| 'habit_logs'
|
||||
| 'habit_skip_days'
|
||||
| 'notes'
|
||||
| 'note_links'
|
||||
| 'note_task_links'
|
||||
| 'report_templates'
|
||||
| 'reports'
|
||||
| 'canvases'
|
||||
| 'canvas_cards'
|
||||
| 'agents'
|
||||
| 'agent_activity'
|
||||
| 'webhooks'
|
||||
| 'webhook_deliveries'
|
||||
| 'agent_tasks'
|
||||
| 'notifications'
|
||||
| 'error_logs'
|
||||
| 'queue_jobs';
|
||||
|
||||
/** Maps each PocketBase collection name to its corresponding TypeScript type. */
|
||||
export interface CollectionTypes {
|
||||
domains: Domain;
|
||||
tags: Tag;
|
||||
projects: Project;
|
||||
project_settings: ProjectSettings;
|
||||
milestones: Milestone;
|
||||
milestone_dependencies: MilestoneDependency;
|
||||
milestone_templates: MilestoneTemplate;
|
||||
milestone_history: MilestoneHistory;
|
||||
tasks: Task;
|
||||
task_subtasks: TaskSubtask;
|
||||
task_dependencies: TaskDependency;
|
||||
task_attachments: TaskAttachment;
|
||||
task_time_entries: TaskTimeEntry;
|
||||
habits: Habit;
|
||||
habit_logs: HabitLog;
|
||||
habit_skip_days: HabitSkipDay;
|
||||
notes: Note;
|
||||
note_links: NoteLink;
|
||||
note_task_links: NoteTaskLink;
|
||||
report_templates: ReportTemplate;
|
||||
reports: Report;
|
||||
canvases: Canvas;
|
||||
canvas_cards: CanvasCard;
|
||||
agents: Agent;
|
||||
agent_activity: AgentActivity;
|
||||
webhooks: Webhook;
|
||||
webhook_deliveries: WebhookDelivery;
|
||||
agent_tasks: AgentTask;
|
||||
notifications: Notification;
|
||||
error_logs: ErrorLog;
|
||||
queue_jobs: QueueJob;
|
||||
}
|
||||
Reference in New Issue
Block a user