@iwo-szapar/data-mcp 0.3.3 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapter/factory.js +4 -0
- package/dist/adapter/markdown.d.ts +29 -0
- package/dist/adapter/markdown.js +406 -0
- package/dist/adapter/types.d.ts +1 -1
- package/dist/config.js +13 -3
- package/dist/server.js +2 -2
- package/dist/tools/memory/link-create.js +2 -2
- package/dist/tools/memory/link-delete.js +1 -1
- package/dist/tools/memory/link-related.js +1 -1
- package/dist/tools/memory/link-suggest.js +1 -1
- package/dist/tools/register.js +3 -1
- package/dist/tools/setup/setup-bootstrap.js +71 -0
- package/dist/tools/setup/setup-migrate.js +20 -16
- package/dist/tools/setup/setup-status.js +11 -6
- package/migrations/pocketbase/001_core_schema.js +24 -28
- package/migrations/pocketbase/002_goals_tasks.js +15 -18
- package/migrations/pocketbase/003_contacts.js +9 -12
- package/migrations/pocketbase/004_entity_aliases.js +9 -12
- package/migrations/pocketbase/005_prospects.js +11 -14
- package/migrations/pocketbase/006_business.js +29 -33
- package/migrations/pocketbase/007_newsletter_affiliates.js +16 -19
- package/migrations/pocketbase/008_knowledge_links.js +34 -37
- package/migrations/supabase/009_align_to_production.sql +14 -11
- package/package.json +1 -1
- package/README.md +0 -286
|
@@ -3,8 +3,16 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Apply migrations — additive only, skips existing collections.
|
|
5
5
|
* Creates all expected collections in the database.
|
|
6
|
+
*
|
|
7
|
+
* RC-3 fix (incident 2026-05-11): Replaced hardcoded project-relative
|
|
8
|
+
* "migrations/supabase/" string with runtime-resolved absolute path to
|
|
9
|
+
* the bundled SQL files inside this package.
|
|
6
10
|
*/
|
|
11
|
+
import { dirname, join } from 'node:path';
|
|
12
|
+
import { fileURLToPath } from 'node:url';
|
|
7
13
|
import { makeToolResponse, makeErrorResponse } from '../shared.js';
|
|
14
|
+
const PACKAGE_MIGRATIONS_SUPABASE = join(dirname(fileURLToPath(import.meta.url)), '..', '..', '..', 'migrations', 'supabase');
|
|
15
|
+
const PACKAGE_MIGRATIONS_POCKETBASE = join(dirname(fileURLToPath(import.meta.url)), '..', '..', '..', 'migrations', 'pocketbase');
|
|
8
16
|
/** Collection schemas for PocketBase creation */
|
|
9
17
|
const COLLECTION_SCHEMAS = [
|
|
10
18
|
{ name: 'knowledge', description: 'Knowledge items (facts, patterns, insights, lessons, references)' },
|
|
@@ -23,10 +31,11 @@ const COLLECTION_SCHEMAS = [
|
|
|
23
31
|
{ name: 'affiliates', description: 'Affiliate partners and commissions' },
|
|
24
32
|
];
|
|
25
33
|
export function registerSetupMigrate(server, adapter) {
|
|
26
|
-
server.tool('setup_migrate', 'Create missing database collections/tables. Additive only — existing collections are skipped. For PocketBase, creates collections via API. For Supabase, reports SQL migrations
|
|
34
|
+
server.tool('setup_migrate', 'Create missing database collections/tables. Additive only — existing collections are skipped. For PocketBase, creates collections via API. For Supabase, reports SQL migrations and recommends running setup_bootstrap for paste-ready SQL.', {}, async () => {
|
|
27
35
|
try {
|
|
28
36
|
const skipped = [];
|
|
29
37
|
const needsMigration = [];
|
|
38
|
+
const migrationsPath = adapter.backend === 'supabase' ? PACKAGE_MIGRATIONS_SUPABASE : PACKAGE_MIGRATIONS_POCKETBASE;
|
|
30
39
|
for (const schema of COLLECTION_SCHEMAS) {
|
|
31
40
|
try {
|
|
32
41
|
const exists = await adapter.collectionExists(schema.name);
|
|
@@ -34,18 +43,12 @@ export function registerSetupMigrate(server, adapter) {
|
|
|
34
43
|
skipped.push(schema.name);
|
|
35
44
|
continue;
|
|
36
45
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
43
|
-
else {
|
|
44
|
-
needsMigration.push({
|
|
45
|
-
name: schema.name,
|
|
46
|
-
instruction: 'Run PocketBase migrations from migrations/pocketbase/ directory.',
|
|
47
|
-
});
|
|
48
|
-
}
|
|
46
|
+
needsMigration.push({
|
|
47
|
+
name: schema.name,
|
|
48
|
+
instruction: adapter.backend === 'supabase'
|
|
49
|
+
? `Apply SQL from ${migrationsPath}/. Easiest path: call setup_bootstrap for a paste-ready SQL block.`
|
|
50
|
+
: `Apply migration from ${migrationsPath}/.`,
|
|
51
|
+
});
|
|
49
52
|
}
|
|
50
53
|
catch (err) {
|
|
51
54
|
const msg = err instanceof Error ? err.message : 'Unknown error';
|
|
@@ -55,6 +58,7 @@ export function registerSetupMigrate(server, adapter) {
|
|
|
55
58
|
}
|
|
56
59
|
return makeToolResponse({
|
|
57
60
|
backend: adapter.backend,
|
|
61
|
+
migrations_path: migrationsPath,
|
|
58
62
|
existing: skipped.length,
|
|
59
63
|
needs_migration: needsMigration.length,
|
|
60
64
|
details: {
|
|
@@ -62,8 +66,8 @@ export function registerSetupMigrate(server, adapter) {
|
|
|
62
66
|
needs_migration: needsMigration.length > 0 ? needsMigration : undefined,
|
|
63
67
|
},
|
|
64
68
|
message: adapter.backend === 'pocketbase'
|
|
65
|
-
? `Schema check complete. ${skipped.length} existing, ${needsMigration.length} need migration files.
|
|
66
|
-
: `Schema check complete. ${skipped.length} existing, ${needsMigration.length} need SQL
|
|
69
|
+
? `Schema check complete. ${skipped.length} existing, ${needsMigration.length} need migration files. Apply from ${migrationsPath}/.`
|
|
70
|
+
: `Schema check complete. ${skipped.length} existing, ${needsMigration.length} need SQL. Run setup_bootstrap for a paste-ready block, or apply from ${migrationsPath}/ manually.`,
|
|
67
71
|
});
|
|
68
72
|
}
|
|
69
73
|
catch (error) {
|
|
@@ -72,4 +76,4 @@ export function registerSetupMigrate(server, adapter) {
|
|
|
72
76
|
}
|
|
73
77
|
});
|
|
74
78
|
}
|
|
75
|
-
//# sourceMappingURL=setup-migrate.js.map
|
|
79
|
+
//# sourceMappingURL=setup-migrate.js.map
|
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
* Tool: setup_status
|
|
3
3
|
*
|
|
4
4
|
* Check backend connection, list existing/missing collections, report status.
|
|
5
|
+
*
|
|
6
|
+
* RC-1 fix (incident 2026-05-11): Previously called adapter.listCollections()
|
|
7
|
+
* which uses rpc('get_public_tables') — a custom Postgres function absent from
|
|
8
|
+
* fresh Supabase projects. Fallback to information_schema also failed because
|
|
9
|
+
* PostgREST only exposes the public schema. Now uses the same code path as
|
|
10
|
+
* setup_migrate (adapter.collectionExists per expected collection) so the
|
|
11
|
+
* diagnostic agrees with the data tools.
|
|
5
12
|
*/
|
|
6
13
|
import { makeToolResponse, makeErrorResponse } from '../shared.js';
|
|
7
14
|
const EXPECTED_COLLECTIONS = [
|
|
@@ -23,20 +30,18 @@ const EXPECTED_COLLECTIONS = [
|
|
|
23
30
|
export function registerSetupStatus(server, adapter) {
|
|
24
31
|
server.tool('setup_status', 'Check database connection status, list existing and missing collections, and report schema readiness.', {}, { readOnlyHint: true }, async () => {
|
|
25
32
|
try {
|
|
26
|
-
// Test connection by listing collections
|
|
27
|
-
const existing = await adapter.listCollections();
|
|
28
|
-
const existingSet = new Set(existing);
|
|
29
33
|
const present = [];
|
|
30
34
|
const missing = [];
|
|
31
35
|
for (const collection of EXPECTED_COLLECTIONS) {
|
|
32
|
-
|
|
36
|
+
const exists = await adapter.collectionExists(collection);
|
|
37
|
+
if (exists) {
|
|
33
38
|
present.push(collection);
|
|
34
39
|
}
|
|
35
40
|
else {
|
|
36
41
|
missing.push(collection);
|
|
37
42
|
}
|
|
38
43
|
}
|
|
39
|
-
|
|
44
|
+
const existingSet = new Set(present);
|
|
40
45
|
let schemaVersion = null;
|
|
41
46
|
if (existingSet.has('settings')) {
|
|
42
47
|
try {
|
|
@@ -75,4 +80,4 @@ export function registerSetupStatus(server, adapter) {
|
|
|
75
80
|
}
|
|
76
81
|
});
|
|
77
82
|
}
|
|
78
|
-
//# sourceMappingURL=setup-status.js.map
|
|
83
|
+
//# sourceMappingURL=setup-status.js.map
|
|
@@ -2,26 +2,24 @@
|
|
|
2
2
|
* PocketBase Migration 001: Core schema
|
|
3
3
|
*
|
|
4
4
|
* Creates knowledge, decisions, sessions collections.
|
|
5
|
-
* PocketBase v0.23+ field format.
|
|
6
5
|
*/
|
|
7
6
|
|
|
8
7
|
/// <reference path="../pb_data/types.d.ts" />
|
|
9
8
|
|
|
10
9
|
migrate((app) => {
|
|
10
|
+
// knowledge collection
|
|
11
11
|
const knowledge = new Collection({
|
|
12
12
|
name: 'knowledge',
|
|
13
13
|
type: 'base',
|
|
14
|
-
|
|
15
|
-
{ name: 'type', type: 'select', required: true,
|
|
16
|
-
{ name: 'title', type: 'text', required: true,
|
|
17
|
-
{ name: 'content', type: 'editor', required: true, maxSize: 50000 },
|
|
18
|
-
{ name: 'summary', type: 'text',
|
|
14
|
+
schema: [
|
|
15
|
+
{ name: 'type', type: 'select', required: true, options: { values: ['fact', 'knowledge', 'pattern', 'insight', 'lesson', 'reference'] } },
|
|
16
|
+
{ name: 'title', type: 'text', required: true, options: { maxSize: 500 } },
|
|
17
|
+
{ name: 'content', type: 'editor', required: true, options: { maxSize: 50000 } },
|
|
18
|
+
{ name: 'summary', type: 'text', options: { maxSize: 2000 } },
|
|
19
19
|
{ name: 'tags', type: 'json' },
|
|
20
|
-
{ name: 'source', type: 'text',
|
|
21
|
-
{ name: 'confidence', type: 'number', min: 0, max: 1 },
|
|
20
|
+
{ name: 'source', type: 'text', options: { maxSize: 500 } },
|
|
21
|
+
{ name: 'confidence', type: 'number', options: { min: 0, max: 1 } },
|
|
22
22
|
{ name: 'last_validated_at', type: 'date' },
|
|
23
|
-
{ name: 'created', type: 'autodate', onCreate: true },
|
|
24
|
-
{ name: 'updated', type: 'autodate', onCreate: true, onUpdate: true },
|
|
25
23
|
],
|
|
26
24
|
indexes: [
|
|
27
25
|
'CREATE INDEX idx_knowledge_type ON knowledge (type)',
|
|
@@ -30,47 +28,45 @@ migrate((app) => {
|
|
|
30
28
|
});
|
|
31
29
|
app.save(knowledge);
|
|
32
30
|
|
|
31
|
+
// decisions collection
|
|
33
32
|
const decisions = new Collection({
|
|
34
33
|
name: 'decisions',
|
|
35
34
|
type: 'base',
|
|
36
|
-
|
|
37
|
-
{ name: 'title', type: 'text', required: true,
|
|
38
|
-
{ name: 'context', type: 'editor', maxSize: 5000 },
|
|
35
|
+
schema: [
|
|
36
|
+
{ name: 'title', type: 'text', required: true, options: { maxSize: 500 } },
|
|
37
|
+
{ name: 'context', type: 'editor', options: { maxSize: 5000 } },
|
|
39
38
|
{ name: 'options_considered', type: 'json', required: true },
|
|
40
|
-
{ name: 'chosen_option', type: 'text', required: true,
|
|
41
|
-
{ name: 'rationale', type: 'editor', maxSize: 5000 },
|
|
42
|
-
{ name: 'outcome', type: 'editor', maxSize: 5000 },
|
|
39
|
+
{ name: 'chosen_option', type: 'text', required: true, options: { maxSize: 500 } },
|
|
40
|
+
{ name: 'rationale', type: 'editor', options: { maxSize: 5000 } },
|
|
41
|
+
{ name: 'outcome', type: 'editor', options: { maxSize: 5000 } },
|
|
43
42
|
{ name: 'tags', type: 'json' },
|
|
44
|
-
{ name: 'created', type: 'autodate', onCreate: true },
|
|
45
|
-
{ name: 'updated', type: 'autodate', onCreate: true, onUpdate: true },
|
|
46
43
|
],
|
|
47
44
|
});
|
|
48
45
|
app.save(decisions);
|
|
49
46
|
|
|
47
|
+
// sessions collection
|
|
50
48
|
const sessions = new Collection({
|
|
51
49
|
name: 'sessions',
|
|
52
50
|
type: 'base',
|
|
53
|
-
|
|
54
|
-
{ name: 'title', type: 'text', required: true,
|
|
55
|
-
{ name: 'summary', type: 'editor', required: true, maxSize: 10000 },
|
|
51
|
+
schema: [
|
|
52
|
+
{ name: 'title', type: 'text', required: true, options: { maxSize: 500 } },
|
|
53
|
+
{ name: 'summary', type: 'editor', required: true, options: { maxSize: 10000 } },
|
|
56
54
|
{ name: 'session_date', type: 'date' },
|
|
57
55
|
{ name: 'skills_used', type: 'json' },
|
|
58
56
|
{ name: 'files_changed', type: 'json' },
|
|
59
57
|
{ name: 'decisions_made', type: 'json' },
|
|
60
58
|
{ name: 'duration_minutes', type: 'number' },
|
|
61
|
-
{ name: 'task_id', type: 'text',
|
|
62
|
-
{ name: 'branch', type: 'text',
|
|
59
|
+
{ name: 'task_id', type: 'text', options: { maxSize: 100 } },
|
|
60
|
+
{ name: 'branch', type: 'text', options: { maxSize: 200 } },
|
|
63
61
|
{ name: 'patterns_learned', type: 'json' },
|
|
64
62
|
{ name: 'knowledge_created', type: 'number' },
|
|
65
63
|
{ name: 'knowledge_updated', type: 'number' },
|
|
66
64
|
{ name: 'metadata', type: 'json' },
|
|
67
|
-
{ name: 'created', type: 'autodate', onCreate: true },
|
|
68
|
-
{ name: 'updated', type: 'autodate', onCreate: true, onUpdate: true },
|
|
69
65
|
],
|
|
70
66
|
});
|
|
71
67
|
app.save(sessions);
|
|
72
68
|
}, (app) => {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
69
|
+
app.delete(app.findCollectionByNameOrId('sessions'));
|
|
70
|
+
app.delete(app.findCollectionByNameOrId('decisions'));
|
|
71
|
+
app.delete(app.findCollectionByNameOrId('knowledge'));
|
|
76
72
|
});
|
|
@@ -1,23 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* PocketBase Migration 002: Goals and tasks
|
|
3
|
-
* PocketBase v0.23+ field format.
|
|
4
3
|
*/
|
|
5
4
|
|
|
6
5
|
/// <reference path="../pb_data/types.d.ts" />
|
|
7
6
|
|
|
8
7
|
migrate((app) => {
|
|
8
|
+
// goals collection
|
|
9
9
|
const goals = new Collection({
|
|
10
10
|
name: 'goals',
|
|
11
11
|
type: 'base',
|
|
12
|
-
|
|
13
|
-
{ name: 'title', type: 'text', required: true,
|
|
14
|
-
{ name: 'description', type: 'editor', maxSize: 5000 },
|
|
15
|
-
{ name: 'timeframe', type: 'select', required: true,
|
|
16
|
-
{ name: 'status', type: 'select', required: true,
|
|
12
|
+
schema: [
|
|
13
|
+
{ name: 'title', type: 'text', required: true, options: { maxSize: 500 } },
|
|
14
|
+
{ name: 'description', type: 'editor', options: { maxSize: 5000 } },
|
|
15
|
+
{ name: 'timeframe', type: 'select', required: true, options: { values: ['daily', 'weekly', 'monthly', 'quarterly', 'yearly'] } },
|
|
16
|
+
{ name: 'status', type: 'select', required: true, options: { values: ['active', 'completed', 'paused', 'abandoned'] } },
|
|
17
17
|
{ name: 'key_results', type: 'json' },
|
|
18
18
|
{ name: 'tags', type: 'json' },
|
|
19
|
-
{ name: 'created', type: 'autodate', onCreate: true },
|
|
20
|
-
{ name: 'updated', type: 'autodate', onCreate: true, onUpdate: true },
|
|
21
19
|
],
|
|
22
20
|
indexes: [
|
|
23
21
|
'CREATE INDEX idx_goals_status ON goals (status)',
|
|
@@ -26,19 +24,18 @@ migrate((app) => {
|
|
|
26
24
|
});
|
|
27
25
|
app.save(goals);
|
|
28
26
|
|
|
27
|
+
// tasks collection
|
|
29
28
|
const tasks = new Collection({
|
|
30
29
|
name: 'tasks',
|
|
31
30
|
type: 'base',
|
|
32
|
-
|
|
33
|
-
{ name: 'title', type: 'text', required: true,
|
|
34
|
-
{ name: 'description', type: 'editor', maxSize: 5000 },
|
|
35
|
-
{ name: 'status', type: 'select', required: true,
|
|
36
|
-
{ name: 'priority', type: 'select', required: true,
|
|
31
|
+
schema: [
|
|
32
|
+
{ name: 'title', type: 'text', required: true, options: { maxSize: 500 } },
|
|
33
|
+
{ name: 'description', type: 'editor', options: { maxSize: 5000 } },
|
|
34
|
+
{ name: 'status', type: 'select', required: true, options: { values: ['todo', 'in_progress', 'done', 'cancelled'] } },
|
|
35
|
+
{ name: 'priority', type: 'select', required: true, options: { values: ['low', 'medium', 'high', 'urgent'] } },
|
|
37
36
|
{ name: 'due_date', type: 'date' },
|
|
38
37
|
{ name: 'tags', type: 'json' },
|
|
39
|
-
{ name: 'goal_id', type: 'text',
|
|
40
|
-
{ name: 'created', type: 'autodate', onCreate: true },
|
|
41
|
-
{ name: 'updated', type: 'autodate', onCreate: true, onUpdate: true },
|
|
38
|
+
{ name: 'goal_id', type: 'text', options: { maxSize: 100 } },
|
|
42
39
|
],
|
|
43
40
|
indexes: [
|
|
44
41
|
'CREATE INDEX idx_tasks_status ON tasks (status)',
|
|
@@ -47,6 +44,6 @@ migrate((app) => {
|
|
|
47
44
|
});
|
|
48
45
|
app.save(tasks);
|
|
49
46
|
}, (app) => {
|
|
50
|
-
|
|
51
|
-
|
|
47
|
+
app.delete(app.findCollectionByNameOrId('tasks'));
|
|
48
|
+
app.delete(app.findCollectionByNameOrId('goals'));
|
|
52
49
|
});
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* PocketBase Migration 003: Contacts
|
|
3
|
-
* PocketBase v0.23+ field format.
|
|
4
3
|
*/
|
|
5
4
|
|
|
6
5
|
/// <reference path="../pb_data/types.d.ts" />
|
|
@@ -9,18 +8,16 @@ migrate((app) => {
|
|
|
9
8
|
const contacts = new Collection({
|
|
10
9
|
name: 'contacts',
|
|
11
10
|
type: 'base',
|
|
12
|
-
|
|
13
|
-
{ name: 'name', type: 'text', required: true,
|
|
14
|
-
{ name: 'company', type: 'text',
|
|
15
|
-
{ name: 'role', type: 'text',
|
|
16
|
-
{ name: 'email', type: 'email' },
|
|
17
|
-
{ name: 'phone', type: 'text',
|
|
18
|
-
{ name: 'relationship', type: 'select',
|
|
19
|
-
{ name: 'notes', type: 'editor', maxSize: 5000 },
|
|
11
|
+
schema: [
|
|
12
|
+
{ name: 'name', type: 'text', required: true, options: { maxSize: 200 } },
|
|
13
|
+
{ name: 'company', type: 'text', options: { maxSize: 200 } },
|
|
14
|
+
{ name: 'role', type: 'text', options: { maxSize: 200 } },
|
|
15
|
+
{ name: 'email', type: 'email', options: { maxSize: 200 } },
|
|
16
|
+
{ name: 'phone', type: 'text', options: { maxSize: 50 } },
|
|
17
|
+
{ name: 'relationship', type: 'select', options: { values: ['colleague', 'client', 'prospect', 'partner', 'other'] } },
|
|
18
|
+
{ name: 'notes', type: 'editor', options: { maxSize: 5000 } },
|
|
20
19
|
{ name: 'tags', type: 'json' },
|
|
21
20
|
{ name: 'last_contact_date', type: 'date' },
|
|
22
|
-
{ name: 'created', type: 'autodate', onCreate: true },
|
|
23
|
-
{ name: 'updated', type: 'autodate', onCreate: true, onUpdate: true },
|
|
24
21
|
],
|
|
25
22
|
indexes: [
|
|
26
23
|
'CREATE INDEX idx_contacts_name ON contacts (name)',
|
|
@@ -28,5 +25,5 @@ migrate((app) => {
|
|
|
28
25
|
});
|
|
29
26
|
app.save(contacts);
|
|
30
27
|
}, (app) => {
|
|
31
|
-
|
|
28
|
+
app.delete(app.findCollectionByNameOrId('contacts'));
|
|
32
29
|
});
|
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* PocketBase Migration 004: Entity aliases + settings
|
|
3
|
-
* PocketBase v0.23+ field format.
|
|
4
3
|
*/
|
|
5
4
|
|
|
6
5
|
/// <reference path="../pb_data/types.d.ts" />
|
|
7
6
|
|
|
8
7
|
migrate((app) => {
|
|
8
|
+
// entity_aliases collection
|
|
9
9
|
const aliases = new Collection({
|
|
10
10
|
name: 'entity_aliases',
|
|
11
11
|
type: 'base',
|
|
12
|
-
|
|
13
|
-
{ name: 'canonical', type: 'text', required: true,
|
|
14
|
-
{ name: 'alias', type: 'text', required: true,
|
|
15
|
-
{ name: 'created', type: 'autodate', onCreate: true },
|
|
16
|
-
{ name: 'updated', type: 'autodate', onCreate: true, onUpdate: true },
|
|
12
|
+
schema: [
|
|
13
|
+
{ name: 'canonical', type: 'text', required: true, options: { maxSize: 100 } },
|
|
14
|
+
{ name: 'alias', type: 'text', required: true, options: { maxSize: 200 } },
|
|
17
15
|
],
|
|
18
16
|
indexes: [
|
|
19
17
|
'CREATE UNIQUE INDEX idx_entity_aliases_unique ON entity_aliases (canonical, alias)',
|
|
@@ -23,14 +21,13 @@ migrate((app) => {
|
|
|
23
21
|
});
|
|
24
22
|
app.save(aliases);
|
|
25
23
|
|
|
24
|
+
// settings collection
|
|
26
25
|
const settings = new Collection({
|
|
27
26
|
name: 'settings',
|
|
28
27
|
type: 'base',
|
|
29
|
-
|
|
30
|
-
{ name: 'key', type: 'text', required: true,
|
|
28
|
+
schema: [
|
|
29
|
+
{ name: 'key', type: 'text', required: true, options: { maxSize: 100 } },
|
|
31
30
|
{ name: 'value', type: 'editor' },
|
|
32
|
-
{ name: 'created', type: 'autodate', onCreate: true },
|
|
33
|
-
{ name: 'updated', type: 'autodate', onCreate: true, onUpdate: true },
|
|
34
31
|
],
|
|
35
32
|
indexes: [
|
|
36
33
|
'CREATE UNIQUE INDEX idx_settings_key ON settings (key)',
|
|
@@ -38,6 +35,6 @@ migrate((app) => {
|
|
|
38
35
|
});
|
|
39
36
|
app.save(settings);
|
|
40
37
|
}, (app) => {
|
|
41
|
-
|
|
42
|
-
|
|
38
|
+
app.delete(app.findCollectionByNameOrId('settings'));
|
|
39
|
+
app.delete(app.findCollectionByNameOrId('entity_aliases'));
|
|
43
40
|
});
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* PocketBase Migration 005: Prospects (CRM)
|
|
3
|
-
* PocketBase v0.23+ field format.
|
|
4
3
|
*/
|
|
5
4
|
|
|
6
5
|
/// <reference path="../pb_data/types.d.ts" />
|
|
@@ -9,22 +8,20 @@ migrate((app) => {
|
|
|
9
8
|
const prospects = new Collection({
|
|
10
9
|
name: 'prospects',
|
|
11
10
|
type: 'base',
|
|
12
|
-
|
|
13
|
-
{ name: 'name', type: 'text', required: true,
|
|
14
|
-
{ name: 'email', type: 'email' },
|
|
15
|
-
{ name: 'company', type: 'text',
|
|
16
|
-
{ name: 'role', type: 'text',
|
|
17
|
-
{ name: 'stage', type: 'select', required: true,
|
|
18
|
-
{ name: 'source', type: 'text',
|
|
11
|
+
schema: [
|
|
12
|
+
{ name: 'name', type: 'text', required: true, options: { maxSize: 200 } },
|
|
13
|
+
{ name: 'email', type: 'email', options: { maxSize: 200 } },
|
|
14
|
+
{ name: 'company', type: 'text', options: { maxSize: 200 } },
|
|
15
|
+
{ name: 'role', type: 'text', options: { maxSize: 200 } },
|
|
16
|
+
{ name: 'stage', type: 'select', required: true, options: { values: ['new', 'contacted', 'responded', 'interested', 'ready_to_buy', 'proposal_sent', 'negotiating', 'closed_won', 'closed_lost', 'nurturing'] } },
|
|
17
|
+
{ name: 'source', type: 'text', options: { maxSize: 200 } },
|
|
19
18
|
{ name: 'estimated_value', type: 'number' },
|
|
20
|
-
{ name: 'next_action_type', type: 'text',
|
|
19
|
+
{ name: 'next_action_type', type: 'text', options: { maxSize: 100 } },
|
|
21
20
|
{ name: 'next_followup_date', type: 'date' },
|
|
22
21
|
{ name: 'last_contact_date', type: 'date' },
|
|
23
|
-
{ name: 'notes', type: 'editor', maxSize: 10000 },
|
|
22
|
+
{ name: 'notes', type: 'editor', options: { maxSize: 10000 } },
|
|
24
23
|
{ name: 'tags', type: 'json' },
|
|
25
|
-
{ name: 'linkedin_url', type: 'url' },
|
|
26
|
-
{ name: 'created', type: 'autodate', onCreate: true },
|
|
27
|
-
{ name: 'updated', type: 'autodate', onCreate: true, onUpdate: true },
|
|
24
|
+
{ name: 'linkedin_url', type: 'url', options: { maxSize: 500 } },
|
|
28
25
|
],
|
|
29
26
|
indexes: [
|
|
30
27
|
'CREATE INDEX idx_prospects_stage ON prospects (stage)',
|
|
@@ -32,5 +29,5 @@ migrate((app) => {
|
|
|
32
29
|
});
|
|
33
30
|
app.save(prospects);
|
|
34
31
|
}, (app) => {
|
|
35
|
-
|
|
32
|
+
app.delete(app.findCollectionByNameOrId('prospects'));
|
|
36
33
|
});
|
|
@@ -1,27 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* PocketBase Migration 006: Business collections (blog, email, content)
|
|
3
|
-
* PocketBase v0.23+ field format.
|
|
4
3
|
*/
|
|
5
4
|
|
|
6
5
|
/// <reference path="../pb_data/types.d.ts" />
|
|
7
6
|
|
|
8
7
|
migrate((app) => {
|
|
8
|
+
// blog_posts collection
|
|
9
9
|
const blogPosts = new Collection({
|
|
10
10
|
name: 'blog_posts',
|
|
11
11
|
type: 'base',
|
|
12
|
-
|
|
13
|
-
{ name: 'title', type: 'text', required: true,
|
|
14
|
-
{ name: 'slug', type: 'text', required: true,
|
|
12
|
+
schema: [
|
|
13
|
+
{ name: 'title', type: 'text', required: true, options: { maxSize: 500 } },
|
|
14
|
+
{ name: 'slug', type: 'text', required: true, options: { maxSize: 200 } },
|
|
15
15
|
{ name: 'content', type: 'editor', required: true },
|
|
16
|
-
{ name: 'excerpt', type: 'text',
|
|
17
|
-
{ name: 'status', type: 'select', required: true,
|
|
16
|
+
{ name: 'excerpt', type: 'text', options: { maxSize: 500 } },
|
|
17
|
+
{ name: 'status', type: 'select', required: true, options: { values: ['draft', 'published', 'archived'] } },
|
|
18
18
|
{ name: 'published_at', type: 'date' },
|
|
19
19
|
{ name: 'tags', type: 'json' },
|
|
20
|
-
{ name: 'seo_title', type: 'text',
|
|
21
|
-
{ name: 'seo_description', type: 'text',
|
|
22
|
-
{ name: 'og_image_url', type: 'url' },
|
|
23
|
-
{ name: 'created', type: 'autodate', onCreate: true },
|
|
24
|
-
{ name: 'updated', type: 'autodate', onCreate: true, onUpdate: true },
|
|
20
|
+
{ name: 'seo_title', type: 'text', options: { maxSize: 200 } },
|
|
21
|
+
{ name: 'seo_description', type: 'text', options: { maxSize: 300 } },
|
|
22
|
+
{ name: 'og_image_url', type: 'url', options: { maxSize: 500 } },
|
|
25
23
|
],
|
|
26
24
|
indexes: [
|
|
27
25
|
'CREATE UNIQUE INDEX idx_blog_posts_slug ON blog_posts (slug)',
|
|
@@ -30,25 +28,24 @@ migrate((app) => {
|
|
|
30
28
|
});
|
|
31
29
|
app.save(blogPosts);
|
|
32
30
|
|
|
31
|
+
// email_queue collection
|
|
33
32
|
const emailQueue = new Collection({
|
|
34
33
|
name: 'email_queue',
|
|
35
34
|
type: 'base',
|
|
36
|
-
|
|
37
|
-
{ name: 'to_email', type: 'email', required: true },
|
|
38
|
-
{ name: 'to_name', type: 'text',
|
|
39
|
-
{ name: 'subject', type: 'text', required: true,
|
|
35
|
+
schema: [
|
|
36
|
+
{ name: 'to_email', type: 'email', required: true, options: { maxSize: 200 } },
|
|
37
|
+
{ name: 'to_name', type: 'text', options: { maxSize: 200 } },
|
|
38
|
+
{ name: 'subject', type: 'text', required: true, options: { maxSize: 500 } },
|
|
40
39
|
{ name: 'body_html', type: 'editor', required: true },
|
|
41
40
|
{ name: 'body_text', type: 'editor' },
|
|
42
|
-
{ name: 'status', type: 'select', required: true,
|
|
43
|
-
{ name: 'sequence_id', type: 'text',
|
|
41
|
+
{ name: 'status', type: 'select', required: true, options: { values: ['queued', 'sent', 'failed', 'bounced'] } },
|
|
42
|
+
{ name: 'sequence_id', type: 'text', options: { maxSize: 100 } },
|
|
44
43
|
{ name: 'sequence_step', type: 'number' },
|
|
45
|
-
{ name: 'prospect_id', type: 'text',
|
|
44
|
+
{ name: 'prospect_id', type: 'text', options: { maxSize: 100 } },
|
|
46
45
|
{ name: 'scheduled_at', type: 'date' },
|
|
47
46
|
{ name: 'sent_at', type: 'date' },
|
|
48
47
|
{ name: 'error', type: 'editor' },
|
|
49
|
-
{ name: 'resend_id', type: 'text',
|
|
50
|
-
{ name: 'created', type: 'autodate', onCreate: true },
|
|
51
|
-
{ name: 'updated', type: 'autodate', onCreate: true, onUpdate: true },
|
|
48
|
+
{ name: 'resend_id', type: 'text', options: { maxSize: 200 } },
|
|
52
49
|
],
|
|
53
50
|
indexes: [
|
|
54
51
|
'CREATE INDEX idx_email_queue_status ON email_queue (status)',
|
|
@@ -56,20 +53,19 @@ migrate((app) => {
|
|
|
56
53
|
});
|
|
57
54
|
app.save(emailQueue);
|
|
58
55
|
|
|
56
|
+
// content_calendar collection
|
|
59
57
|
const contentCalendar = new Collection({
|
|
60
58
|
name: 'content_calendar',
|
|
61
59
|
type: 'base',
|
|
62
|
-
|
|
63
|
-
{ name: 'title', type: 'text', required: true,
|
|
60
|
+
schema: [
|
|
61
|
+
{ name: 'title', type: 'text', required: true, options: { maxSize: 500 } },
|
|
64
62
|
{ name: 'content', type: 'editor' },
|
|
65
|
-
{ name: 'platform', type: 'select', required: true,
|
|
66
|
-
{ name: 'pillar', type: 'text',
|
|
67
|
-
{ name: 'status', type: 'select', required: true,
|
|
63
|
+
{ name: 'platform', type: 'select', required: true, options: { values: ['linkedin', 'newsletter', 'blog', 'twitter', 'other'] } },
|
|
64
|
+
{ name: 'pillar', type: 'text', options: { maxSize: 100 } },
|
|
65
|
+
{ name: 'status', type: 'select', required: true, options: { values: ['idea', 'drafting', 'ready', 'published'] } },
|
|
68
66
|
{ name: 'scheduled_date', type: 'date' },
|
|
69
|
-
{ name: 'published_url', type: 'url' },
|
|
70
|
-
{ name: 'persona', type: 'text',
|
|
71
|
-
{ name: 'created', type: 'autodate', onCreate: true },
|
|
72
|
-
{ name: 'updated', type: 'autodate', onCreate: true, onUpdate: true },
|
|
67
|
+
{ name: 'published_url', type: 'url', options: { maxSize: 500 } },
|
|
68
|
+
{ name: 'persona', type: 'text', options: { maxSize: 100 } },
|
|
73
69
|
],
|
|
74
70
|
indexes: [
|
|
75
71
|
'CREATE INDEX idx_content_calendar_platform ON content_calendar (platform)',
|
|
@@ -78,7 +74,7 @@ migrate((app) => {
|
|
|
78
74
|
});
|
|
79
75
|
app.save(contentCalendar);
|
|
80
76
|
}, (app) => {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
77
|
+
app.delete(app.findCollectionByNameOrId('content_calendar'));
|
|
78
|
+
app.delete(app.findCollectionByNameOrId('email_queue'));
|
|
79
|
+
app.delete(app.findCollectionByNameOrId('blog_posts'));
|
|
84
80
|
});
|
|
@@ -1,24 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* PocketBase Migration 007: Newsletter subscribers and affiliates
|
|
3
|
-
* PocketBase v0.23+ field format.
|
|
4
3
|
*/
|
|
5
4
|
|
|
6
5
|
/// <reference path="../pb_data/types.d.ts" />
|
|
7
6
|
|
|
8
7
|
migrate((app) => {
|
|
8
|
+
// newsletter_subscribers collection
|
|
9
9
|
const subscribers = new Collection({
|
|
10
10
|
name: 'newsletter_subscribers',
|
|
11
11
|
type: 'base',
|
|
12
|
-
|
|
13
|
-
{ name: 'email', type: 'email', required: true },
|
|
14
|
-
{ name: 'name', type: 'text',
|
|
15
|
-
{ name: 'status', type: 'select', required: true,
|
|
16
|
-
{ name: 'source', type: 'text',
|
|
12
|
+
schema: [
|
|
13
|
+
{ name: 'email', type: 'email', required: true, options: { maxSize: 200 } },
|
|
14
|
+
{ name: 'name', type: 'text', options: { maxSize: 200 } },
|
|
15
|
+
{ name: 'status', type: 'select', required: true, options: { values: ['active', 'unsubscribed', 'bounced'] } },
|
|
16
|
+
{ name: 'source', type: 'text', options: { maxSize: 200 } },
|
|
17
17
|
{ name: 'tags', type: 'json' },
|
|
18
18
|
{ name: 'subscribed_at', type: 'date' },
|
|
19
19
|
{ name: 'unsubscribed_at', type: 'date' },
|
|
20
|
-
{ name: 'created', type: 'autodate', onCreate: true },
|
|
21
|
-
{ name: 'updated', type: 'autodate', onCreate: true, onUpdate: true },
|
|
22
20
|
],
|
|
23
21
|
indexes: [
|
|
24
22
|
'CREATE UNIQUE INDEX idx_newsletter_subscribers_email ON newsletter_subscribers (email)',
|
|
@@ -26,20 +24,19 @@ migrate((app) => {
|
|
|
26
24
|
});
|
|
27
25
|
app.save(subscribers);
|
|
28
26
|
|
|
27
|
+
// affiliates collection
|
|
29
28
|
const affiliates = new Collection({
|
|
30
29
|
name: 'affiliates',
|
|
31
30
|
type: 'base',
|
|
32
|
-
|
|
33
|
-
{ name: 'name', type: 'text', required: true,
|
|
34
|
-
{ name: 'email', type: 'email', required: true },
|
|
35
|
-
{ name: 'code', type: 'text', required: true,
|
|
36
|
-
{ name: 'commission_rate', type: 'number', min: 0, max: 1 },
|
|
37
|
-
{ name: 'status', type: 'select', required: true,
|
|
31
|
+
schema: [
|
|
32
|
+
{ name: 'name', type: 'text', required: true, options: { maxSize: 200 } },
|
|
33
|
+
{ name: 'email', type: 'email', required: true, options: { maxSize: 200 } },
|
|
34
|
+
{ name: 'code', type: 'text', required: true, options: { maxSize: 100 } },
|
|
35
|
+
{ name: 'commission_rate', type: 'number', options: { min: 0, max: 1 } },
|
|
36
|
+
{ name: 'status', type: 'select', required: true, options: { values: ['pending', 'active', 'paused', 'terminated'] } },
|
|
38
37
|
{ name: 'total_earned_cents', type: 'number' },
|
|
39
38
|
{ name: 'total_paid_cents', type: 'number' },
|
|
40
|
-
{ name: 'stripe_account_id', type: 'text',
|
|
41
|
-
{ name: 'created', type: 'autodate', onCreate: true },
|
|
42
|
-
{ name: 'updated', type: 'autodate', onCreate: true, onUpdate: true },
|
|
39
|
+
{ name: 'stripe_account_id', type: 'text', options: { maxSize: 200 } },
|
|
43
40
|
],
|
|
44
41
|
indexes: [
|
|
45
42
|
'CREATE UNIQUE INDEX idx_affiliates_email ON affiliates (email)',
|
|
@@ -48,6 +45,6 @@ migrate((app) => {
|
|
|
48
45
|
});
|
|
49
46
|
app.save(affiliates);
|
|
50
47
|
}, (app) => {
|
|
51
|
-
|
|
52
|
-
|
|
48
|
+
app.delete(app.findCollectionByNameOrId('affiliates'));
|
|
49
|
+
app.delete(app.findCollectionByNameOrId('newsletter_subscribers'));
|
|
53
50
|
});
|