@kelpie/server 0.12.0 → 0.14.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/app.d.ts +13 -0
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +8 -1
- package/dist/app.js.map +1 -1
- package/dist/boot.d.ts +9 -0
- package/dist/boot.d.ts.map +1 -1
- package/dist/boot.js +12 -2
- package/dist/boot.js.map +1 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/apiKeyScopes.d.ts.map +1 -1
- package/dist/lib/apiKeyScopes.js +6 -0
- package/dist/lib/apiKeyScopes.js.map +1 -1
- package/dist/lib/columns.d.ts +6 -0
- package/dist/lib/columns.d.ts.map +1 -1
- package/dist/lib/columns.js +5 -3
- package/dist/lib/columns.js.map +1 -1
- package/dist/lib/config.d.ts +17 -0
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +3 -0
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/egress.d.ts.map +1 -1
- package/dist/lib/egress.js +8 -4
- package/dist/lib/egress.js.map +1 -1
- package/dist/lib/errors.d.ts +3 -3
- package/dist/lib/errors.d.ts.map +1 -1
- package/dist/lib/errors.js +13 -5
- package/dist/lib/errors.js.map +1 -1
- package/dist/lib/ids.d.ts +11 -0
- package/dist/lib/ids.d.ts.map +1 -1
- package/dist/lib/ids.js +11 -0
- package/dist/lib/ids.js.map +1 -1
- package/dist/lib/jobs.d.ts +110 -0
- package/dist/lib/jobs.d.ts.map +1 -0
- package/dist/lib/jobs.js +5 -0
- package/dist/lib/jobs.js.map +1 -0
- package/dist/lib/kelpieConfigFile.d.ts +15 -1
- package/dist/lib/kelpieConfigFile.d.ts.map +1 -1
- package/dist/lib/kelpieConfigFile.js +33 -1
- package/dist/lib/kelpieConfigFile.js.map +1 -1
- package/dist/modules/agent-tasks/catalog.d.ts.map +1 -1
- package/dist/modules/agent-tasks/catalog.js +32 -1
- package/dist/modules/agent-tasks/catalog.js.map +1 -1
- package/dist/modules/agent-tasks/index.d.ts.map +1 -1
- package/dist/modules/agent-tasks/index.js +1 -0
- package/dist/modules/agent-tasks/index.js.map +1 -1
- package/dist/modules/agent-tasks/resolve.d.ts.map +1 -1
- package/dist/modules/agent-tasks/resolve.js +5 -0
- package/dist/modules/agent-tasks/resolve.js.map +1 -1
- package/dist/modules/agent-tasks/routes.d.ts +2 -0
- package/dist/modules/agent-tasks/routes.d.ts.map +1 -1
- package/dist/modules/attachedRecords.d.ts +22 -8
- package/dist/modules/attachedRecords.d.ts.map +1 -1
- package/dist/modules/attachedRecords.js +42 -5
- package/dist/modules/attachedRecords.js.map +1 -1
- package/dist/modules/auth/index.d.ts.map +1 -1
- package/dist/modules/auth/index.js +1 -0
- package/dist/modules/auth/index.js.map +1 -1
- package/dist/modules/auth/repository.d.ts +14 -0
- package/dist/modules/auth/repository.d.ts.map +1 -1
- package/dist/modules/auth/repository.js +20 -2
- package/dist/modules/auth/repository.js.map +1 -1
- package/dist/modules/auth/routes.d.ts.map +1 -1
- package/dist/modules/auth/routes.js +30 -0
- package/dist/modules/auth/routes.js.map +1 -1
- package/dist/modules/auth/service.d.ts +28 -0
- package/dist/modules/auth/service.d.ts.map +1 -1
- package/dist/modules/auth/service.js +72 -4
- package/dist/modules/auth/service.js.map +1 -1
- package/dist/modules/companies/repository.d.ts.map +1 -1
- package/dist/modules/companies/repository.js +1 -1
- package/dist/modules/companies/repository.js.map +1 -1
- package/dist/modules/companies/routes.d.ts +2 -4
- package/dist/modules/companies/routes.d.ts.map +1 -1
- package/dist/modules/companies/routes.js +6 -11
- package/dist/modules/companies/routes.js.map +1 -1
- package/dist/modules/companies/schema.d.ts +13 -28
- package/dist/modules/companies/schema.d.ts.map +1 -1
- package/dist/modules/companies/schema.js +2 -2
- package/dist/modules/companies/schema.js.map +1 -1
- package/dist/modules/companies/service.d.ts +2 -3
- package/dist/modules/companies/service.d.ts.map +1 -1
- package/dist/modules/companies/service.js +4 -7
- package/dist/modules/companies/service.js.map +1 -1
- package/dist/modules/core.d.ts.map +1 -1
- package/dist/modules/core.js +2 -0
- package/dist/modules/core.js.map +1 -1
- package/dist/modules/custom-fields/repository.d.ts.map +1 -1
- package/dist/modules/custom-fields/repository.js +2 -0
- package/dist/modules/custom-fields/repository.js.map +1 -1
- package/dist/modules/custom-fields/routes.d.ts +1 -0
- package/dist/modules/custom-fields/routes.d.ts.map +1 -1
- package/dist/modules/dashboard/index.d.ts.map +1 -1
- package/dist/modules/dashboard/index.js +1 -0
- package/dist/modules/dashboard/index.js.map +1 -1
- package/dist/modules/dashboard/repository.d.ts +13 -0
- package/dist/modules/dashboard/repository.d.ts.map +1 -1
- package/dist/modules/dashboard/repository.js +36 -0
- package/dist/modules/dashboard/repository.js.map +1 -1
- package/dist/modules/dashboard/routes.d.ts.map +1 -1
- package/dist/modules/dashboard/routes.js +13 -0
- package/dist/modules/dashboard/routes.js.map +1 -1
- package/dist/modules/dashboard/service.d.ts +11 -0
- package/dist/modules/dashboard/service.d.ts.map +1 -1
- package/dist/modules/dashboard/service.js +24 -1
- package/dist/modules/dashboard/service.js.map +1 -1
- package/dist/modules/decisions/repository.d.ts.map +1 -1
- package/dist/modules/decisions/repository.js +2 -0
- package/dist/modules/decisions/repository.js.map +1 -1
- package/dist/modules/decisions/routes.d.ts +2 -0
- package/dist/modules/decisions/routes.d.ts.map +1 -1
- package/dist/modules/events/attendances.d.ts +35 -0
- package/dist/modules/events/attendances.d.ts.map +1 -0
- package/dist/modules/events/attendances.js +157 -0
- package/dist/modules/events/attendances.js.map +1 -0
- package/dist/modules/events/catalog.d.ts +35 -0
- package/dist/modules/events/catalog.d.ts.map +1 -0
- package/dist/modules/events/catalog.js +11 -0
- package/dist/modules/events/catalog.js.map +1 -0
- package/dist/modules/events/index.d.ts +10 -0
- package/dist/modules/events/index.d.ts.map +1 -0
- package/dist/modules/events/index.js +47 -0
- package/dist/modules/events/index.js.map +1 -0
- package/dist/modules/events/repository.d.ts +67 -0
- package/dist/modules/events/repository.d.ts.map +1 -0
- package/dist/modules/events/repository.js +212 -0
- package/dist/modules/events/repository.js.map +1 -0
- package/dist/modules/events/routes.d.ts +136 -0
- package/dist/modules/events/routes.d.ts.map +1 -0
- package/dist/modules/events/routes.js +280 -0
- package/dist/modules/events/routes.js.map +1 -0
- package/dist/modules/events/schema.d.ts +626 -0
- package/dist/modules/events/schema.d.ts.map +1 -0
- package/dist/modules/events/schema.js +93 -0
- package/dist/modules/events/schema.js.map +1 -0
- package/dist/modules/events/service.d.ts +68 -0
- package/dist/modules/events/service.d.ts.map +1 -0
- package/dist/modules/events/service.js +262 -0
- package/dist/modules/events/service.js.map +1 -0
- package/dist/modules/events/tools.d.ts +8 -0
- package/dist/modules/events/tools.d.ts.map +1 -0
- package/dist/modules/events/tools.js +80 -0
- package/dist/modules/events/tools.js.map +1 -0
- package/dist/modules/forms/applySubmissionFields.d.ts.map +1 -1
- package/dist/modules/forms/applySubmissionFields.js +7 -5
- package/dist/modules/forms/applySubmissionFields.js.map +1 -1
- package/dist/modules/forms/fillAddresses.d.ts +4 -0
- package/dist/modules/forms/fillAddresses.d.ts.map +1 -0
- package/dist/modules/forms/fillAddresses.js +93 -0
- package/dist/modules/forms/fillAddresses.js.map +1 -0
- package/dist/modules/forms/repository.d.ts +2 -2
- package/dist/modules/forms/repository.d.ts.map +1 -1
- package/dist/modules/forms/repository.js.map +1 -1
- package/dist/modules/forms/routes.d.ts +2 -0
- package/dist/modules/forms/routes.d.ts.map +1 -1
- package/dist/modules/forms/routes.js +2 -2
- package/dist/modules/forms/routes.js.map +1 -1
- package/dist/modules/forms/schema.js +2 -2
- package/dist/modules/forms/schema.js.map +1 -1
- package/dist/modules/forms/service.js +2 -2
- package/dist/modules/forms/service.js.map +1 -1
- package/dist/modules/forms/submission.d.ts +1 -0
- package/dist/modules/forms/submission.d.ts.map +1 -1
- package/dist/modules/forms/submission.js +27 -4
- package/dist/modules/forms/submission.js.map +1 -1
- package/dist/modules/hiring/roles.d.ts.map +1 -1
- package/dist/modules/hiring/roles.js +2 -1
- package/dist/modules/hiring/roles.js.map +1 -1
- package/dist/modules/import-export/addresses.d.ts +22 -0
- package/dist/modules/import-export/addresses.d.ts.map +1 -0
- package/dist/modules/import-export/addresses.js +83 -0
- package/dist/modules/import-export/addresses.js.map +1 -0
- package/dist/modules/import-export/drafts.d.ts +3 -4
- package/dist/modules/import-export/drafts.d.ts.map +1 -1
- package/dist/modules/import-export/drafts.js +4 -5
- package/dist/modules/import-export/drafts.js.map +1 -1
- package/dist/modules/import-export/exportRows.d.ts.map +1 -1
- package/dist/modules/import-export/exportRows.js +3 -3
- package/dist/modules/import-export/exportRows.js.map +1 -1
- package/dist/modules/import-export/plan.d.ts.map +1 -1
- package/dist/modules/import-export/plan.js +17 -2
- package/dist/modules/import-export/plan.js.map +1 -1
- package/dist/modules/import-export/presets.d.ts.map +1 -1
- package/dist/modules/import-export/presets.js +52 -7
- package/dist/modules/import-export/presets.js.map +1 -1
- package/dist/modules/import-export/repository.d.ts +3 -4
- package/dist/modules/import-export/repository.d.ts.map +1 -1
- package/dist/modules/import-export/repository.js +2 -3
- package/dist/modules/import-export/repository.js.map +1 -1
- package/dist/modules/import-export/writes.d.ts.map +1 -1
- package/dist/modules/import-export/writes.js +8 -4
- package/dist/modules/import-export/writes.js.map +1 -1
- package/dist/modules/lists/routes.d.ts +4 -0
- package/dist/modules/lists/routes.d.ts.map +1 -1
- package/dist/modules/notes/routes.d.ts +2 -0
- package/dist/modules/notes/routes.d.ts.map +1 -1
- package/dist/modules/people/repository.d.ts.map +1 -1
- package/dist/modules/people/repository.js +1 -1
- package/dist/modules/people/repository.js.map +1 -1
- package/dist/modules/people/routes.d.ts +2 -2
- package/dist/modules/people/routes.js +6 -6
- package/dist/modules/people/routes.js.map +1 -1
- package/dist/modules/people/schema.d.ts +17 -14
- package/dist/modules/people/schema.d.ts.map +1 -1
- package/dist/modules/people/schema.js +6 -4
- package/dist/modules/people/schema.js.map +1 -1
- package/dist/modules/people/service.d.ts +2 -2
- package/dist/modules/people/service.d.ts.map +1 -1
- package/dist/modules/people/service.js +5 -4
- package/dist/modules/people/service.js.map +1 -1
- package/dist/modules/plans/repository.d.ts +2 -2
- package/dist/modules/plans/repository.d.ts.map +1 -1
- package/dist/modules/plans/routes.d.ts +1 -0
- package/dist/modules/plans/routes.d.ts.map +1 -1
- package/dist/modules/plans/routes.js +4 -4
- package/dist/modules/plans/routes.js.map +1 -1
- package/dist/modules/plans/schema.d.ts +2 -2
- package/dist/modules/plans/schema.d.ts.map +1 -1
- package/dist/modules/plans/schema.js +3 -3
- package/dist/modules/plans/schema.js.map +1 -1
- package/dist/modules/plans/service.d.ts +2 -2
- package/dist/modules/plans/service.d.ts.map +1 -1
- package/dist/modules/plans/service.js.map +1 -1
- package/dist/modules/plans/tools.js +2 -2
- package/dist/modules/plans/tools.js.map +1 -1
- package/dist/modules/recordTargets.d.ts.map +1 -1
- package/dist/modules/recordTargets.js +11 -0
- package/dist/modules/recordTargets.js.map +1 -1
- package/dist/modules/references.d.ts.map +1 -1
- package/dist/modules/references.js +2 -0
- package/dist/modules/references.js.map +1 -1
- package/dist/modules/sample-data/fixture.d.ts +26 -4
- package/dist/modules/sample-data/fixture.d.ts.map +1 -1
- package/dist/modules/sample-data/fixture.js +157 -23
- package/dist/modules/sample-data/fixture.js.map +1 -1
- package/dist/modules/sample-data/index.d.ts +5 -2
- package/dist/modules/sample-data/index.d.ts.map +1 -1
- package/dist/modules/sample-data/index.js +7 -2
- package/dist/modules/sample-data/index.js.map +1 -1
- package/dist/modules/sample-data/routes.d.ts.map +1 -1
- package/dist/modules/sample-data/routes.js +2 -0
- package/dist/modules/sample-data/routes.js.map +1 -1
- package/dist/modules/sample-data/service.d.ts +11 -3
- package/dist/modules/sample-data/service.d.ts.map +1 -1
- package/dist/modules/sample-data/service.js +452 -363
- package/dist/modules/sample-data/service.js.map +1 -1
- package/dist/modules/sample-data/tools.d.ts.map +1 -1
- package/dist/modules/sample-data/tools.js +4 -3
- package/dist/modules/sample-data/tools.js.map +1 -1
- package/dist/modules/search/index.d.ts +1 -1
- package/dist/modules/search/index.d.ts.map +1 -1
- package/dist/modules/search/index.js +2 -1
- package/dist/modules/search/index.js.map +1 -1
- package/dist/modules/search/repository.d.ts +1 -0
- package/dist/modules/search/repository.d.ts.map +1 -1
- package/dist/modules/search/repository.js +22 -0
- package/dist/modules/search/repository.js.map +1 -1
- package/dist/modules/search/service.d.ts +2 -2
- package/dist/modules/search/service.d.ts.map +1 -1
- package/dist/modules/search/service.js +4 -3
- package/dist/modules/search/service.js.map +1 -1
- package/dist/modules/smtp-email/index.d.ts +12 -4
- package/dist/modules/smtp-email/index.d.ts.map +1 -1
- package/dist/modules/smtp-email/index.js +25 -5
- package/dist/modules/smtp-email/index.js.map +1 -1
- package/dist/modules/workspace/repository.d.ts +2 -0
- package/dist/modules/workspace/repository.d.ts.map +1 -1
- package/dist/modules/workspace/repository.js +12 -0
- package/dist/modules/workspace/repository.js.map +1 -1
- package/dist/modules/workspace/routes.d.ts.map +1 -1
- package/dist/modules/workspace/routes.js +13 -0
- package/dist/modules/workspace/routes.js.map +1 -1
- package/dist/modules/workspace/service.d.ts +10 -0
- package/dist/modules/workspace/service.d.ts.map +1 -1
- package/dist/modules/workspace/service.js +34 -10
- package/dist/modules/workspace/service.js.map +1 -1
- package/dist/modules/workspace/starters.d.ts +17 -2
- package/dist/modules/workspace/starters.d.ts.map +1 -1
- package/dist/modules/workspace/starters.js +141 -14
- package/dist/modules/workspace/starters.js.map +1 -1
- package/dist/modules/workspace/workspaceAccessMiddleware.d.ts.map +1 -1
- package/dist/modules/workspace/workspaceAccessMiddleware.js +3 -4
- package/dist/modules/workspace/workspaceAccessMiddleware.js.map +1 -1
- package/dist/runtime/events.d.ts +1 -1
- package/dist/runtime/events.d.ts.map +1 -1
- package/dist/runtime/events.js +2 -0
- package/dist/runtime/events.js.map +1 -1
- package/dist/runtime/jobs.d.ts +63 -0
- package/dist/runtime/jobs.d.ts.map +1 -0
- package/dist/runtime/jobs.js +231 -0
- package/dist/runtime/jobs.js.map +1 -0
- package/dist/runtime/module.d.ts +20 -0
- package/dist/runtime/module.d.ts.map +1 -1
- package/dist/runtime/registry.d.ts +8 -0
- package/dist/runtime/registry.d.ts.map +1 -1
- package/dist/runtime/registry.js +117 -7
- package/dist/runtime/registry.js.map +1 -1
- package/dist/runtime/transaction.d.ts +19 -0
- package/dist/runtime/transaction.d.ts.map +1 -1
- package/dist/runtime/transaction.js +7 -0
- package/dist/runtime/transaction.js.map +1 -1
- package/dist/schema/index.d.ts +1 -0
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/schema/index.js +1 -0
- package/dist/schema/index.js.map +1 -1
- package/dist/testing/app.d.ts +15 -0
- package/dist/testing/app.d.ts.map +1 -1
- package/dist/testing/app.js +7 -1
- package/dist/testing/app.js.map +1 -1
- package/dist/testing/client.d.ts.map +1 -1
- package/dist/testing/client.js +9 -1
- package/dist/testing/client.js.map +1 -1
- package/dist/testing/database.d.ts +7 -0
- package/dist/testing/database.d.ts.map +1 -1
- package/dist/testing/database.js +26 -2
- package/dist/testing/database.js.map +1 -1
- package/dist/testing/services.d.ts +7 -0
- package/dist/testing/services.d.ts.map +1 -1
- package/dist/testing/services.js +8 -1
- package/dist/testing/services.js.map +1 -1
- package/dist/worker.d.ts +15 -0
- package/dist/worker.d.ts.map +1 -0
- package/dist/worker.js +19 -0
- package/dist/worker.js.map +1 -0
- package/migrations/0044_events.sql +79 -0
- package/migrations/0045_addresses.sql +66 -0
- package/migrations/0046_drop_company_website.sql +66 -0
- package/migrations/meta/_journal.json +21 -0
- package/package.json +4 -3
- package/src/app.ts +25 -1
- package/src/boot.ts +21 -2
- package/src/index.ts +17 -0
- package/src/lib/apiKeyScopes.ts +6 -0
- package/src/lib/columns.ts +12 -3
- package/src/lib/config.ts +21 -0
- package/src/lib/egress.ts +8 -4
- package/src/lib/errors.ts +16 -9
- package/src/lib/ids.ts +11 -0
- package/src/lib/jobs.ts +126 -0
- package/src/lib/kelpieConfigFile.ts +69 -1
- package/src/modules/agent-tasks/catalog.ts +33 -1
- package/src/modules/agent-tasks/index.ts +1 -0
- package/src/modules/agent-tasks/resolve.ts +11 -0
- package/src/modules/attachedRecords.ts +85 -13
- package/src/modules/auth/index.ts +1 -0
- package/src/modules/auth/repository.ts +34 -3
- package/src/modules/auth/routes.ts +44 -1
- package/src/modules/auth/service.ts +118 -4
- package/src/modules/companies/repository.ts +1 -0
- package/src/modules/companies/routes.ts +6 -11
- package/src/modules/companies/schema.ts +3 -3
- package/src/modules/companies/service.ts +6 -10
- package/src/modules/core.ts +2 -0
- package/src/modules/custom-fields/repository.ts +2 -0
- package/src/modules/dashboard/index.ts +1 -0
- package/src/modules/dashboard/repository.ts +64 -0
- package/src/modules/dashboard/routes.ts +15 -0
- package/src/modules/dashboard/service.ts +48 -0
- package/src/modules/decisions/repository.ts +2 -0
- package/src/modules/events/attendances.ts +245 -0
- package/src/modules/events/catalog.ts +36 -0
- package/src/modules/events/index.ts +54 -0
- package/src/modules/events/repository.ts +396 -0
- package/src/modules/events/routes.ts +405 -0
- package/src/modules/events/schema.ts +148 -0
- package/src/modules/events/service.ts +443 -0
- package/src/modules/events/tools.ts +118 -0
- package/src/modules/forms/applySubmissionFields.ts +7 -5
- package/src/modules/forms/fillAddresses.ts +185 -0
- package/src/modules/forms/repository.ts +3 -3
- package/src/modules/forms/routes.ts +2 -2
- package/src/modules/forms/schema.ts +2 -2
- package/src/modules/forms/service.ts +2 -2
- package/src/modules/forms/submission.ts +34 -4
- package/src/modules/hiring/roles.ts +2 -1
- package/src/modules/import-export/addresses.ts +150 -0
- package/src/modules/import-export/drafts.ts +7 -9
- package/src/modules/import-export/exportRows.ts +3 -3
- package/src/modules/import-export/plan.ts +30 -2
- package/src/modules/import-export/presets.ts +55 -7
- package/src/modules/import-export/repository.ts +5 -7
- package/src/modules/import-export/writes.ts +8 -4
- package/src/modules/people/repository.ts +1 -0
- package/src/modules/people/routes.ts +6 -6
- package/src/modules/people/schema.ts +7 -5
- package/src/modules/people/service.ts +7 -5
- package/src/modules/plans/repository.ts +2 -2
- package/src/modules/plans/routes.ts +6 -6
- package/src/modules/plans/schema.ts +4 -4
- package/src/modules/plans/service.ts +3 -3
- package/src/modules/plans/tools.ts +2 -2
- package/src/modules/recordTargets.ts +14 -0
- package/src/modules/references.ts +2 -0
- package/src/modules/sample-data/fixture.ts +208 -27
- package/src/modules/sample-data/index.ts +7 -2
- package/src/modules/sample-data/routes.ts +2 -0
- package/src/modules/sample-data/service.ts +166 -49
- package/src/modules/sample-data/tools.ts +4 -3
- package/src/modules/search/index.ts +2 -1
- package/src/modules/search/repository.ts +32 -1
- package/src/modules/search/service.ts +6 -4
- package/src/modules/smtp-email/index.ts +32 -10
- package/src/modules/workspace/repository.ts +18 -0
- package/src/modules/workspace/routes.ts +16 -0
- package/src/modules/workspace/service.ts +59 -14
- package/src/modules/workspace/starters.ts +275 -14
- package/src/modules/workspace/workspaceAccessMiddleware.ts +3 -4
- package/src/runtime/events.ts +2 -0
- package/src/runtime/jobs.ts +358 -0
- package/src/runtime/module.ts +20 -0
- package/src/runtime/registry.ts +146 -8
- package/src/runtime/transaction.ts +43 -0
- package/src/schema/index.ts +1 -0
- package/src/testing/app.ts +22 -1
- package/src/testing/client.ts +11 -1
- package/src/testing/database.ts +35 -2
- package/src/testing/services.ts +15 -1
- package/src/worker.ts +20 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
CREATE TABLE "events" (
|
|
2
|
+
"id" text PRIMARY KEY NOT NULL,
|
|
3
|
+
"workspace_id" text NOT NULL,
|
|
4
|
+
"name" text NOT NULL,
|
|
5
|
+
"kind" text DEFAULT '' NOT NULL,
|
|
6
|
+
"starts_at" timestamp with time zone NOT NULL,
|
|
7
|
+
"ends_at" timestamp with time zone NOT NULL,
|
|
8
|
+
"timezone" text NOT NULL,
|
|
9
|
+
"location" text DEFAULT '' NOT NULL,
|
|
10
|
+
"meeting_url" text,
|
|
11
|
+
"format" text NOT NULL,
|
|
12
|
+
"details" text DEFAULT '' NOT NULL,
|
|
13
|
+
"status" text NOT NULL,
|
|
14
|
+
"owner_id" text,
|
|
15
|
+
"summary" text DEFAULT '' NOT NULL,
|
|
16
|
+
"tags" text[] DEFAULT '{}' NOT NULL,
|
|
17
|
+
"custom_fields" jsonb DEFAULT '{}'::jsonb NOT NULL,
|
|
18
|
+
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
|
19
|
+
"updated_at" timestamp with time zone DEFAULT now() NOT NULL,
|
|
20
|
+
"search_vector" "tsvector" GENERATED ALWAYS AS (setweight(to_tsvector('english', regexp_replace(coalesce("events"."name"::text, ''), '[^[:alnum:]]+', ' ', 'g')), 'A') || setweight(to_tsvector('english', regexp_replace(coalesce("events"."details"::text, ''), '[^[:alnum:]]+', ' ', 'g')), 'B') || setweight(to_tsvector('english', regexp_replace(coalesce("events"."summary"::text, ''), '[^[:alnum:]]+', ' ', 'g')), 'B') || setweight(to_tsvector('english', regexp_replace(coalesce("events"."kind"::text, ''), '[^[:alnum:]]+', ' ', 'g')), 'C') || setweight(to_tsvector('english', regexp_replace(coalesce("events"."location"::text, ''), '[^[:alnum:]]+', ' ', 'g')), 'C') || setweight(to_tsvector('english', regexp_replace(coalesce(kelpie_text_array_to_string("events"."tags"), ''), '[^[:alnum:]]+', ' ', 'g')), 'C')) STORED
|
|
21
|
+
);
|
|
22
|
+
--> statement-breakpoint
|
|
23
|
+
CREATE TABLE "attendances" (
|
|
24
|
+
"id" text PRIMARY KEY NOT NULL,
|
|
25
|
+
"workspace_id" text NOT NULL,
|
|
26
|
+
"event_id" text NOT NULL,
|
|
27
|
+
"person_id" text NOT NULL,
|
|
28
|
+
"status" text NOT NULL,
|
|
29
|
+
"source" text NOT NULL,
|
|
30
|
+
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
|
31
|
+
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
|
32
|
+
);
|
|
33
|
+
--> statement-breakpoint
|
|
34
|
+
CREATE TABLE "event_associations" (
|
|
35
|
+
"id" text PRIMARY KEY NOT NULL,
|
|
36
|
+
"workspace_id" text NOT NULL,
|
|
37
|
+
"event_id" text NOT NULL,
|
|
38
|
+
"target_type" text NOT NULL,
|
|
39
|
+
"target_id" text NOT NULL,
|
|
40
|
+
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
|
41
|
+
);
|
|
42
|
+
--> statement-breakpoint
|
|
43
|
+
ALTER TABLE "notes" DROP CONSTRAINT "notes_target_type_check";--> statement-breakpoint
|
|
44
|
+
ALTER TABLE "activities" DROP CONSTRAINT "activities_target_type_check";--> statement-breakpoint
|
|
45
|
+
ALTER TABLE "decisions" DROP CONSTRAINT "decisions_target_type_check";--> statement-breakpoint
|
|
46
|
+
ALTER TABLE "lists" DROP CONSTRAINT "lists_target_type_check";--> statement-breakpoint
|
|
47
|
+
ALTER TABLE "list_members" DROP CONSTRAINT "list_members_target_type_check";--> statement-breakpoint
|
|
48
|
+
ALTER TABLE "plan_items" DROP CONSTRAINT "plan_items_target_type_check";--> statement-breakpoint
|
|
49
|
+
ALTER TABLE "form_attach_targets" DROP CONSTRAINT "form_attach_targets_target_type_check";--> statement-breakpoint
|
|
50
|
+
ALTER TABLE "custom_field_definitions" DROP CONSTRAINT "custom_field_definitions_object_type_check";--> statement-breakpoint
|
|
51
|
+
ALTER TABLE "events" ADD CONSTRAINT "events_workspace_id_workspaces_id_fk" FOREIGN KEY ("workspace_id") REFERENCES "public"."workspaces"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
52
|
+
ALTER TABLE "events" ADD CONSTRAINT "events_owner_id_workspace_members_id_fk" FOREIGN KEY ("owner_id") REFERENCES "public"."workspace_members"("id") ON DELETE restrict ON UPDATE no action;--> statement-breakpoint
|
|
53
|
+
ALTER TABLE "events" ADD CONSTRAINT "events_format_check" CHECK ("events"."format" in ('in_person', 'virtual', 'hybrid'));--> statement-breakpoint
|
|
54
|
+
ALTER TABLE "events" ADD CONSTRAINT "events_status_check" CHECK ("events"."status" in ('draft', 'scheduled', 'cancelled'));--> statement-breakpoint
|
|
55
|
+
CREATE INDEX "events_workspace_idx" ON "events" USING btree ("workspace_id");--> statement-breakpoint
|
|
56
|
+
CREATE INDEX "events_starts_idx" ON "events" USING btree ("workspace_id","starts_at");--> statement-breakpoint
|
|
57
|
+
CREATE INDEX "events_search_idx" ON "events" USING gin ("search_vector");--> statement-breakpoint
|
|
58
|
+
ALTER TABLE "attendances" ADD CONSTRAINT "attendances_workspace_id_workspaces_id_fk" FOREIGN KEY ("workspace_id") REFERENCES "public"."workspaces"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
59
|
+
ALTER TABLE "attendances" ADD CONSTRAINT "attendances_event_id_events_id_fk" FOREIGN KEY ("event_id") REFERENCES "public"."events"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
60
|
+
ALTER TABLE "attendances" ADD CONSTRAINT "attendances_person_id_people_id_fk" FOREIGN KEY ("person_id") REFERENCES "public"."people"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
61
|
+
ALTER TABLE "attendances" ADD CONSTRAINT "attendances_event_person_key" UNIQUE ("event_id","person_id");--> statement-breakpoint
|
|
62
|
+
ALTER TABLE "attendances" ADD CONSTRAINT "attendances_status_check" CHECK ("attendances"."status" in ('registered', 'attended', 'no_show', 'cancelled'));--> statement-breakpoint
|
|
63
|
+
ALTER TABLE "attendances" ADD CONSTRAINT "attendances_source_check" CHECK ("attendances"."source" in ('manual', 'form'));--> statement-breakpoint
|
|
64
|
+
CREATE INDEX "attendances_workspace_idx" ON "attendances" USING btree ("workspace_id");--> statement-breakpoint
|
|
65
|
+
CREATE INDEX "attendances_event_idx" ON "attendances" USING btree ("event_id");--> statement-breakpoint
|
|
66
|
+
CREATE INDEX "attendances_person_idx" ON "attendances" USING btree ("person_id");--> statement-breakpoint
|
|
67
|
+
ALTER TABLE "event_associations" ADD CONSTRAINT "event_associations_workspace_id_workspaces_id_fk" FOREIGN KEY ("workspace_id") REFERENCES "public"."workspaces"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
68
|
+
ALTER TABLE "event_associations" ADD CONSTRAINT "event_associations_event_id_events_id_fk" FOREIGN KEY ("event_id") REFERENCES "public"."events"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
69
|
+
ALTER TABLE "event_associations" ADD CONSTRAINT "event_associations_event_target_key" UNIQUE ("event_id","target_type","target_id");--> statement-breakpoint
|
|
70
|
+
ALTER TABLE "event_associations" ADD CONSTRAINT "event_associations_target_type_check" CHECK ("event_associations"."target_type" in ('person', 'company', 'deal', 'opportunity', 'partnership', 'raise', 'enquiry', 'role', 'candidate'));--> statement-breakpoint
|
|
71
|
+
CREATE INDEX "event_associations_target_idx" ON "event_associations" USING btree ("workspace_id","target_type","target_id");--> statement-breakpoint
|
|
72
|
+
ALTER TABLE "notes" ADD CONSTRAINT "notes_target_type_check" CHECK ("notes"."target_type" in ('person', 'company', 'deal', 'opportunity', 'partnership', 'raise', 'enquiry', 'candidate', 'event', 'attendance'));--> statement-breakpoint
|
|
73
|
+
ALTER TABLE "activities" ADD CONSTRAINT "activities_target_type_check" CHECK ("activities"."target_type" in ('person', 'company', 'deal', 'opportunity', 'partnership', 'raise', 'enquiry', 'candidate', 'event', 'attendance'));--> statement-breakpoint
|
|
74
|
+
ALTER TABLE "decisions" ADD CONSTRAINT "decisions_target_type_check" CHECK ("decisions"."target_type" in ('person', 'company', 'deal', 'opportunity', 'partnership', 'raise', 'enquiry', 'candidate', 'event', 'attendance'));--> statement-breakpoint
|
|
75
|
+
ALTER TABLE "lists" ADD CONSTRAINT "lists_target_type_check" CHECK ("lists"."target_type" in ('person', 'company', 'deal', 'opportunity', 'partnership', 'raise', 'enquiry', 'candidate', 'event', 'attendance'));--> statement-breakpoint
|
|
76
|
+
ALTER TABLE "list_members" ADD CONSTRAINT "list_members_target_type_check" CHECK ("list_members"."target_type" in ('person', 'company', 'deal', 'opportunity', 'partnership', 'raise', 'enquiry', 'candidate', 'event', 'attendance'));--> statement-breakpoint
|
|
77
|
+
ALTER TABLE "plan_items" ADD CONSTRAINT "plan_items_target_type_check" CHECK ("plan_items"."target_type" in ('enquiry', 'deal', 'opportunity', 'raise', 'partnership', 'event'));--> statement-breakpoint
|
|
78
|
+
ALTER TABLE "form_attach_targets" ADD CONSTRAINT "form_attach_targets_target_type_check" CHECK ("form_attach_targets"."target_type" in ('enquiry', 'deal', 'opportunity', 'raise', 'partnership', 'event'));--> statement-breakpoint
|
|
79
|
+
ALTER TABLE "custom_field_definitions" ADD CONSTRAINT "custom_field_definitions_object_type_check" CHECK ("custom_field_definitions"."object_type" in ('person', 'company', 'deal', 'opportunity', 'partnership', 'raise', 'enquiry', 'event'));
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
-- Postal addresses on people and companies. jsonb, like phones and social
|
|
2
|
+
-- profiles: nothing queries into the parts, and the route layer is what
|
|
3
|
+
-- validates the shape. `kelpie_addresses_search_text` is hand-added because
|
|
4
|
+
-- Drizzle Kit does not manage functions, the same reason `kelpie_text_array_to_string`
|
|
5
|
+
-- lives in 0015. jsonb_array_elements is STABLE; the wrapper asserts IMMUTABLE
|
|
6
|
+
-- so a generated search_vector can read it.
|
|
7
|
+
--
|
|
8
|
+
-- Stored keys match the TypeScript shape (`postalCode`), not the wire
|
|
9
|
+
-- (`postal_code`). The route maps at the boundary the same way it does for
|
|
10
|
+
-- every other resource.
|
|
11
|
+
CREATE OR REPLACE FUNCTION kelpie_addresses_search_text(jsonb) RETURNS text
|
|
12
|
+
LANGUAGE sql IMMUTABLE PARALLEL SAFE STRICT
|
|
13
|
+
AS $$
|
|
14
|
+
SELECT coalesce((
|
|
15
|
+
SELECT string_agg(trim(both ' ' from concat_ws(' ',
|
|
16
|
+
nullif(elem->>'line1', ''),
|
|
17
|
+
nullif(elem->>'line2', ''),
|
|
18
|
+
nullif(elem->>'city', ''),
|
|
19
|
+
nullif(elem->>'region', ''),
|
|
20
|
+
nullif(elem->>'postalCode', ''),
|
|
21
|
+
nullif(elem->>'country', '')
|
|
22
|
+
)), ' ')
|
|
23
|
+
FROM jsonb_array_elements($1) AS t(elem)
|
|
24
|
+
), '')
|
|
25
|
+
$$;
|
|
26
|
+
--> statement-breakpoint
|
|
27
|
+
ALTER TABLE "people" ADD COLUMN "addresses" jsonb DEFAULT '[]'::jsonb NOT NULL;--> statement-breakpoint
|
|
28
|
+
ALTER TABLE "companies" ADD COLUMN "addresses" jsonb DEFAULT '[]'::jsonb NOT NULL;--> statement-breakpoint
|
|
29
|
+
UPDATE "people"
|
|
30
|
+
SET "addresses" = jsonb_build_array(
|
|
31
|
+
jsonb_build_object(
|
|
32
|
+
'kind', 'home',
|
|
33
|
+
'line1', null,
|
|
34
|
+
'line2', null,
|
|
35
|
+
'city', "location",
|
|
36
|
+
'region', null,
|
|
37
|
+
'postalCode', null,
|
|
38
|
+
'country', null,
|
|
39
|
+
'primary', true
|
|
40
|
+
)
|
|
41
|
+
)
|
|
42
|
+
WHERE "location" IS NOT NULL AND btrim("location") <> '';--> statement-breakpoint
|
|
43
|
+
UPDATE "companies"
|
|
44
|
+
SET "addresses" = jsonb_build_array(
|
|
45
|
+
jsonb_build_object(
|
|
46
|
+
'kind', 'hq',
|
|
47
|
+
'line1', null,
|
|
48
|
+
'line2', null,
|
|
49
|
+
'city', "hq",
|
|
50
|
+
'region', null,
|
|
51
|
+
'postalCode', null,
|
|
52
|
+
'country', null,
|
|
53
|
+
'primary', true
|
|
54
|
+
)
|
|
55
|
+
)
|
|
56
|
+
WHERE "hq" IS NOT NULL AND btrim("hq") <> '';--> statement-breakpoint
|
|
57
|
+
UPDATE "form_fields" SET "map_to" = 'person.address.city' WHERE "map_to" = 'person.location';--> statement-breakpoint
|
|
58
|
+
UPDATE "form_fields" SET "map_to" = 'company.address.city' WHERE "map_to" = 'company.hq';--> statement-breakpoint
|
|
59
|
+
ALTER TABLE "people" DROP COLUMN "location";--> statement-breakpoint
|
|
60
|
+
ALTER TABLE "companies" DROP COLUMN "hq";--> statement-breakpoint
|
|
61
|
+
ALTER TABLE "people" DROP COLUMN "search_vector";--> statement-breakpoint
|
|
62
|
+
ALTER TABLE "people" ADD COLUMN "search_vector" "tsvector" GENERATED ALWAYS AS (setweight(to_tsvector('english', regexp_replace(coalesce("people"."name"::text, ''), '[^[:alnum:]]+', ' ', 'g')), 'A') || setweight(to_tsvector('english', regexp_replace(coalesce("people"."first_name"::text, ''), '[^[:alnum:]]+', ' ', 'g')), 'A') || setweight(to_tsvector('english', regexp_replace(coalesce("people"."last_name"::text, ''), '[^[:alnum:]]+', ' ', 'g')), 'A') || setweight(to_tsvector('english', regexp_replace(coalesce("people"."email"::text, ''), '[^[:alnum:]]+', ' ', 'g')), 'B') || setweight(to_tsvector('english', regexp_replace(coalesce("people"."summary"::text, ''), '[^[:alnum:]]+', ' ', 'g')), 'B') || setweight(to_tsvector('english', regexp_replace(coalesce(kelpie_text_array_to_string("people"."tags"), ''), '[^[:alnum:]]+', ' ', 'g')), 'C') || setweight(to_tsvector('english', regexp_replace(coalesce(kelpie_addresses_search_text("people"."addresses"), ''), '[^[:alnum:]]+', ' ', 'g')), 'C')) STORED;--> statement-breakpoint
|
|
63
|
+
CREATE INDEX "people_search_idx" ON "people" USING gin ("search_vector");--> statement-breakpoint
|
|
64
|
+
ALTER TABLE "companies" DROP COLUMN "search_vector";--> statement-breakpoint
|
|
65
|
+
ALTER TABLE "companies" ADD COLUMN "search_vector" "tsvector" GENERATED ALWAYS AS (setweight(to_tsvector('english', regexp_replace(coalesce("companies"."name"::text, ''), '[^[:alnum:]]+', ' ', 'g')), 'A') || setweight(to_tsvector('english', regexp_replace(coalesce("companies"."domain"::text, ''), '[^[:alnum:]]+', ' ', 'g')), 'B') || setweight(to_tsvector('english', regexp_replace(coalesce("companies"."industry"::text, ''), '[^[:alnum:]]+', ' ', 'g')), 'B') || setweight(to_tsvector('english', regexp_replace(coalesce("companies"."description"::text, ''), '[^[:alnum:]]+', ' ', 'g')), 'B') || setweight(to_tsvector('english', regexp_replace(coalesce("companies"."summary"::text, ''), '[^[:alnum:]]+', ' ', 'g')), 'B') || setweight(to_tsvector('english', regexp_replace(coalesce(kelpie_text_array_to_string("companies"."tags"), ''), '[^[:alnum:]]+', ' ', 'g')), 'C') || setweight(to_tsvector('english', regexp_replace(coalesce(kelpie_text_array_to_string("companies"."tech_stack"), ''), '[^[:alnum:]]+', ' ', 'g')), 'C') || setweight(to_tsvector('english', regexp_replace(coalesce(kelpie_addresses_search_text("companies"."addresses"), ''), '[^[:alnum:]]+', ' ', 'g')), 'C')) STORED;--> statement-breakpoint
|
|
66
|
+
CREATE INDEX "companies_search_idx" ON "companies" USING gin ("search_vector");
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
-- Domain is the company homepage. `website` was a second URL that almost
|
|
2
|
+
-- always repeated `https://` plus the same host. Fill a blank domain from
|
|
3
|
+
-- that URL, then drop the column. Visit on the heading opens https://{domain}.
|
|
4
|
+
UPDATE "companies" AS c
|
|
5
|
+
SET "domain" = v.host
|
|
6
|
+
FROM (
|
|
7
|
+
SELECT DISTINCT ON (workspace_id, host)
|
|
8
|
+
id,
|
|
9
|
+
workspace_id,
|
|
10
|
+
host
|
|
11
|
+
FROM (
|
|
12
|
+
SELECT
|
|
13
|
+
id,
|
|
14
|
+
workspace_id,
|
|
15
|
+
NULLIF(
|
|
16
|
+
regexp_replace(
|
|
17
|
+
regexp_replace(
|
|
18
|
+
regexp_replace(lower(trim(website)), '^[a-z][a-z0-9+.-]*://', ''),
|
|
19
|
+
'/.*$',
|
|
20
|
+
''
|
|
21
|
+
),
|
|
22
|
+
'\.$',
|
|
23
|
+
''
|
|
24
|
+
),
|
|
25
|
+
''
|
|
26
|
+
) AS host
|
|
27
|
+
FROM "companies"
|
|
28
|
+
WHERE "domain" IS NULL AND "website" IS NOT NULL
|
|
29
|
+
) AS sourced
|
|
30
|
+
WHERE host IS NOT NULL
|
|
31
|
+
ORDER BY workspace_id, host, id
|
|
32
|
+
) AS v
|
|
33
|
+
WHERE c.id = v.id
|
|
34
|
+
AND NOT EXISTS (
|
|
35
|
+
SELECT 1
|
|
36
|
+
FROM "companies" AS other
|
|
37
|
+
WHERE other.workspace_id = c.workspace_id
|
|
38
|
+
AND other.domain = v.host
|
|
39
|
+
AND other.id <> c.id
|
|
40
|
+
);
|
|
41
|
+
--> statement-breakpoint
|
|
42
|
+
ALTER TABLE "companies" DROP COLUMN "website";
|
|
43
|
+
--> statement-breakpoint
|
|
44
|
+
UPDATE "form_fields"
|
|
45
|
+
SET "map_to" = 'company.domain'
|
|
46
|
+
WHERE "map_to" = 'company.website';
|
|
47
|
+
--> statement-breakpoint
|
|
48
|
+
UPDATE "import_jobs"
|
|
49
|
+
SET "column_map" = ("column_map" - 'website') || jsonb_build_object('domain', "column_map"->>'website')
|
|
50
|
+
WHERE jsonb_typeof("column_map") = 'object'
|
|
51
|
+
AND "column_map" ? 'website'
|
|
52
|
+
AND "column_map"->>'website' IS NOT NULL
|
|
53
|
+
AND NOT (
|
|
54
|
+
"column_map" ? 'domain'
|
|
55
|
+
AND "column_map"->>'domain' IS NOT NULL
|
|
56
|
+
);
|
|
57
|
+
--> statement-breakpoint
|
|
58
|
+
UPDATE "import_jobs"
|
|
59
|
+
SET "column_map" = "column_map" - 'website'
|
|
60
|
+
WHERE jsonb_typeof("column_map") = 'object'
|
|
61
|
+
AND "column_map" ? 'website';
|
|
62
|
+
--> statement-breakpoint
|
|
63
|
+
UPDATE "import_jobs"
|
|
64
|
+
SET "match_key" = 'domain'
|
|
65
|
+
WHERE "object" = 'companies'
|
|
66
|
+
AND "match_key" = 'website';
|
|
@@ -309,6 +309,27 @@
|
|
|
309
309
|
"when": 1788700000000,
|
|
310
310
|
"tag": "0043_external_sign_in",
|
|
311
311
|
"breakpoints": true
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
"idx": 44,
|
|
315
|
+
"version": "7",
|
|
316
|
+
"when": 1788800000000,
|
|
317
|
+
"tag": "0044_events",
|
|
318
|
+
"breakpoints": true
|
|
319
|
+
},
|
|
320
|
+
{
|
|
321
|
+
"idx": 45,
|
|
322
|
+
"version": "7",
|
|
323
|
+
"when": 1788900000000,
|
|
324
|
+
"tag": "0045_addresses",
|
|
325
|
+
"breakpoints": true
|
|
326
|
+
},
|
|
327
|
+
{
|
|
328
|
+
"idx": 46,
|
|
329
|
+
"version": "7",
|
|
330
|
+
"when": 1789000000000,
|
|
331
|
+
"tag": "0046_drop_company_website",
|
|
332
|
+
"breakpoints": true
|
|
312
333
|
}
|
|
313
334
|
]
|
|
314
335
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kelpie/server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"description": "The Kelpie service as a library: module runtime, core CRM modules, REST API, MCP surface, and the shared migration pipeline.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"kelpie",
|
|
@@ -52,13 +52,14 @@
|
|
|
52
52
|
},
|
|
53
53
|
"dependencies": {
|
|
54
54
|
"@hono/node-server": "^2.0.12",
|
|
55
|
-
"@kelpie/schemas": "^0.
|
|
55
|
+
"@kelpie/schemas": "^0.14.0",
|
|
56
56
|
"@node-rs/argon2": "^2.0.2",
|
|
57
57
|
"drizzle-orm": "^0.45.2",
|
|
58
58
|
"emittery": "^2.0.0",
|
|
59
|
-
"hono": "^4.
|
|
59
|
+
"hono": "^4.13.7",
|
|
60
60
|
"mailgen": "^2.0.36",
|
|
61
61
|
"nodemailer": "^9.0.5",
|
|
62
|
+
"pg-boss": "^12.31.0",
|
|
62
63
|
"postgres": "^3.4.9",
|
|
63
64
|
"ulid": "^3.0.2",
|
|
64
65
|
"winston": "^3.19.0",
|
package/src/app.ts
CHANGED
|
@@ -2,6 +2,8 @@ import { Hono } from 'hono'
|
|
|
2
2
|
import type { Context } from 'hono'
|
|
3
3
|
import { cors } from 'hono/cors'
|
|
4
4
|
|
|
5
|
+
import type { PublicRegion } from '@kelpie/schemas'
|
|
6
|
+
|
|
5
7
|
import type { RuntimeMode } from './lib/config.ts'
|
|
6
8
|
import type { DatabaseProbe } from './lib/database.ts'
|
|
7
9
|
import { AppError, internalErrorBody, toErrorBody } from './lib/errors.ts'
|
|
@@ -65,6 +67,18 @@ export interface AppDependencies {
|
|
|
65
67
|
* name itself".
|
|
66
68
|
*/
|
|
67
69
|
readonly siteName?: string | undefined
|
|
70
|
+
/**
|
|
71
|
+
* Reported through `GET /v1/public/config` so the sign-up page can show a
|
|
72
|
+
* "signups are closed" message instead of the form. Defaults to `true`
|
|
73
|
+
* (open), matching the config layer's own default.
|
|
74
|
+
*/
|
|
75
|
+
readonly signupsEnabled?: boolean
|
|
76
|
+
/**
|
|
77
|
+
* Reported through `GET /v1/public/config` so signed-out pages can draw a
|
|
78
|
+
* region switcher. Defaults to `[]` (no switcher), matching a self-hosted
|
|
79
|
+
* install that never declared regions.
|
|
80
|
+
*/
|
|
81
|
+
readonly regions?: readonly PublicRegion[]
|
|
68
82
|
}
|
|
69
83
|
|
|
70
84
|
/** Per-request values the middleware chain sets and handlers read. */
|
|
@@ -185,9 +199,19 @@ export function createApp(dependencies: AppDependencies): Hono<AppBindings> {
|
|
|
185
199
|
// exists to be visible; nothing here is sensitive.
|
|
186
200
|
const runtimeMode: RuntimeMode = dependencies.runtimeMode ?? 'production'
|
|
187
201
|
const siteName = dependencies.siteName ?? null
|
|
202
|
+
const signupsEnabled = dependencies.signupsEnabled ?? true
|
|
203
|
+
const regions = dependencies.regions ?? []
|
|
188
204
|
|
|
189
205
|
app.get(`${PUBLIC_ROUTE_PREFIX}/config`, (context) =>
|
|
190
|
-
context.json(
|
|
206
|
+
context.json(
|
|
207
|
+
{
|
|
208
|
+
runtime_mode: runtimeMode,
|
|
209
|
+
site_name: siteName,
|
|
210
|
+
signups_enabled: signupsEnabled,
|
|
211
|
+
regions,
|
|
212
|
+
},
|
|
213
|
+
200,
|
|
214
|
+
),
|
|
191
215
|
)
|
|
192
216
|
|
|
193
217
|
for (const { router } of dependencies.contributions.routers) {
|
package/src/boot.ts
CHANGED
|
@@ -11,6 +11,8 @@ import { readModuleConfigFile } from './lib/moduleConfig.ts'
|
|
|
11
11
|
import { resolveActorFrom } from './modules/auth/credentials.ts'
|
|
12
12
|
import type { CredentialDependencies } from './modules/auth/credentials.ts'
|
|
13
13
|
import { createEventBus } from './runtime/events.ts'
|
|
14
|
+
import { createJobsRuntime } from './runtime/jobs.ts'
|
|
15
|
+
import type { JobsRuntime } from './runtime/jobs.ts'
|
|
14
16
|
import { registerModules } from './runtime/registry.ts'
|
|
15
17
|
import type { ModuleContributions } from './runtime/registry.ts'
|
|
16
18
|
import { createTransactionScope } from './runtime/transaction.ts'
|
|
@@ -34,6 +36,14 @@ export interface AssemblyBoot {
|
|
|
34
36
|
readonly createId: IdFactory
|
|
35
37
|
readonly credentials: CredentialDependencies
|
|
36
38
|
readonly contributions: ModuleContributions
|
|
39
|
+
/**
|
|
40
|
+
* The pg-boss-backed job runtime the modules registered against. The
|
|
41
|
+
* entry point owns its lifecycle: call `jobs.migrate()` alongside
|
|
42
|
+
* `runMigrations`, `jobs.start()` before serving, and — on the worker —
|
|
43
|
+
* `jobs.startWorking()` right after. `jobs.stop()` drains before
|
|
44
|
+
* `database.close()`.
|
|
45
|
+
*/
|
|
46
|
+
readonly jobs: JobsRuntime
|
|
37
47
|
}
|
|
38
48
|
|
|
39
49
|
/**
|
|
@@ -60,6 +70,7 @@ export async function bootAssembly(
|
|
|
60
70
|
})
|
|
61
71
|
const database = connectDatabase(config.databaseUrl, logger)
|
|
62
72
|
const events = createEventBus(logger)
|
|
73
|
+
const jobs = createJobsRuntime({ connectionString: config.databaseUrl, logger })
|
|
63
74
|
const createId = createIdFactory()
|
|
64
75
|
const credentials: CredentialDependencies = { db: database.db, now: () => new Date() }
|
|
65
76
|
const moduleConfig = readModuleConfigFile(config.moduleConfigPath)
|
|
@@ -69,14 +80,22 @@ export async function bootAssembly(
|
|
|
69
80
|
logger,
|
|
70
81
|
events,
|
|
71
82
|
moduleConfig,
|
|
83
|
+
jobs: jobs.registry,
|
|
72
84
|
resolveActor: (context) => resolveActorFrom(credentials, context),
|
|
73
85
|
services: {
|
|
74
86
|
db: database.db,
|
|
75
|
-
transaction: createTransactionScope({
|
|
87
|
+
transaction: createTransactionScope({
|
|
88
|
+
db: database.db,
|
|
89
|
+
bus: events,
|
|
90
|
+
logger,
|
|
91
|
+
createId,
|
|
92
|
+
enqueueOnTx: jobs.enqueueOnTx,
|
|
93
|
+
}),
|
|
76
94
|
createId,
|
|
77
95
|
now: () => new Date(),
|
|
78
96
|
appBaseUrl: config.appBaseUrl,
|
|
79
97
|
secretEncryption: config.secretEncryption,
|
|
98
|
+
signupsEnabled: config.signups === 'open',
|
|
80
99
|
},
|
|
81
100
|
// `provider` picks a named sender from the runtime's registry. `'log'` is
|
|
82
101
|
// built in; `'smtp'` is registered by the built-in `smtp-email` core
|
|
@@ -85,5 +104,5 @@ export async function bootAssembly(
|
|
|
85
104
|
email: { provider: config.email.EMAIL_PROVIDER, from: config.email.EMAIL_FROM },
|
|
86
105
|
})
|
|
87
106
|
|
|
88
|
-
return { config, logger, database, createId, credentials, contributions }
|
|
107
|
+
return { config, logger, database, createId, credentials, contributions, jobs }
|
|
89
108
|
}
|
package/src/index.ts
CHANGED
|
@@ -9,6 +9,22 @@ export type { AppDependencies, AppBindings } from './app.ts'
|
|
|
9
9
|
export { bootAssembly } from './boot.ts'
|
|
10
10
|
export type { AssemblyBoot } from './boot.ts'
|
|
11
11
|
|
|
12
|
+
export { startWorker } from './worker.ts'
|
|
13
|
+
|
|
14
|
+
export { createJobsRuntime } from './runtime/jobs.ts'
|
|
15
|
+
export type { JobsRuntime, JobsRuntimeOptions } from './runtime/jobs.ts'
|
|
16
|
+
export { deadLetterQueueName } from './lib/jobs.ts'
|
|
17
|
+
export type {
|
|
18
|
+
EnqueueOptions,
|
|
19
|
+
JobContext,
|
|
20
|
+
JobDefaults,
|
|
21
|
+
JobDefinition,
|
|
22
|
+
JobHandle,
|
|
23
|
+
JobHandler,
|
|
24
|
+
JobRegistry,
|
|
25
|
+
TransactionJobs,
|
|
26
|
+
} from './lib/jobs.ts'
|
|
27
|
+
|
|
12
28
|
export { WebBundleError, serveWebBundle } from './webBundle.ts'
|
|
13
29
|
export type { WebBundleOptions } from './webBundle.ts'
|
|
14
30
|
|
|
@@ -143,6 +159,7 @@ export type { ModuleEventCatalog } from './runtime/module.ts'
|
|
|
143
159
|
export { createTransactionScope } from './runtime/transaction.ts'
|
|
144
160
|
export type {
|
|
145
161
|
BufferedEvents,
|
|
162
|
+
EnqueueOnTransaction,
|
|
146
163
|
Transaction,
|
|
147
164
|
TransactionContext,
|
|
148
165
|
TransactionOptions,
|
package/src/lib/apiKeyScopes.ts
CHANGED
|
@@ -58,6 +58,10 @@ const ROUTE_SCOPE_RULES: readonly RouteScopeRule[] = [
|
|
|
58
58
|
{ methods: ['POST', 'PATCH', 'DELETE'], pattern: /^\/v1\/roles(?:\/[^/]+)?$/u, scope: 'roles:write' },
|
|
59
59
|
{ methods: ['GET'], pattern: /^\/v1\/candidates(?:\/[^/]+)?$/u, scope: 'candidates:read' },
|
|
60
60
|
{ methods: ['POST', 'PATCH', 'DELETE'], pattern: /^\/v1\/candidates(?:\/[^/]+)?$/u, scope: 'candidates:write' },
|
|
61
|
+
{ methods: ['GET'], pattern: /^\/v1\/(?:events(?:\/[^/]+(?:\/attendances)?)?|event-associations)$/u, scope: 'events:read' },
|
|
62
|
+
{ methods: ['POST', 'PATCH', 'DELETE'], pattern: /^\/v1\/(?:events(?:\/[^/]+(?:\/attendances)?)?|event-associations)$/u, scope: 'events:write' },
|
|
63
|
+
{ methods: ['GET'], pattern: /^\/v1\/attendances(?:\/[^/]+)?$/u, scope: 'attendances:read' },
|
|
64
|
+
{ methods: ['POST', 'PATCH', 'DELETE'], pattern: /^\/v1\/attendances(?:\/[^/]+)?$/u, scope: 'attendances:write' },
|
|
61
65
|
{ methods: ['GET'], pattern: /^\/v1\/decisions(?:\/[^/]+)?$/u, scope: 'decisions:read' },
|
|
62
66
|
{ methods: ['POST', 'PATCH', 'DELETE'], pattern: /^\/v1\/decisions(?:\/[^/]+)?$/u, scope: 'decisions:write' },
|
|
63
67
|
{ methods: ['GET'], pattern: /^\/v1\/plan_items(?:\/[^/]+)?$/u, scope: 'plan_items:read' },
|
|
@@ -100,6 +104,7 @@ const ROUTE_SCOPE_RULES: readonly RouteScopeRule[] = [
|
|
|
100
104
|
{ methods: ['GET'], pattern: /^\/v1\/workspaces\/[^/]+\/modules(?:\/[^/]+)?$/u, scope: 'modules:read' },
|
|
101
105
|
{ methods: ['PATCH'], pattern: /^\/v1\/workspaces\/[^/]+\/modules(?:\/[^/]+)?$/u, scope: 'modules:write' },
|
|
102
106
|
{ methods: ['POST'], pattern: /^\/v1\/workspaces\/[^/]+\/sample-data$/u, scope: 'sample_data:write' },
|
|
107
|
+
{ methods: ['POST'], pattern: /^\/v1\/workspaces\/[^/]+\/handbook\/seed$/u, scope: 'handbook:write' },
|
|
103
108
|
{ methods: ['POST'], pattern: /^\/v1\/workspaces\/[^/]+\/relink-email-domains$/u, scope: 'workspace:write' },
|
|
104
109
|
{ methods: ['GET'], pattern: /^\/v1\/mcp\/tools$/u, scope: 'search:read' },
|
|
105
110
|
]
|
|
@@ -158,6 +163,7 @@ const MCP_RESOURCE_ALIASES: Readonly<Record<string, string>> = {
|
|
|
158
163
|
consent_purposes: 'consent_purposes',
|
|
159
164
|
form_submissions: 'forms',
|
|
160
165
|
list_memberships: 'lists',
|
|
166
|
+
event_associations: 'events',
|
|
161
167
|
agent_tasks: 'agent_tasks',
|
|
162
168
|
agent_runs: 'agent_runs',
|
|
163
169
|
import_jobs: 'import_export',
|
package/src/lib/columns.ts
CHANGED
|
@@ -60,6 +60,12 @@ export interface SearchVectorPart {
|
|
|
60
60
|
* a generated column built on it.
|
|
61
61
|
*/
|
|
62
62
|
readonly array?: boolean
|
|
63
|
+
/**
|
|
64
|
+
* True for an `addresses` jsonb column. Flattened by
|
|
65
|
+
* `kelpie_addresses_search_text`, a wrapper that migration `0045` adds so the
|
|
66
|
+
* generated vector reads city and postal code without the kind keys.
|
|
67
|
+
*/
|
|
68
|
+
readonly addresses?: boolean
|
|
63
69
|
}
|
|
64
70
|
|
|
65
71
|
/**
|
|
@@ -78,9 +84,12 @@ export interface SearchVectorPart {
|
|
|
78
84
|
const SPLIT_ON_PUNCTUATION = sql.raw("'[^[:alnum:]]+', ' ', 'g'")
|
|
79
85
|
|
|
80
86
|
function searchVectorPart(part: SearchVectorPart): SQL {
|
|
81
|
-
const raw =
|
|
82
|
-
|
|
83
|
-
|
|
87
|
+
const raw =
|
|
88
|
+
part.addresses === true
|
|
89
|
+
? sql`coalesce(kelpie_addresses_search_text(${part.column}), '')`
|
|
90
|
+
: part.array === true
|
|
91
|
+
? sql`coalesce(kelpie_text_array_to_string(${part.column}), '')`
|
|
92
|
+
: sql`coalesce(${part.column}::text, '')`
|
|
84
93
|
const source = sql`regexp_replace(${raw}, ${SPLIT_ON_PUNCTUATION})`
|
|
85
94
|
|
|
86
95
|
return sql`setweight(to_tsvector(${sql.raw(`'${SEARCH_CONFIGURATION}'`)}, ${source}), ${sql.raw(`'${part.weight}'`)})`
|
package/src/lib/config.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { PublicRegion } from '@kelpie/schemas'
|
|
1
2
|
import { z } from 'zod'
|
|
2
3
|
|
|
3
4
|
import { emailConfigSchema } from './email.ts'
|
|
@@ -24,6 +25,9 @@ export type RuntimeMode = 'development' | 'test' | 'production'
|
|
|
24
25
|
|
|
25
26
|
export type LogLevel = 'debug' | 'info' | 'warn' | 'error'
|
|
26
27
|
|
|
28
|
+
/** Whether `POST /v1/auth/signup` and external-sign-in provisioning accept a brand-new account. */
|
|
29
|
+
export type SignupsMode = 'open' | 'closed'
|
|
30
|
+
|
|
27
31
|
/**
|
|
28
32
|
* Everything the logger needs to boot: the minimum severity to emit, and the
|
|
29
33
|
* ordered list of destinations each line is written to. Stdout is one such
|
|
@@ -96,6 +100,20 @@ export interface KelpieConfig {
|
|
|
96
100
|
* it, so both webhooks and agent-tasks receive it through `services`.
|
|
97
101
|
*/
|
|
98
102
|
readonly secretEncryption: SecretEncryptionConfig | undefined
|
|
103
|
+
/**
|
|
104
|
+
* Whether the instance accepts a brand-new account, through password signup
|
|
105
|
+
* or external-sign-in provisioning. Defaults to `'open'`, so a self-hoster
|
|
106
|
+
* who never sets `SIGNUPS` sees no change in behaviour. Joining an existing
|
|
107
|
+
* workspace via invite, and workspace creation by an already-verified
|
|
108
|
+
* account, are unaffected either way.
|
|
109
|
+
*/
|
|
110
|
+
readonly signups: SignupsMode
|
|
111
|
+
/**
|
|
112
|
+
* Origins a multi-region assembly advertises on signed-out pages. Empty
|
|
113
|
+
* (the default) hides the switcher. Two or more draws it. Exposed through
|
|
114
|
+
* `GET /v1/public/config`.
|
|
115
|
+
*/
|
|
116
|
+
readonly regions: readonly PublicRegion[]
|
|
99
117
|
}
|
|
100
118
|
|
|
101
119
|
/** Thrown at boot when the environment cannot produce a valid configuration. */
|
|
@@ -129,6 +147,7 @@ const environmentSchema = z.object({
|
|
|
129
147
|
KELPIE_SITE_NAME: z.string().min(1).optional(),
|
|
130
148
|
WEB_BUNDLE_DIR: z.string().min(1).optional(),
|
|
131
149
|
TRUSTED_PROXY_HOP_COUNT: z.coerce.number().int().nonnegative().default(0),
|
|
150
|
+
SIGNUPS: z.enum(['open', 'closed']).default('open'),
|
|
132
151
|
...rateLimitConfigSchema.shape,
|
|
133
152
|
})
|
|
134
153
|
|
|
@@ -171,5 +190,7 @@ export function loadConfig(environment: Environment): KelpieConfig {
|
|
|
171
190
|
env: environment,
|
|
172
191
|
appBaseUrl: undefined,
|
|
173
192
|
secretEncryption: undefined,
|
|
193
|
+
signups: environmentResult.data.SIGNUPS,
|
|
194
|
+
regions: [],
|
|
174
195
|
}
|
|
175
196
|
}
|
package/src/lib/egress.ts
CHANGED
|
@@ -74,17 +74,21 @@ function isPrivateIpv4(ip: string): boolean {
|
|
|
74
74
|
|
|
75
75
|
/** True for an IPv6 address in a loopback, unspecified, link-local, or unique-local range. */
|
|
76
76
|
function isPrivateIpv6(ip: string): boolean {
|
|
77
|
-
|
|
77
|
+
// Normalize both DNS results and literals. URL parsing compresses IPv6 and
|
|
78
|
+
// rewrites a dotted IPv4 suffix as two hexadecimal groups.
|
|
79
|
+
const lower = new URL(`http://[${ip}]/`).hostname.slice(1, -1)
|
|
78
80
|
|
|
79
81
|
if (lower === '::1' || lower === '::') {
|
|
80
82
|
return true
|
|
81
83
|
}
|
|
82
84
|
|
|
83
|
-
const mapped = /^::ffff:(\
|
|
85
|
+
const mapped = /^::ffff:([\da-f]{1,4}):([\da-f]{1,4})$/u.exec(lower)
|
|
84
86
|
|
|
85
|
-
if (mapped?.[1] !== undefined) {
|
|
87
|
+
if (mapped?.[1] !== undefined && mapped[2] !== undefined) {
|
|
86
88
|
// An IPv4 address written in IPv6 form resolves by its IPv4 rules.
|
|
87
|
-
|
|
89
|
+
const high = Number.parseInt(mapped[1], 16)
|
|
90
|
+
const low = Number.parseInt(mapped[2], 16)
|
|
91
|
+
return isPrivateIpv4([high >>> 8, high & 255, low >>> 8, low & 255].join('.'))
|
|
88
92
|
}
|
|
89
93
|
|
|
90
94
|
const firstHextet = Number.parseInt(lower.split(':')[0] || '0', 16)
|
package/src/lib/errors.ts
CHANGED
|
@@ -89,25 +89,32 @@ export class AppError extends Error {
|
|
|
89
89
|
* A reason rather than a message, so a provider module can branch on it (to
|
|
90
90
|
* redirect somewhere useful, say) without matching on English text.
|
|
91
91
|
*/
|
|
92
|
-
export type ExternalSignInRefusal = 'email_unverified' | 'unknown_identity'
|
|
92
|
+
export type ExternalSignInRefusal = 'email_unverified' | 'unknown_identity' | 'signups_closed'
|
|
93
|
+
|
|
94
|
+
const EXTERNAL_SIGN_IN_CODE: Record<ExternalSignInRefusal, ErrorCode> = {
|
|
95
|
+
email_unverified: 'unauthorized',
|
|
96
|
+
unknown_identity: 'forbidden',
|
|
97
|
+
signups_closed: 'forbidden',
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const EXTERNAL_SIGN_IN_MESSAGE: Record<ExternalSignInRefusal, string> = {
|
|
101
|
+
email_unverified: 'The identity provider did not verify this email address',
|
|
102
|
+
unknown_identity: 'No account exists for this identity',
|
|
103
|
+
signups_closed: 'Signups are closed on this instance',
|
|
104
|
+
}
|
|
93
105
|
|
|
94
106
|
/**
|
|
95
107
|
* An external sign-in core would not complete.
|
|
96
108
|
*
|
|
97
109
|
* An `AppError`, so an uncaught one still renders as the `api.md` body. No new
|
|
98
|
-
* `ErrorCode`: refusing an unverified identity is an ordinary `401`, and
|
|
99
|
-
* refusing to provision
|
|
110
|
+
* `ErrorCode`: refusing an unverified identity is an ordinary `401`, and both
|
|
111
|
+
* refusing to provision and signups being closed are an ordinary `403`.
|
|
100
112
|
*/
|
|
101
113
|
export class ExternalSignInError extends AppError {
|
|
102
114
|
readonly reason: ExternalSignInRefusal
|
|
103
115
|
|
|
104
116
|
constructor(reason: ExternalSignInRefusal) {
|
|
105
|
-
super(
|
|
106
|
-
reason === 'email_unverified' ? 'unauthorized' : 'forbidden',
|
|
107
|
-
reason === 'email_unverified'
|
|
108
|
-
? 'The identity provider did not verify this email address'
|
|
109
|
-
: 'No account exists for this identity',
|
|
110
|
-
)
|
|
117
|
+
super(EXTERNAL_SIGN_IN_CODE[reason], EXTERNAL_SIGN_IN_MESSAGE[reason])
|
|
111
118
|
this.name = 'ExternalSignInError'
|
|
112
119
|
this.reason = reason
|
|
113
120
|
}
|
package/src/lib/ids.ts
CHANGED
|
@@ -71,6 +71,17 @@ export const idPrefixes = {
|
|
|
71
71
|
* key. Not persisted by core.
|
|
72
72
|
*/
|
|
73
73
|
event: 'ev',
|
|
74
|
+
/**
|
|
75
|
+
* A dated CRM gathering. The public prefix is `event_`; the factory key is
|
|
76
|
+
* `crmEvent` because `event` already stamps domain-event envelopes as `ev_`.
|
|
77
|
+
*/
|
|
78
|
+
crmEvent: 'event',
|
|
79
|
+
attendance: 'atnd',
|
|
80
|
+
/**
|
|
81
|
+
* Never returns over the wire: associations nest on the Event and on
|
|
82
|
+
* `GET /v1/event-associations`. Same reason as `personLink`.
|
|
83
|
+
*/
|
|
84
|
+
eventAssociation: 'elink',
|
|
74
85
|
} as const
|
|
75
86
|
|
|
76
87
|
export type ObjectKind = keyof typeof idPrefixes
|