boltstore 0.5.2 → 0.6.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/README.md +65 -23
- package/dist/admin/api-keys.d.ts +87 -0
- package/dist/admin/api-keys.d.ts.map +1 -0
- package/dist/admin/api-keys.js +267 -0
- package/dist/admin/api-keys.js.map +1 -0
- package/dist/admin/backup.d.ts +76 -0
- package/dist/admin/backup.d.ts.map +1 -0
- package/dist/admin/backup.js +255 -0
- package/dist/admin/backup.js.map +1 -0
- package/dist/admin/import-export/csv.d.ts +4 -0
- package/dist/admin/import-export/csv.d.ts.map +1 -0
- package/dist/admin/import-export/csv.js +123 -0
- package/dist/admin/import-export/csv.js.map +1 -0
- package/dist/admin/import-export/export.d.ts +4 -0
- package/dist/admin/import-export/export.d.ts.map +1 -0
- package/dist/admin/import-export/export.js +66 -0
- package/dist/admin/import-export/export.js.map +1 -0
- package/dist/admin/import-export/import.d.ts +4 -0
- package/dist/admin/import-export/import.d.ts.map +1 -0
- package/dist/admin/import-export/import.js +253 -0
- package/dist/admin/import-export/import.js.map +1 -0
- package/dist/admin/import-export/index.d.ts +7 -0
- package/dist/admin/import-export/index.d.ts.map +1 -0
- package/dist/admin/import-export/index.js +7 -0
- package/dist/admin/import-export/index.js.map +1 -0
- package/dist/admin/import-export/json-input.d.ts +2 -0
- package/dist/admin/import-export/json-input.d.ts.map +1 -0
- package/dist/admin/import-export/json-input.js +42 -0
- package/dist/admin/import-export/json-input.js.map +1 -0
- package/dist/admin/import-export/schema-inference.d.ts +6 -0
- package/dist/admin/import-export/schema-inference.d.ts.map +1 -0
- package/dist/admin/import-export/schema-inference.js +34 -0
- package/dist/admin/import-export/schema-inference.js.map +1 -0
- package/dist/admin/import-export/types.d.ts +40 -0
- package/dist/admin/import-export/types.d.ts.map +1 -0
- package/dist/admin/import-export/types.js +2 -0
- package/dist/admin/import-export/types.js.map +1 -0
- package/dist/admin/oauth/github.d.ts +12 -0
- package/dist/admin/oauth/github.d.ts.map +1 -0
- package/dist/admin/oauth/github.js +78 -0
- package/dist/admin/oauth/github.js.map +1 -0
- package/dist/admin/oauth/google.d.ts +12 -0
- package/dist/admin/oauth/google.d.ts.map +1 -0
- package/dist/admin/oauth/google.js +55 -0
- package/dist/admin/oauth/google.js.map +1 -0
- package/dist/admin/oauth/handler.d.ts +5 -0
- package/dist/admin/oauth/handler.d.ts.map +1 -0
- package/dist/admin/oauth/handler.js +37 -0
- package/dist/admin/oauth/handler.js.map +1 -0
- package/dist/admin/oauth/index.d.ts +5 -0
- package/dist/admin/oauth/index.d.ts.map +1 -0
- package/dist/admin/oauth/index.js +5 -0
- package/dist/admin/oauth/index.js.map +1 -0
- package/dist/admin/oauth/registry.d.ts +16 -0
- package/dist/admin/oauth/registry.d.ts.map +1 -0
- package/dist/admin/oauth/registry.js +37 -0
- package/dist/admin/oauth/registry.js.map +1 -0
- package/dist/admin/oauth/types.d.ts +14 -0
- package/dist/admin/oauth/types.d.ts.map +1 -0
- package/dist/admin/oauth/types.js +2 -0
- package/dist/admin/oauth/types.js.map +1 -0
- package/dist/admin/oauth/user.d.ts +12 -0
- package/dist/admin/oauth/user.d.ts.map +1 -0
- package/dist/admin/oauth/user.js +28 -0
- package/dist/admin/oauth/user.js.map +1 -0
- package/dist/admin/query.d.ts +54 -0
- package/dist/admin/query.d.ts.map +1 -0
- package/dist/admin/query.js +168 -0
- package/dist/admin/query.js.map +1 -0
- package/dist/admin/transaction.d.ts +46 -0
- package/dist/admin/transaction.d.ts.map +1 -0
- package/dist/admin/transaction.js +52 -0
- package/dist/admin/transaction.js.map +1 -0
- package/dist/admin/views.d.ts +64 -0
- package/dist/admin/views.d.ts.map +1 -0
- package/dist/admin/views.js +197 -0
- package/dist/admin/views.js.map +1 -0
- package/dist/audit.d.ts +36 -0
- package/dist/audit.d.ts.map +1 -0
- package/dist/audit.js +92 -0
- package/dist/audit.js.map +1 -0
- package/dist/auth/jwt.d.ts +8 -0
- package/dist/auth/jwt.d.ts.map +1 -0
- package/dist/auth/jwt.js +81 -0
- package/dist/auth/jwt.js.map +1 -0
- package/dist/auth/password.d.ts +4 -0
- package/dist/auth/password.d.ts.map +1 -0
- package/dist/auth/password.js +15 -0
- package/dist/auth/password.js.map +1 -0
- package/dist/auth/tables.d.ts +5 -0
- package/dist/auth/tables.d.ts.map +1 -0
- package/dist/auth/tables.js +65 -0
- package/dist/auth/tables.js.map +1 -0
- package/dist/auth/tokens.d.ts +16 -0
- package/dist/auth/tokens.d.ts.map +1 -0
- package/dist/auth/tokens.js +126 -0
- package/dist/auth/tokens.js.map +1 -0
- package/dist/auth/types.d.ts +36 -0
- package/dist/auth/types.d.ts.map +1 -0
- package/dist/auth/types.js +2 -0
- package/dist/auth/types.js.map +1 -0
- package/dist/auth/users.d.ts +9 -0
- package/dist/auth/users.d.ts.map +1 -0
- package/dist/auth/users.js +83 -0
- package/dist/auth/users.js.map +1 -0
- package/dist/auth/validation.d.ts +5 -0
- package/dist/auth/validation.d.ts.map +1 -0
- package/dist/auth/validation.js +25 -0
- package/dist/auth/validation.js.map +1 -0
- package/dist/auth.d.ts +9 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +9 -0
- package/dist/auth.js.map +1 -0
- package/dist/cli.d.ts +12 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +322 -0
- package/dist/cli.js.map +1 -0
- package/dist/collections/ddl.d.ts +25 -0
- package/dist/collections/ddl.d.ts.map +1 -0
- package/dist/collections/ddl.js +83 -0
- package/dist/collections/ddl.js.map +1 -0
- package/dist/collections/management.d.ts +57 -0
- package/dist/collections/management.d.ts.map +1 -0
- package/dist/collections/management.js +289 -0
- package/dist/collections/management.js.map +1 -0
- package/dist/collections.d.ts +3 -0
- package/dist/collections.d.ts.map +1 -0
- package/dist/collections.js +3 -0
- package/dist/collections.js.map +1 -0
- package/dist/config.d.ts +47 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +274 -0
- package/dist/config.js.map +1 -0
- package/dist/db/cast.d.ts +15 -0
- package/dist/db/cast.d.ts.map +1 -0
- package/dist/db/cast.js +16 -0
- package/dist/db/cast.js.map +1 -0
- package/dist/db/manager.d.ts +93 -0
- package/dist/db/manager.d.ts.map +1 -0
- package/dist/db/manager.js +209 -0
- package/dist/db/manager.js.map +1 -0
- package/dist/db/pool.d.ts +99 -0
- package/dist/db/pool.d.ts.map +1 -0
- package/dist/db/pool.js +232 -0
- package/dist/db/pool.js.map +1 -0
- package/dist/entry.d.ts +11 -0
- package/dist/entry.d.ts.map +1 -0
- package/dist/entry.js +80 -0
- package/dist/entry.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +47 -0
- package/dist/index.js.map +1 -0
- package/dist/indexes.d.ts +61 -0
- package/dist/indexes.d.ts.map +1 -0
- package/dist/indexes.js +174 -0
- package/dist/indexes.js.map +1 -0
- package/dist/logger.d.ts +40 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +147 -0
- package/dist/logger.js.map +1 -0
- package/dist/middleware/auth.d.ts +43 -0
- package/dist/middleware/auth.d.ts.map +1 -0
- package/dist/middleware/auth.js +87 -0
- package/dist/middleware/auth.js.map +1 -0
- package/dist/middleware/cors.d.ts +28 -0
- package/dist/middleware/cors.d.ts.map +1 -0
- package/dist/middleware/cors.js +71 -0
- package/dist/middleware/cors.js.map +1 -0
- package/dist/middleware/proxy.d.ts +10 -0
- package/dist/middleware/proxy.d.ts.map +1 -0
- package/dist/middleware/proxy.js +31 -0
- package/dist/middleware/proxy.js.map +1 -0
- package/dist/middleware/rate-limit.d.ts +64 -0
- package/dist/middleware/rate-limit.d.ts.map +1 -0
- package/dist/middleware/rate-limit.js +130 -0
- package/dist/middleware/rate-limit.js.map +1 -0
- package/dist/migrations.d.ts +56 -0
- package/dist/migrations.d.ts.map +1 -0
- package/dist/migrations.js +133 -0
- package/dist/migrations.js.map +1 -0
- package/dist/query/builder.d.ts +6 -0
- package/dist/query/builder.d.ts.map +1 -0
- package/dist/query/builder.js +97 -0
- package/dist/query/builder.js.map +1 -0
- package/dist/query/executor.d.ts +3 -0
- package/dist/query/executor.d.ts.map +1 -0
- package/dist/query/executor.js +64 -0
- package/dist/query/executor.js.map +1 -0
- package/dist/query/filter-builder.d.ts +8 -0
- package/dist/query/filter-builder.d.ts.map +1 -0
- package/dist/query/filter-builder.js +128 -0
- package/dist/query/filter-builder.js.map +1 -0
- package/dist/query/search.d.ts +4 -0
- package/dist/query/search.d.ts.map +1 -0
- package/dist/query/search.js +28 -0
- package/dist/query/search.js.map +1 -0
- package/dist/query/types.d.ts +45 -0
- package/dist/query/types.d.ts.map +1 -0
- package/dist/query/types.js +2 -0
- package/dist/query/types.js.map +1 -0
- package/dist/query.d.ts +6 -0
- package/dist/query.d.ts.map +1 -0
- package/dist/query.js +6 -0
- package/dist/query.js.map +1 -0
- package/dist/records/batch.d.ts +13 -0
- package/dist/records/batch.d.ts.map +1 -0
- package/dist/records/batch.js +113 -0
- package/dist/records/batch.js.map +1 -0
- package/dist/records/count.d.ts +5 -0
- package/dist/records/count.d.ts.map +1 -0
- package/dist/records/count.js +34 -0
- package/dist/records/count.js.map +1 -0
- package/dist/records/crud.d.ts +8 -0
- package/dist/records/crud.d.ts.map +1 -0
- package/dist/records/crud.js +121 -0
- package/dist/records/crud.js.map +1 -0
- package/dist/records/distinct.d.ts +5 -0
- package/dist/records/distinct.d.ts.map +1 -0
- package/dist/records/distinct.js +22 -0
- package/dist/records/distinct.js.map +1 -0
- package/dist/records/helpers.d.ts +4 -0
- package/dist/records/helpers.d.ts.map +1 -0
- package/dist/records/helpers.js +9 -0
- package/dist/records/helpers.js.map +1 -0
- package/dist/records/list.d.ts +44 -0
- package/dist/records/list.d.ts.map +1 -0
- package/dist/records/list.js +133 -0
- package/dist/records/list.js.map +1 -0
- package/dist/records/schema-cache.d.ts +8 -0
- package/dist/records/schema-cache.d.ts.map +1 -0
- package/dist/records/schema-cache.js +64 -0
- package/dist/records/schema-cache.js.map +1 -0
- package/dist/records.d.ts +8 -0
- package/dist/records.d.ts.map +1 -0
- package/dist/records.js +8 -0
- package/dist/records.js.map +1 -0
- package/dist/relations.d.ts +50 -0
- package/dist/relations.d.ts.map +1 -0
- package/dist/relations.js +179 -0
- package/dist/relations.js.map +1 -0
- package/dist/rls.d.ts +63 -0
- package/dist/rls.d.ts.map +1 -0
- package/dist/rls.js +146 -0
- package/dist/rls.js.map +1 -0
- package/dist/router.d.ts +53 -0
- package/dist/router.d.ts.map +1 -0
- package/dist/router.js +104 -0
- package/dist/router.js.map +1 -0
- package/dist/routes/admin-query.d.ts +5 -0
- package/dist/routes/admin-query.d.ts.map +1 -0
- package/dist/routes/admin-query.js +57 -0
- package/dist/routes/admin-query.js.map +1 -0
- package/dist/routes/api-keys.d.ts +12 -0
- package/dist/routes/api-keys.d.ts.map +1 -0
- package/dist/routes/api-keys.js +114 -0
- package/dist/routes/api-keys.js.map +1 -0
- package/dist/routes/auth.d.ts +10 -0
- package/dist/routes/auth.d.ts.map +1 -0
- package/dist/routes/auth.js +116 -0
- package/dist/routes/auth.js.map +1 -0
- package/dist/routes/backup.d.ts +5 -0
- package/dist/routes/backup.d.ts.map +1 -0
- package/dist/routes/backup.js +88 -0
- package/dist/routes/backup.js.map +1 -0
- package/dist/routes/collections.d.ts +5 -0
- package/dist/routes/collections.d.ts.map +1 -0
- package/dist/routes/collections.js +110 -0
- package/dist/routes/collections.js.map +1 -0
- package/dist/routes/databases.d.ts +5 -0
- package/dist/routes/databases.d.ts.map +1 -0
- package/dist/routes/databases.js +90 -0
- package/dist/routes/databases.js.map +1 -0
- package/dist/routes/health.d.ts +9 -0
- package/dist/routes/health.d.ts.map +1 -0
- package/dist/routes/health.js +34 -0
- package/dist/routes/health.js.map +1 -0
- package/dist/routes/import-export.d.ts +8 -0
- package/dist/routes/import-export.d.ts.map +1 -0
- package/dist/routes/import-export.js +173 -0
- package/dist/routes/import-export.js.map +1 -0
- package/dist/routes/indexes.d.ts +5 -0
- package/dist/routes/indexes.d.ts.map +1 -0
- package/dist/routes/indexes.js +94 -0
- package/dist/routes/indexes.js.map +1 -0
- package/dist/routes/migrations.d.ts +5 -0
- package/dist/routes/migrations.d.ts.map +1 -0
- package/dist/routes/migrations.js +87 -0
- package/dist/routes/migrations.js.map +1 -0
- package/dist/routes/oauth.d.ts +5 -0
- package/dist/routes/oauth.d.ts.map +1 -0
- package/dist/routes/oauth.js +35 -0
- package/dist/routes/oauth.js.map +1 -0
- package/dist/routes/query.d.ts +5 -0
- package/dist/routes/query.d.ts.map +1 -0
- package/dist/routes/query.js +36 -0
- package/dist/routes/query.js.map +1 -0
- package/dist/routes/records.d.ts +5 -0
- package/dist/routes/records.d.ts.map +1 -0
- package/dist/routes/records.js +181 -0
- package/dist/routes/records.js.map +1 -0
- package/dist/routes/transactions.d.ts +5 -0
- package/dist/routes/transactions.d.ts.map +1 -0
- package/dist/routes/transactions.js +43 -0
- package/dist/routes/transactions.js.map +1 -0
- package/dist/routes/views.d.ts +5 -0
- package/dist/routes/views.d.ts.map +1 -0
- package/dist/routes/views.js +123 -0
- package/dist/routes/views.js.map +1 -0
- package/dist/server.d.ts +80 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +271 -0
- package/dist/server.js.map +1 -0
- package/package.json +20 -43
- package/bin/cli.ts +0 -185
- package/src/admin/assets.ts +0 -126
- package/src/admin/serve.ts +0 -447
- package/src/admin-ui/App.vue +0 -71
- package/src/admin-ui/api/client.ts +0 -179
- package/src/admin-ui/components/Icon.vue +0 -85
- package/src/admin-ui/components/Sidebar.vue +0 -268
- package/src/admin-ui/components/StatCard.vue +0 -40
- package/src/admin-ui/dist/assets/ApiKeys.css +0 -1
- package/src/admin-ui/dist/assets/ApiKeys.js +0 -1
- package/src/admin-ui/dist/assets/AppDashboard.css +0 -1
- package/src/admin-ui/dist/assets/AppDashboard.js +0 -1
- package/src/admin-ui/dist/assets/Backups.js +0 -1
- package/src/admin-ui/dist/assets/Dashboard.css +0 -1
- package/src/admin-ui/dist/assets/Dashboard.js +0 -3
- package/src/admin-ui/dist/assets/DataBrowser.css +0 -1
- package/src/admin-ui/dist/assets/DataBrowser.js +0 -1
- package/src/admin-ui/dist/assets/FileBrowser.css +0 -1
- package/src/admin-ui/dist/assets/FileBrowser.js +0 -1
- package/src/admin-ui/dist/assets/HookEditor.css +0 -1
- package/src/admin-ui/dist/assets/HookEditor.js +0 -2
- package/src/admin-ui/dist/assets/LogViewer.css +0 -1
- package/src/admin-ui/dist/assets/LogViewer.js +0 -1
- package/src/admin-ui/dist/assets/Login.css +0 -1
- package/src/admin-ui/dist/assets/Login.js +0 -1
- package/src/admin-ui/dist/assets/SchemaBuilder.css +0 -1
- package/src/admin-ui/dist/assets/SchemaBuilder.js +0 -1
- package/src/admin-ui/dist/assets/Settings.js +0 -1
- package/src/admin-ui/dist/assets/Setup.css +0 -1
- package/src/admin-ui/dist/assets/Setup.js +0 -1
- package/src/admin-ui/dist/assets/StatCard.css +0 -1
- package/src/admin-ui/dist/assets/StatCard.js +0 -1
- package/src/admin-ui/dist/assets/main.css +0 -1
- package/src/admin-ui/dist/assets/main.js +0 -30
- package/src/admin-ui/dist/index.html +0 -49
- package/src/admin-ui/index.html +0 -48
- package/src/admin-ui/main.ts +0 -25
- package/src/admin-ui/pages/ApiKeys.vue +0 -198
- package/src/admin-ui/pages/AppDashboard.vue +0 -274
- package/src/admin-ui/pages/Applications.vue +0 -123
- package/src/admin-ui/pages/Backups.vue +0 -140
- package/src/admin-ui/pages/Dashboard.vue +0 -293
- package/src/admin-ui/pages/DataBrowser.vue +0 -310
- package/src/admin-ui/pages/FileBrowser.vue +0 -587
- package/src/admin-ui/pages/HookEditor.vue +0 -258
- package/src/admin-ui/pages/LogViewer.vue +0 -109
- package/src/admin-ui/pages/Login.vue +0 -229
- package/src/admin-ui/pages/SchemaBuilder.vue +0 -501
- package/src/admin-ui/pages/Settings.vue +0 -251
- package/src/admin-ui/pages/Setup.vue +0 -275
- package/src/admin-ui/router.ts +0 -163
- package/src/admin-ui/stores/application.ts +0 -68
- package/src/admin-ui/stores/auth.ts +0 -109
- package/src/admin-ui/style.css +0 -532
- package/src/admin-ui/tsconfig.json +0 -21
- package/src/admin-ui/version.ts +0 -3
- package/src/admin-ui/vite.config.ts +0 -35
- package/src/api/admin-config.ts +0 -596
- package/src/api/admin.ts +0 -8
- package/src/api/aggregate.ts +0 -104
- package/src/api/applications.ts +0 -148
- package/src/api/auth.ts +0 -782
- package/src/api/backups.ts +0 -173
- package/src/api/batch.ts +0 -135
- package/src/api/collections.ts +0 -354
- package/src/api/files.ts +0 -808
- package/src/api/health.ts +0 -48
- package/src/api/helpers.ts +0 -86
- package/src/api/middleware/auth.ts +0 -205
- package/src/api/middleware/cors.ts +0 -78
- package/src/api/middleware/logging.ts +0 -27
- package/src/api/middleware/ratelimit.ts +0 -166
- package/src/api/middleware/rls.ts +0 -37
- package/src/api/records.ts +0 -571
- package/src/api/router.ts +0 -296
- package/src/app.ts +0 -617
- package/src/auth/apikey.ts +0 -73
- package/src/auth/jwt.ts +0 -220
- package/src/auth/oauth2.ts +0 -144
- package/src/auth/password.ts +0 -68
- package/src/backup/manager.ts +0 -227
- package/src/config/yaml.ts +0 -78
- package/src/config.ts +0 -240
- package/src/db/application-migrations.ts +0 -112
- package/src/db/application-schema.ts +0 -297
- package/src/db/migrations.ts +0 -54
- package/src/db/pool.ts +0 -143
- package/src/db/system-schema.ts +0 -153
- package/src/hooks/api.ts +0 -157
- package/src/hooks/loader.ts +0 -263
- package/src/hooks/runner.ts +0 -116
- package/src/index.ts +0 -17
- package/src/logging/logger.ts +0 -131
- package/src/realtime/broadcast.ts +0 -132
- package/src/realtime/subscriptions.ts +0 -124
- package/src/security/rls-engine.ts +0 -183
- package/src/security/sanitizer.ts +0 -72
- package/src/storage/interface.ts +0 -48
- package/src/storage/local.ts +0 -189
- package/src/storage/mime-types.ts +0 -32
- package/src/storage/s3.ts +0 -213
- package/src/sync/changelog.ts +0 -247
- package/src/sync/engine.ts +0 -159
- package/src/sync/lamport.ts +0 -48
- package/src/sync/protocol.ts +0 -253
- package/src/version.ts +0 -2
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { DatabasePool } from "../../db/pool";
|
|
2
|
+
import { type AuthConfig, type TokenPair } from "../../auth";
|
|
3
|
+
export declare function getAuthorizationUrl(provider: string, redirectUri: string): string;
|
|
4
|
+
export declare function authenticateWithOAuth(pool: DatabasePool, provider: string, code: string, redirectUri: string, authConfig: AuthConfig): Promise<TokenPair>;
|
|
5
|
+
//# sourceMappingURL=handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../../src/admin/oauth/handler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAA0B,KAAK,UAAU,EAAE,KAAK,SAAS,EAAa,MAAM,YAAY,CAAC;AAIhG,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,GAClB,MAAM,CAoBR;AAED,wBAAsB,qBAAqB,CACzC,IAAI,EAAE,YAAY,EAClB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,UAAU,GACrB,OAAO,CAAC,SAAS,CAAC,CAyCpB"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { createTokenPairForUser } from "../../auth";
|
|
2
|
+
import { getProviders, getProviderEnvConfig } from "./registry";
|
|
3
|
+
import { findOrCreateOAuthUser } from "./user";
|
|
4
|
+
export function getAuthorizationUrl(provider, redirectUri) {
|
|
5
|
+
const providers = getProviders();
|
|
6
|
+
const handler = providers.get(provider.toLowerCase());
|
|
7
|
+
if (!handler) {
|
|
8
|
+
throw Object.assign(new Error(`OAuth provider "${provider}" is not configured. Supported: ${[...providers.keys()].join(", ") || "none"}`), { status: 400 });
|
|
9
|
+
}
|
|
10
|
+
if (!redirectUri || typeof redirectUri !== "string") {
|
|
11
|
+
throw Object.assign(new Error("Query parameter 'redirect_uri' is required."), { status: 400 });
|
|
12
|
+
}
|
|
13
|
+
const providerConfig = getProviderEnvConfig(provider);
|
|
14
|
+
return handler.getAuthUrl(redirectUri, providerConfig);
|
|
15
|
+
}
|
|
16
|
+
export async function authenticateWithOAuth(pool, provider, code, redirectUri, authConfig) {
|
|
17
|
+
const providers = getProviders();
|
|
18
|
+
const handler = providers.get(provider.toLowerCase());
|
|
19
|
+
if (!handler) {
|
|
20
|
+
throw Object.assign(new Error(`OAuth provider "${provider}" is not configured.`), { status: 400 });
|
|
21
|
+
}
|
|
22
|
+
if (!code || typeof code !== "string") {
|
|
23
|
+
throw Object.assign(new Error("Field 'code' is required."), { status: 400 });
|
|
24
|
+
}
|
|
25
|
+
if (!redirectUri || typeof redirectUri !== "string") {
|
|
26
|
+
throw Object.assign(new Error("Field 'redirect_uri' is required."), { status: 400 });
|
|
27
|
+
}
|
|
28
|
+
const providerConfig = getProviderEnvConfig(provider);
|
|
29
|
+
const accessToken = await handler.exchangeCode(code, redirectUri, providerConfig);
|
|
30
|
+
const profile = await handler.fetchProfile(accessToken);
|
|
31
|
+
const user = await findOrCreateOAuthUser(pool, profile);
|
|
32
|
+
if (user.oauth_only === 1 && !user.password_set) {
|
|
33
|
+
throw Object.assign(new Error("OAuth account requires a password reset before first login. Use PATCH /api/:database/auth/me to set a password."), { status: 403, code: "OAUTH_PASSWORD_RESET_REQUIRED" });
|
|
34
|
+
}
|
|
35
|
+
return createTokenPairForUser(pool, user, authConfig);
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler.js","sourceRoot":"","sources":["../../../src/admin/oauth/handler.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/admin/oauth/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,QAAQ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/admin/oauth/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,QAAQ,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { OAuthProviderConfig } from "./types";
|
|
2
|
+
type ProviderHandler = {
|
|
3
|
+
name: string;
|
|
4
|
+
scopes: string;
|
|
5
|
+
getAuthUrl(redirectUri: string, config: OAuthProviderConfig): string;
|
|
6
|
+
exchangeCode(code: string, redirectUri: string, config: OAuthProviderConfig): Promise<string>;
|
|
7
|
+
fetchProfile(accessToken: string): Promise<{
|
|
8
|
+
id: string;
|
|
9
|
+
email: string;
|
|
10
|
+
name?: string;
|
|
11
|
+
}>;
|
|
12
|
+
};
|
|
13
|
+
export declare function getProviders(): Map<string, ProviderHandler>;
|
|
14
|
+
export declare function getProviderEnvConfig(provider: string): OAuthProviderConfig;
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../src/admin/oauth/registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAI9C,KAAK,eAAe,GAAG;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,mBAAmB,GAAG,MAAM,CAAC;IACrE,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC9F,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC1F,CAAC;AAEF,wBAAgB,YAAY,IAAI,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CAsB3D;AAED,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,mBAAmB,CA0B1E"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { googleProvider } from "./google";
|
|
2
|
+
import { githubProvider } from "./github";
|
|
3
|
+
export function getProviders() {
|
|
4
|
+
const providers = new Map();
|
|
5
|
+
const googleConfig = {
|
|
6
|
+
clientId: Bun.env.GOOGLE_CLIENT_ID,
|
|
7
|
+
clientSecret: Bun.env.GOOGLE_CLIENT_SECRET,
|
|
8
|
+
};
|
|
9
|
+
if (googleConfig.clientId && googleConfig.clientSecret) {
|
|
10
|
+
providers.set("google", googleProvider);
|
|
11
|
+
}
|
|
12
|
+
const githubConfig = {
|
|
13
|
+
clientId: Bun.env.GITHUB_CLIENT_ID,
|
|
14
|
+
clientSecret: Bun.env.GITHUB_CLIENT_SECRET,
|
|
15
|
+
};
|
|
16
|
+
if (githubConfig.clientId && githubConfig.clientSecret) {
|
|
17
|
+
providers.set("github", githubProvider);
|
|
18
|
+
}
|
|
19
|
+
return providers;
|
|
20
|
+
}
|
|
21
|
+
export function getProviderEnvConfig(provider) {
|
|
22
|
+
const lower = provider.toLowerCase();
|
|
23
|
+
if (lower === "google") {
|
|
24
|
+
if (!Bun.env.GOOGLE_CLIENT_ID || !Bun.env.GOOGLE_CLIENT_SECRET) {
|
|
25
|
+
throw Object.assign(new Error("Google OAuth is not configured. Set GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET."), { status: 500 });
|
|
26
|
+
}
|
|
27
|
+
return { clientId: Bun.env.GOOGLE_CLIENT_ID, clientSecret: Bun.env.GOOGLE_CLIENT_SECRET };
|
|
28
|
+
}
|
|
29
|
+
if (lower === "github") {
|
|
30
|
+
if (!Bun.env.GITHUB_CLIENT_ID || !Bun.env.GITHUB_CLIENT_SECRET) {
|
|
31
|
+
throw Object.assign(new Error("GitHub OAuth is not configured. Set GITHUB_CLIENT_ID and GITHUB_CLIENT_SECRET."), { status: 500 });
|
|
32
|
+
}
|
|
33
|
+
return { clientId: Bun.env.GITHUB_CLIENT_ID, clientSecret: Bun.env.GITHUB_CLIENT_SECRET };
|
|
34
|
+
}
|
|
35
|
+
throw Object.assign(new Error(`Unknown provider: ${provider}`), { status: 400 });
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../src/admin/oauth/registry.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAU1C,MAAM,UAAU,YAAY;IAC1B,MAAM,SAAS,GAAG,IAAI,GAAG,EAA2B,CAAC;IAErD,MAAM,YAAY,GAAG;QACnB,QAAQ,EAAE,GAAG,CAAC,GAAG,CAAC,gBAAgB;QAClC,YAAY,EAAE,GAAG,CAAC,GAAG,CAAC,oBAAoB;KAC3C,CAAC;IAEF,IAAI,YAAY,CAAC,QAAQ,IAAI,YAAY,CAAC,YAAY,EAAE,CAAC;QACvD,SAAS,CAAC,GAAG,CAAC,QAAQ,EAAE,cAAiC,CAAC,CAAC;IAC7D,CAAC;IAED,MAAM,YAAY,GAAG;QACnB,QAAQ,EAAE,GAAG,CAAC,GAAG,CAAC,gBAAgB;QAClC,YAAY,EAAE,GAAG,CAAC,GAAG,CAAC,oBAAoB;KAC3C,CAAC;IAEF,IAAI,YAAY,CAAC,QAAQ,IAAI,YAAY,CAAC,YAAY,EAAE,CAAC;QACvD,SAAS,CAAC,GAAG,CAAC,QAAQ,EAAE,cAAiC,CAAC,CAAC;IAC7D,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,QAAgB;IACnD,MAAM,KAAK,GAAG,QAAQ,CAAC,WAAW,EAAE,CAAC;IACrC,IAAI,KAAK,KAAK,QAAQ,EAAE,CAAC;QACvB,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,gBAAgB,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,oBAAoB,EAAE,CAAC;YAC/D,MAAM,MAAM,CAAC,MAAM,CACjB,IAAI,KAAK,CAAC,gFAAgF,CAAC,EAC3F,EAAE,MAAM,EAAE,GAAG,EAAE,CAChB,CAAC;QACJ,CAAC;QACD,OAAO,EAAE,QAAQ,EAAE,GAAG,CAAC,GAAG,CAAC,gBAAgB,EAAE,YAAY,EAAE,GAAG,CAAC,GAAG,CAAC,oBAAoB,EAAE,CAAC;IAC5F,CAAC;IAED,IAAI,KAAK,KAAK,QAAQ,EAAE,CAAC;QACvB,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,gBAAgB,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,oBAAoB,EAAE,CAAC;YAC/D,MAAM,MAAM,CAAC,MAAM,CACjB,IAAI,KAAK,CAAC,gFAAgF,CAAC,EAC3F,EAAE,MAAM,EAAE,GAAG,EAAE,CAChB,CAAC;QACJ,CAAC;QACD,OAAO,EAAE,QAAQ,EAAE,GAAG,CAAC,GAAG,CAAC,gBAAgB,EAAE,YAAY,EAAE,GAAG,CAAC,GAAG,CAAC,oBAAoB,EAAE,CAAC;IAC5F,CAAC;IAED,MAAM,MAAM,CAAC,MAAM,CACjB,IAAI,KAAK,CAAC,qBAAqB,QAAQ,EAAE,CAAC,EAC1C,EAAE,MAAM,EAAE,GAAG,EAAE,CAChB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface OAuthProviderConfig {
|
|
2
|
+
clientId: string;
|
|
3
|
+
clientSecret: string;
|
|
4
|
+
}
|
|
5
|
+
export interface OAuthConfig {
|
|
6
|
+
google?: OAuthProviderConfig;
|
|
7
|
+
github?: OAuthProviderConfig;
|
|
8
|
+
}
|
|
9
|
+
export interface OAuthProfile {
|
|
10
|
+
id: string;
|
|
11
|
+
email: string;
|
|
12
|
+
name?: string;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/admin/oauth/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,CAAC,EAAE,mBAAmB,CAAC;IAC7B,MAAM,CAAC,EAAE,mBAAmB,CAAC;CAC9B;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/admin/oauth/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { DatabasePool } from "../../db/pool";
|
|
2
|
+
import { type User } from "../../auth";
|
|
3
|
+
export declare function findOrCreateOAuthUser(pool: DatabasePool, profile: {
|
|
4
|
+
id: string;
|
|
5
|
+
email: string;
|
|
6
|
+
name?: string;
|
|
7
|
+
}): Promise<User & {
|
|
8
|
+
oauth_only: number;
|
|
9
|
+
password_set: number;
|
|
10
|
+
}>;
|
|
11
|
+
export declare function markPasswordSet(pool: DatabasePool, userId: string): void;
|
|
12
|
+
//# sourceMappingURL=user.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user.d.ts","sourceRoot":"","sources":["../../../src/admin/oauth/user.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAiE,KAAK,IAAI,EAAE,MAAM,YAAY,CAAC;AAEtG,wBAAsB,qBAAqB,CACzC,IAAI,EAAE,YAAY,EAClB,OAAO,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GACpD,OAAO,CAAC,IAAI,GAAG;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAAC,CA6B9D;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAGxE"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { bootstrapAuthTables, generateSecureId, generateRandomPassword } from "../../auth";
|
|
2
|
+
export async function findOrCreateOAuthUser(pool, profile) {
|
|
3
|
+
bootstrapAuthTables(pool);
|
|
4
|
+
const db = pool.read();
|
|
5
|
+
const existing = db
|
|
6
|
+
.query("SELECT id, email, role, oauth_only, password_set, created_at, updated_at FROM _users WHERE email=?")
|
|
7
|
+
.get(profile.email);
|
|
8
|
+
if (existing) {
|
|
9
|
+
const passwordSet = existing.password_set ?? (existing.oauth_only === 1 ? 0 : 1);
|
|
10
|
+
if (existing.oauth_only === 1 && passwordSet === 0) {
|
|
11
|
+
return { ...existing, oauth_only: 1, password_set: 0 };
|
|
12
|
+
}
|
|
13
|
+
return { ...existing, oauth_only: existing.oauth_only ?? 0, password_set: passwordSet };
|
|
14
|
+
}
|
|
15
|
+
const id = generateSecureId("usr");
|
|
16
|
+
const ts = new Date().toISOString();
|
|
17
|
+
const randomPassword = await generateRandomPassword();
|
|
18
|
+
return pool.writeTransaction(() => {
|
|
19
|
+
const writeDb = pool.write();
|
|
20
|
+
writeDb.run("INSERT INTO _users (id, email, password_hash, role, oauth_only, password_set, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", [id, profile.email, randomPassword, "user", 1, 0, ts, ts]);
|
|
21
|
+
return { id, email: profile.email, role: "user", oauth_only: 1, password_set: 0, created_at: ts, updated_at: ts };
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
export function markPasswordSet(pool, userId) {
|
|
25
|
+
bootstrapAuthTables(pool);
|
|
26
|
+
pool.write().run("UPDATE _users SET password_set=1, oauth_only=0 WHERE id=?", [userId]);
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=user.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user.js","sourceRoot":"","sources":["../../../src/admin/oauth/user.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,sBAAsB,EAAa,MAAM,YAAY,CAAC;AAEtG,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,IAAkB,EAClB,OAAqD;IAErD,mBAAmB,CAAC,IAAI,CAAC,CAAC;IAC1B,MAAM,EAAE,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IAEvB,MAAM,QAAQ,GAAG,EAAE;SAChB,KAAK,CAAC,oGAAoG,CAAC;SAC3G,GAAG,CAAC,OAAO,CAAC,KAAK,CAAmE,CAAC;IAExF,IAAI,QAAQ,EAAE,CAAC;QACb,MAAM,WAAW,GAAG,QAAQ,CAAC,YAAY,IAAI,CAAC,QAAQ,CAAC,UAAU,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACjF,IAAI,QAAQ,CAAC,UAAU,KAAK,CAAC,IAAI,WAAW,KAAK,CAAC,EAAE,CAAC;YACnD,OAAO,EAAE,GAAG,QAAQ,EAAE,UAAU,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,CAAC;QACzD,CAAC;QACD,OAAO,EAAE,GAAG,QAAQ,EAAE,UAAU,EAAE,QAAQ,CAAC,UAAU,IAAI,CAAC,EAAE,YAAY,EAAE,WAAW,EAAE,CAAC;IAC1F,CAAC;IAED,MAAM,EAAE,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAC;IACnC,MAAM,EAAE,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACpC,MAAM,cAAc,GAAG,MAAM,sBAAsB,EAAE,CAAC;IAEtD,OAAO,IAAI,CAAC,gBAAgB,CAAC,GAAG,EAAE;QAChC,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;QAC7B,OAAO,CAAC,GAAG,CACT,uIAAuI,EACvI,CAAC,EAAE,EAAE,OAAO,CAAC,KAAK,EAAE,cAAc,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,CAAC,CAC1D,CAAC;QAEF,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,IAAI,EAAE,MAAe,EAAE,UAAU,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,UAAU,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC;IAC7H,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,IAAkB,EAAE,MAAc;IAChE,mBAAmB,CAAC,IAAI,CAAC,CAAC;IAC1B,IAAI,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,2DAA2D,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;AAC1F,CAAC"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Raw SQL query execution for admin users.
|
|
3
|
+
*
|
|
4
|
+
* Allows executing arbitrary SQL against a database with safety guards:
|
|
5
|
+
* - Read-only queries via a separate endpoint
|
|
6
|
+
* - Write queries with system table protection
|
|
7
|
+
* - EXPLAIN QUERY PLAN for debugging
|
|
8
|
+
* - All queries use parameterized prepared statements
|
|
9
|
+
*
|
|
10
|
+
* @module boltstore/admin/query
|
|
11
|
+
*/
|
|
12
|
+
import { DatabasePool } from "../db/pool";
|
|
13
|
+
/** Context for a raw SQL execution. */
|
|
14
|
+
export interface QueryContext {
|
|
15
|
+
principalId?: string;
|
|
16
|
+
principalType?: "user" | "api_key";
|
|
17
|
+
ip?: string;
|
|
18
|
+
userAgent?: string;
|
|
19
|
+
database: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Execute a read-only SQL query.
|
|
23
|
+
*
|
|
24
|
+
* Only SELECT, EXPLAIN, PRAGMA (read-only), and WITH (CTE) statements are allowed.
|
|
25
|
+
*
|
|
26
|
+
* `POST /api/admin/:database/query`
|
|
27
|
+
*/
|
|
28
|
+
export declare function executeReadQuery(pool: DatabasePool, sql: string, params?: unknown[]): {
|
|
29
|
+
columns: string[];
|
|
30
|
+
rows: Record<string, unknown>[];
|
|
31
|
+
rowCount: number;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Execute a write SQL query (INSERT, UPDATE, DELETE, CREATE, etc.).
|
|
35
|
+
*
|
|
36
|
+
* Destructive operations on system tables are blocked.
|
|
37
|
+
*
|
|
38
|
+
* `POST /api/admin/:database/query/write`
|
|
39
|
+
*/
|
|
40
|
+
export declare function executeWriteQuery(pool: DatabasePool, sql: string, params?: unknown[], ctx?: QueryContext): {
|
|
41
|
+
changes: number;
|
|
42
|
+
lastInsertRowid: number | bigint;
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Execute EXPLAIN QUERY PLAN on a SQL statement.
|
|
46
|
+
*
|
|
47
|
+
* Returns the query plan rows for debugging slow queries.
|
|
48
|
+
*
|
|
49
|
+
* `POST /api/admin/:database/query/explain`
|
|
50
|
+
*/
|
|
51
|
+
export declare function explainQuery(pool: DatabasePool, sql: string, params?: unknown[]): {
|
|
52
|
+
rows: Record<string, unknown>[];
|
|
53
|
+
};
|
|
54
|
+
//# sourceMappingURL=query.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../src/admin/query.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAgD1C,uCAAuC;AACvC,MAAM,WAAW,YAAY;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB;AA0BD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,YAAY,EAClB,GAAG,EAAE,MAAM,EACX,MAAM,GAAE,OAAO,EAAO,GACrB;IAAE,OAAO,EAAE,MAAM,EAAE,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAwB1E;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,YAAY,EAClB,GAAG,EAAE,MAAM,EACX,MAAM,GAAE,OAAO,EAAO,EACtB,GAAG,CAAC,EAAE,YAAY,GACjB;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,CAoDvD;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,YAAY,EAClB,GAAG,EAAE,MAAM,EACX,MAAM,GAAE,OAAO,EAAO,GACrB;IAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAA;CAAE,CAcrC"}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Raw SQL query execution for admin users.
|
|
3
|
+
*
|
|
4
|
+
* Allows executing arbitrary SQL against a database with safety guards:
|
|
5
|
+
* - Read-only queries via a separate endpoint
|
|
6
|
+
* - Write queries with system table protection
|
|
7
|
+
* - EXPLAIN QUERY PLAN for debugging
|
|
8
|
+
* - All queries use parameterized prepared statements
|
|
9
|
+
*
|
|
10
|
+
* @module boltstore/admin/query
|
|
11
|
+
*/
|
|
12
|
+
import { toBindings } from "../db/cast";
|
|
13
|
+
import { logAuditEvent, sanitizeSqlForAudit } from "../audit";
|
|
14
|
+
/** System tables that must not be dropped or altered via raw SQL. */
|
|
15
|
+
const PROTECTED_TABLES = [
|
|
16
|
+
"_collections",
|
|
17
|
+
"_databases",
|
|
18
|
+
"_migrations",
|
|
19
|
+
"_users",
|
|
20
|
+
"_tokens",
|
|
21
|
+
"_api_keys",
|
|
22
|
+
"_webhooks",
|
|
23
|
+
"_jobs",
|
|
24
|
+
"sqlite_master",
|
|
25
|
+
"sqlite_sequence",
|
|
26
|
+
"sqlite_stat1",
|
|
27
|
+
];
|
|
28
|
+
/** Destructive SQL patterns that are blocked. */
|
|
29
|
+
const BLOCKED_PATTERNS = [
|
|
30
|
+
/\bDROP\s+TABLE\s+/i,
|
|
31
|
+
/\bALTER\s+TABLE\s+/i,
|
|
32
|
+
/\bDROP\s+INDEX\s+/i,
|
|
33
|
+
/\bDROP\s+VIEW\s+/i,
|
|
34
|
+
/\bDROP\s+TRIGGER\s+/i,
|
|
35
|
+
];
|
|
36
|
+
/** Dangerous statements that are never allowed via the raw write endpoint. */
|
|
37
|
+
const DANGEROUS_KEYWORDS = [
|
|
38
|
+
"ATTACH",
|
|
39
|
+
"DETACH",
|
|
40
|
+
"REINDEX",
|
|
41
|
+
"VACUUM",
|
|
42
|
+
"PRAGMA",
|
|
43
|
+
"CREATE TRIGGER",
|
|
44
|
+
"CREATE VIRTUAL TABLE",
|
|
45
|
+
];
|
|
46
|
+
function hasDangerousKeyword(sql) {
|
|
47
|
+
const upper = sql.toUpperCase();
|
|
48
|
+
for (const kw of DANGEROUS_KEYWORDS) {
|
|
49
|
+
if (upper.includes(kw))
|
|
50
|
+
return kw;
|
|
51
|
+
}
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
/** Maximum length of a raw SQL statement accepted by the write endpoint. */
|
|
55
|
+
const MAX_SQL_LENGTH = 10000;
|
|
56
|
+
function auditRawWrite(pool, ctx, sql, success, error) {
|
|
57
|
+
const event = {
|
|
58
|
+
type: "raw_sql.write",
|
|
59
|
+
principalId: ctx.principalId,
|
|
60
|
+
principalType: ctx.principalType,
|
|
61
|
+
database: ctx.database,
|
|
62
|
+
ip: ctx.ip,
|
|
63
|
+
userAgent: ctx.userAgent,
|
|
64
|
+
success,
|
|
65
|
+
details: { sql: sanitizeSqlForAudit(sql) },
|
|
66
|
+
error,
|
|
67
|
+
};
|
|
68
|
+
logAuditEvent(event, pool);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Execute a read-only SQL query.
|
|
72
|
+
*
|
|
73
|
+
* Only SELECT, EXPLAIN, PRAGMA (read-only), and WITH (CTE) statements are allowed.
|
|
74
|
+
*
|
|
75
|
+
* `POST /api/admin/:database/query`
|
|
76
|
+
*/
|
|
77
|
+
export function executeReadQuery(pool, sql, params = []) {
|
|
78
|
+
const trimmed = sql.trim().toUpperCase();
|
|
79
|
+
// Allow SELECT, EXPLAIN, PRAGMA (read-only), and WITH (CTE)
|
|
80
|
+
const isReadOnly = trimmed.startsWith("SELECT") ||
|
|
81
|
+
trimmed.startsWith("EXPLAIN") ||
|
|
82
|
+
trimmed.startsWith("PRAGMA") ||
|
|
83
|
+
trimmed.startsWith("WITH") ||
|
|
84
|
+
trimmed.startsWith("DESCRIBE");
|
|
85
|
+
if (!isReadOnly) {
|
|
86
|
+
throw Object.assign(new Error("Only SELECT, EXPLAIN, PRAGMA, WITH, and DESCRIBE queries are allowed on this endpoint. Use /api/admin/query/write for mutations."), { status: 400 });
|
|
87
|
+
}
|
|
88
|
+
const db = pool.read();
|
|
89
|
+
const rows = db.query(sql).all(...toBindings(params));
|
|
90
|
+
const columns = rows.length > 0 ? Object.keys(rows[0]) : [];
|
|
91
|
+
return { columns, rows, rowCount: rows.length };
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Execute a write SQL query (INSERT, UPDATE, DELETE, CREATE, etc.).
|
|
95
|
+
*
|
|
96
|
+
* Destructive operations on system tables are blocked.
|
|
97
|
+
*
|
|
98
|
+
* `POST /api/admin/:database/query/write`
|
|
99
|
+
*/
|
|
100
|
+
export function executeWriteQuery(pool, sql, params = [], ctx) {
|
|
101
|
+
if (sql.length > MAX_SQL_LENGTH) {
|
|
102
|
+
const err = `SQL statement exceeds maximum length of ${MAX_SQL_LENGTH} characters.`;
|
|
103
|
+
if (ctx)
|
|
104
|
+
auditRawWrite(pool, ctx, sql, false, err);
|
|
105
|
+
throw Object.assign(new Error(err), { status: 413 });
|
|
106
|
+
}
|
|
107
|
+
const dangerous = hasDangerousKeyword(sql);
|
|
108
|
+
if (dangerous) {
|
|
109
|
+
const err = `Dangerous statement "${dangerous}" is not allowed via the raw SQL write endpoint.`;
|
|
110
|
+
if (ctx)
|
|
111
|
+
auditRawWrite(pool, ctx, sql, false, err);
|
|
112
|
+
throw Object.assign(new Error(err), { status: 403 });
|
|
113
|
+
}
|
|
114
|
+
// Validate system table protection
|
|
115
|
+
for (const pattern of BLOCKED_PATTERNS) {
|
|
116
|
+
const match = sql.match(pattern);
|
|
117
|
+
if (match) {
|
|
118
|
+
const tableMatch = sql.match(/(?:DROP\s+TABLE|ALTER\s+TABLE|DROP\s+INDEX|DROP\s+VIEW|DROP\s+TRIGGER)\s+(?:IF\s+EXISTS\s+)?['"]?(\w+)['"]?/i);
|
|
119
|
+
if (tableMatch) {
|
|
120
|
+
const tableName = tableMatch[1].toLowerCase();
|
|
121
|
+
if (PROTECTED_TABLES.includes(tableName) || tableName.startsWith("sqlite_") || tableName.startsWith("_")) {
|
|
122
|
+
const err = `Cannot perform destructive operation on system table "${tableName}".`;
|
|
123
|
+
if (ctx)
|
|
124
|
+
auditRawWrite(pool, ctx, sql, false, err);
|
|
125
|
+
throw Object.assign(new Error(err), { status: 403 });
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
try {
|
|
131
|
+
return pool.writeTransaction(() => {
|
|
132
|
+
const db = pool.write();
|
|
133
|
+
db.run(sql, toBindings(params));
|
|
134
|
+
// Use db.query() for SELECT-based introspection (run() doesn't support .values())
|
|
135
|
+
const changesQuery = db.query("SELECT changes() as cnt").get();
|
|
136
|
+
const rowIdQuery = db.query("SELECT last_insert_rowid() as id").get();
|
|
137
|
+
if (ctx)
|
|
138
|
+
auditRawWrite(pool, ctx, sql, true);
|
|
139
|
+
return {
|
|
140
|
+
changes: changesQuery?.cnt ?? 0,
|
|
141
|
+
lastInsertRowid: rowIdQuery?.id ?? 0,
|
|
142
|
+
};
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
catch (err) {
|
|
146
|
+
if (ctx)
|
|
147
|
+
auditRawWrite(pool, ctx, sql, false, err.message);
|
|
148
|
+
throw Object.assign(err, { status: err.status || 500 });
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Execute EXPLAIN QUERY PLAN on a SQL statement.
|
|
153
|
+
*
|
|
154
|
+
* Returns the query plan rows for debugging slow queries.
|
|
155
|
+
*
|
|
156
|
+
* `POST /api/admin/:database/query/explain`
|
|
157
|
+
*/
|
|
158
|
+
export function explainQuery(pool, sql, params = []) {
|
|
159
|
+
const trimmed = sql.trim().toUpperCase();
|
|
160
|
+
// EXPLAIN QUERY PLAN only works on SELECT, INSERT, UPDATE, DELETE
|
|
161
|
+
if (trimmed.startsWith("EXPLAIN") || trimmed.startsWith("PRAGMA")) {
|
|
162
|
+
throw Object.assign(new Error("Cannot explain an EXPLAIN or PRAGMA statement."), { status: 400 });
|
|
163
|
+
}
|
|
164
|
+
const db = pool.read();
|
|
165
|
+
const rows = db.query(`EXPLAIN QUERY PLAN ${sql}`).all(...toBindings(params));
|
|
166
|
+
return { rows };
|
|
167
|
+
}
|
|
168
|
+
//# sourceMappingURL=query.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query.js","sourceRoot":"","sources":["../../src/admin/query.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transaction API — execute multiple SQL operations atomically.
|
|
3
|
+
*
|
|
4
|
+
* All operations in a transaction either succeed together or fail together.
|
|
5
|
+
* Supports a mix of read (SELECT) and write (INSERT, UPDATE, DELETE) operations.
|
|
6
|
+
*
|
|
7
|
+
* @module boltstore/admin/transaction
|
|
8
|
+
*/
|
|
9
|
+
import { DatabasePool } from "../db/pool";
|
|
10
|
+
/** A single operation within a transaction. */
|
|
11
|
+
export interface TransactionOperation {
|
|
12
|
+
/** SQL statement to execute (parameterized). */
|
|
13
|
+
sql: string;
|
|
14
|
+
/** Parameters to bind (positional). */
|
|
15
|
+
params?: unknown[];
|
|
16
|
+
}
|
|
17
|
+
/** Result of a single operation within a transaction. */
|
|
18
|
+
export interface OperationResult {
|
|
19
|
+
/** 0-based index of the operation in the batch. */
|
|
20
|
+
index: number;
|
|
21
|
+
/** For SELECT operations: the returned rows. */
|
|
22
|
+
rows?: Record<string, unknown>[];
|
|
23
|
+
/** Number of columns in SELECT results. */
|
|
24
|
+
columns?: string[];
|
|
25
|
+
/** For write operations: number of rows changed. */
|
|
26
|
+
changes?: number;
|
|
27
|
+
/** For INSERT operations: the last inserted row ID. */
|
|
28
|
+
lastInsertRowid?: number | bigint;
|
|
29
|
+
}
|
|
30
|
+
/** Result of an entire batch transaction. */
|
|
31
|
+
export interface TransactionResult {
|
|
32
|
+
/** Whether all operations succeeded. */
|
|
33
|
+
success: boolean;
|
|
34
|
+
/** Per-operation results. */
|
|
35
|
+
results: OperationResult[];
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Execute multiple SQL operations within a single write transaction.
|
|
39
|
+
*
|
|
40
|
+
* If any operation fails, the entire batch is rolled back and an error is thrown.
|
|
41
|
+
* SELECT results are captured; write operations return change counts and row IDs.
|
|
42
|
+
*
|
|
43
|
+
* `POST /api/admin/:database/transactions`
|
|
44
|
+
*/
|
|
45
|
+
export declare function executeTransaction(pool: DatabasePool, operations: TransactionOperation[]): TransactionResult;
|
|
46
|
+
//# sourceMappingURL=transaction.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transaction.d.ts","sourceRoot":"","sources":["../../src/admin/transaction.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAG1C,+CAA+C;AAC/C,MAAM,WAAW,oBAAoB;IACnC,gDAAgD;IAChD,GAAG,EAAE,MAAM,CAAC;IACZ,uCAAuC;IACvC,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC;CACpB;AAED,yDAAyD;AACzD,MAAM,WAAW,eAAe;IAC9B,mDAAmD;IACnD,KAAK,EAAE,MAAM,CAAC;IACd,gDAAgD;IAChD,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IACjC,2CAA2C;IAC3C,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,oDAAoD;IACpD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uDAAuD;IACvD,eAAe,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CACnC;AAED,6CAA6C;AAC7C,MAAM,WAAW,iBAAiB;IAChC,wCAAwC;IACxC,OAAO,EAAE,OAAO,CAAC;IACjB,6BAA6B;IAC7B,OAAO,EAAE,eAAe,EAAE,CAAC;CAC5B;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,YAAY,EAClB,UAAU,EAAE,oBAAoB,EAAE,GACjC,iBAAiB,CAyCnB"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transaction API — execute multiple SQL operations atomically.
|
|
3
|
+
*
|
|
4
|
+
* All operations in a transaction either succeed together or fail together.
|
|
5
|
+
* Supports a mix of read (SELECT) and write (INSERT, UPDATE, DELETE) operations.
|
|
6
|
+
*
|
|
7
|
+
* @module boltstore/admin/transaction
|
|
8
|
+
*/
|
|
9
|
+
import { toBindings } from "../db/cast";
|
|
10
|
+
/**
|
|
11
|
+
* Execute multiple SQL operations within a single write transaction.
|
|
12
|
+
*
|
|
13
|
+
* If any operation fails, the entire batch is rolled back and an error is thrown.
|
|
14
|
+
* SELECT results are captured; write operations return change counts and row IDs.
|
|
15
|
+
*
|
|
16
|
+
* `POST /api/admin/:database/transactions`
|
|
17
|
+
*/
|
|
18
|
+
export function executeTransaction(pool, operations) {
|
|
19
|
+
if (!Array.isArray(operations) || operations.length === 0) {
|
|
20
|
+
throw Object.assign(new Error("At least one operation is required."), { status: 400 });
|
|
21
|
+
}
|
|
22
|
+
return pool.writeTransaction(() => {
|
|
23
|
+
const db = pool.write();
|
|
24
|
+
const results = [];
|
|
25
|
+
for (let i = 0; i < operations.length; i++) {
|
|
26
|
+
const op = operations[i];
|
|
27
|
+
const params = Array.isArray(op.params) ? op.params : [];
|
|
28
|
+
const trimmed = op.sql.trim().toUpperCase();
|
|
29
|
+
const isSelect = trimmed.startsWith("SELECT") ||
|
|
30
|
+
trimmed.startsWith("PRAGMA") ||
|
|
31
|
+
trimmed.startsWith("EXPLAIN") ||
|
|
32
|
+
trimmed.startsWith("WITH");
|
|
33
|
+
if (isSelect) {
|
|
34
|
+
const rows = db.query(op.sql).all(...toBindings(params));
|
|
35
|
+
const columns = rows.length > 0 ? Object.keys(rows[0]) : [];
|
|
36
|
+
results.push({ index: i, rows, columns });
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
db.run(op.sql, toBindings(params));
|
|
40
|
+
const changesQuery = db.query("SELECT changes() as cnt").get();
|
|
41
|
+
const rowIdQuery = db.query("SELECT last_insert_rowid() as id").get();
|
|
42
|
+
results.push({
|
|
43
|
+
index: i,
|
|
44
|
+
changes: changesQuery?.cnt ?? 0,
|
|
45
|
+
lastInsertRowid: rowIdQuery?.id ?? 0,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return { success: true, results };
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=transaction.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transaction.js","sourceRoot":"","sources":["../../src/admin/transaction.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAgCxC;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB,CAChC,IAAkB,EAClB,UAAkC;IAElC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1D,MAAM,MAAM,CAAC,MAAM,CACjB,IAAI,KAAK,CAAC,qCAAqC,CAAC,EAChD,EAAE,MAAM,EAAE,GAAG,EAAE,CAChB,CAAC;IACJ,CAAC;IAED,OAAO,IAAI,CAAC,gBAAgB,CAAC,GAAG,EAAE;QAChC,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;QACxB,MAAM,OAAO,GAAsB,EAAE,CAAC;QAEtC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YAC3C,MAAM,EAAE,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;YACzB,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;YAEzD,MAAM,OAAO,GAAG,EAAE,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;YAC5C,MAAM,QAAQ,GACZ,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAC;gBAC5B,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAC;gBAC5B,OAAO,CAAC,UAAU,CAAC,SAAS,CAAC;gBAC7B,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;YAE7B,IAAI,QAAQ,EAAE,CAAC;gBACb,MAAM,IAAI,GAAG,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,UAAU,CAAC,MAAM,CAAC,CAA8B,CAAC;gBACtF,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC5D,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;YAC5C,CAAC;iBAAM,CAAC;gBACN,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;gBACnC,MAAM,YAAY,GAAG,EAAE,CAAC,KAAK,CAAC,yBAAyB,CAAC,CAAC,GAAG,EAA4B,CAAC;gBACzF,MAAM,UAAU,GAAG,EAAE,CAAC,KAAK,CAAC,kCAAkC,CAAC,CAAC,GAAG,EAA2B,CAAC;gBAC/F,OAAO,CAAC,IAAI,CAAC;oBACX,KAAK,EAAE,CAAC;oBACR,OAAO,EAAE,YAAY,EAAE,GAAG,IAAI,CAAC;oBAC/B,eAAe,EAAE,UAAU,EAAE,EAAE,IAAI,CAAC;iBACrC,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;IACpC,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Views management module for Boltstore.
|
|
3
|
+
*
|
|
4
|
+
* SQLite views are saved SELECT queries presented as virtual tables.
|
|
5
|
+
* They provide read-only access to pre-defined queries — useful for
|
|
6
|
+
* denormalized data, reporting, and access control.
|
|
7
|
+
*
|
|
8
|
+
* All routes are admin-only (`/api/admin/:database/views`).
|
|
9
|
+
*
|
|
10
|
+
* @module boltstore/admin/views
|
|
11
|
+
*/
|
|
12
|
+
import { DatabasePool } from "../db/pool";
|
|
13
|
+
export interface ViewInfo {
|
|
14
|
+
/** View name. */
|
|
15
|
+
name: string;
|
|
16
|
+
/** The SQL definition (CREATE VIEW ... AS ...). */
|
|
17
|
+
sql: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Create a new view.
|
|
21
|
+
*
|
|
22
|
+
* The SQL must be a SELECT or WITH statement. Write operations,
|
|
23
|
+
* schema changes, and references to system tables are rejected.
|
|
24
|
+
*
|
|
25
|
+
* `POST /api/admin/:database/views`
|
|
26
|
+
*/
|
|
27
|
+
export declare function createView(pool: DatabasePool, name: string, sql: string): ViewInfo;
|
|
28
|
+
/**
|
|
29
|
+
* List all user-created views in the database.
|
|
30
|
+
*
|
|
31
|
+
* Excludes SQLite internal views (sqlite_*).
|
|
32
|
+
*
|
|
33
|
+
* `GET /api/admin/:database/views`
|
|
34
|
+
*/
|
|
35
|
+
export declare function listViews(pool: DatabasePool): ViewInfo[];
|
|
36
|
+
/**
|
|
37
|
+
* Get metadata for a single view, including its SQL definition.
|
|
38
|
+
*
|
|
39
|
+
* `GET /api/admin/:database/views/:name`
|
|
40
|
+
*/
|
|
41
|
+
export declare function getView(pool: DatabasePool, name: string): ViewInfo;
|
|
42
|
+
/**
|
|
43
|
+
* Query data from a view.
|
|
44
|
+
*
|
|
45
|
+
* Supports the same filtering, sorting, and pagination options as
|
|
46
|
+
* `listRecords` on regular collections. The view is treated as a
|
|
47
|
+
* read-only virtual table.
|
|
48
|
+
*
|
|
49
|
+
* `GET /api/admin/:database/views/:name?sort=field&direction=desc&limit=10`
|
|
50
|
+
*/
|
|
51
|
+
export declare function queryView(pool: DatabasePool, name: string, options?: {
|
|
52
|
+
filter?: Record<string, unknown>;
|
|
53
|
+
sort?: string;
|
|
54
|
+
direction?: "asc" | "desc";
|
|
55
|
+
limit?: number;
|
|
56
|
+
offset?: number;
|
|
57
|
+
}): Record<string, unknown>[];
|
|
58
|
+
/**
|
|
59
|
+
* Delete (drop) a view.
|
|
60
|
+
*
|
|
61
|
+
* `DELETE /api/admin/:database/views/:name`
|
|
62
|
+
*/
|
|
63
|
+
export declare function dropView(pool: DatabasePool, name: string): void;
|
|
64
|
+
//# sourceMappingURL=views.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"views.d.ts","sourceRoot":"","sources":["../../src/admin/views.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAS1C,MAAM,WAAW,QAAQ;IACvB,iBAAiB;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,mDAAmD;IACnD,GAAG,EAAE,MAAM,CAAC;CACb;AA8DD;;;;;;;GAOG;AACH,wBAAgB,UAAU,CACxB,IAAI,EAAE,YAAY,EAClB,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,MAAM,GACV,QAAQ,CAuCV;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,YAAY,GAAG,QAAQ,EAAE,CAWxD;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,GAAG,QAAQ,CAmBlE;AAED;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CACvB,IAAI,EAAE,YAAY,EAClB,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE;IACR,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GACA,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAgD3B;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAyB/D"}
|