cursedbelt-server 1.0.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/LICENSE +15 -0
- package/dist/server/activity/index.d.ts +36 -0
- package/dist/server/activity/index.js +36 -0
- package/dist/server/activity/migrations.d.ts +21 -0
- package/dist/server/activity/migrations.js +55 -0
- package/dist/server/activity/shareSource.d.ts +11 -0
- package/dist/server/activity/shareSource.js +45 -0
- package/dist/server/activity/store.d.ts +123 -0
- package/dist/server/activity/store.js +178 -0
- package/dist/server/app.d.ts +56 -0
- package/dist/server/app.js +45 -0
- package/dist/server/audit/index.d.ts +2 -0
- package/dist/server/audit/index.js +2 -0
- package/dist/server/audit/migrations.d.ts +2 -0
- package/dist/server/audit/migrations.js +79 -0
- package/dist/server/audit/sqliteSink.d.ts +36 -0
- package/dist/server/audit/sqliteSink.js +95 -0
- package/dist/server/audit/sqliteSink.test.d.ts +1 -0
- package/dist/server/audit/sqliteSink.test.js +180 -0
- package/dist/server/auth/cookie.d.ts +22 -0
- package/dist/server/auth/cookie.js +35 -0
- package/dist/server/auth/csrf.d.ts +42 -0
- package/dist/server/auth/csrf.js +36 -0
- package/dist/server/auth/fileTokenSigner.d.ts +10 -0
- package/dist/server/auth/fileTokenSigner.js +22 -0
- package/dist/server/auth/getOnly.d.ts +20 -0
- package/dist/server/auth/getOnly.js +27 -0
- package/dist/server/auth/index.d.ts +12 -0
- package/dist/server/auth/index.js +15 -0
- package/dist/server/auth/jwt.d.ts +18 -0
- package/dist/server/auth/jwt.js +56 -0
- package/dist/server/auth/loginThrottle.d.ts +118 -0
- package/dist/server/auth/loginThrottle.js +204 -0
- package/dist/server/auth/middleware.d.ts +14 -0
- package/dist/server/auth/middleware.js +47 -0
- package/dist/server/auth/password.d.ts +14 -0
- package/dist/server/auth/password.js +65 -0
- package/dist/server/auth/sessionStore.d.ts +19 -0
- package/dist/server/auth/sessionStore.js +221 -0
- package/dist/server/auth/totp.d.ts +34 -0
- package/dist/server/auth/totp.js +81 -0
- package/dist/server/auth/types.d.ts +133 -0
- package/dist/server/auth/types.js +1 -0
- package/dist/server/auth/vault.d.ts +27 -0
- package/dist/server/auth/vault.js +57 -0
- package/dist/server/context.d.ts +35 -0
- package/dist/server/context.js +1 -0
- package/dist/server/ctgr.d.ts +32 -0
- package/dist/server/ctgr.js +69 -0
- package/dist/server/db/kysely.d.ts +14 -0
- package/dist/server/db/kysely.js +16 -0
- package/dist/server/db/plumbingTypes.d.ts +116 -0
- package/dist/server/db/plumbingTypes.js +12 -0
- package/dist/server/dropzone/duplicates.d.ts +82 -0
- package/dist/server/dropzone/duplicates.js +90 -0
- package/dist/server/dropzone/index.d.ts +13 -0
- package/dist/server/dropzone/index.js +13 -0
- package/dist/server/dropzone/scan.d.ts +166 -0
- package/dist/server/dropzone/scan.js +272 -0
- package/dist/server/errors.d.ts +7 -0
- package/dist/server/errors.js +7 -0
- package/dist/server/guard/argon2Binding.d.ts +97 -0
- package/dist/server/guard/argon2Binding.js +138 -0
- package/dist/server/guard/index.d.ts +4 -0
- package/dist/server/guard/index.js +6 -0
- package/dist/server/guard/kdfWorker.d.ts +101 -0
- package/dist/server/guard/kdfWorker.js +301 -0
- package/dist/server/guard/kdfWorkerEntry.d.ts +1 -0
- package/dist/server/guard/kdfWorkerEntry.js +15 -0
- package/dist/server/guard/passwordVerifier.d.ts +65 -0
- package/dist/server/guard/passwordVerifier.js +122 -0
- package/dist/server/guard/revocationStore.d.ts +132 -0
- package/dist/server/guard/revocationStore.js +211 -0
- package/dist/server/guard/sessionSigner.d.ts +132 -0
- package/dist/server/guard/sessionSigner.js +303 -0
- package/dist/server/guard/verifier.d.ts +21 -0
- package/dist/server/guard/verifier.js +1 -0
- package/dist/server/incidents/channels.d.ts +161 -0
- package/dist/server/incidents/channels.js +357 -0
- package/dist/server/incidents/checks.d.ts +61 -0
- package/dist/server/incidents/checks.js +55 -0
- package/dist/server/incidents/index.d.ts +51 -0
- package/dist/server/incidents/index.js +51 -0
- package/dist/server/incidents/migrations.d.ts +20 -0
- package/dist/server/incidents/migrations.js +58 -0
- package/dist/server/incidents/notifier.d.ts +79 -0
- package/dist/server/incidents/notifier.js +141 -0
- package/dist/server/incidents/reporter.d.ts +60 -0
- package/dist/server/incidents/reporter.js +49 -0
- package/dist/server/incidents/routes.d.ts +91 -0
- package/dist/server/incidents/routes.js +202 -0
- package/dist/server/incidents/store.d.ts +75 -0
- package/dist/server/incidents/store.js +176 -0
- package/dist/server/incidents/types.d.ts +107 -0
- package/dist/server/incidents/types.js +32 -0
- package/dist/server/index.d.ts +25 -0
- package/dist/server/index.js +53 -0
- package/dist/server/jobs/fireQueue.d.ts +12 -0
- package/dist/server/jobs/fireQueue.js +37 -0
- package/dist/server/jobs/index.d.ts +6 -0
- package/dist/server/jobs/index.js +10 -0
- package/dist/server/jobs/logger.d.ts +10 -0
- package/dist/server/jobs/logger.js +14 -0
- package/dist/server/jobs/plainjobAdapter.d.ts +19 -0
- package/dist/server/jobs/plainjobAdapter.js +48 -0
- package/dist/server/jobs/polling.d.ts +17 -0
- package/dist/server/jobs/polling.js +79 -0
- package/dist/server/jobs/runner.d.ts +24 -0
- package/dist/server/jobs/runner.js +186 -0
- package/dist/server/jobs/timeout.d.ts +28 -0
- package/dist/server/jobs/timeout.js +47 -0
- package/dist/server/jobs/types.d.ts +167 -0
- package/dist/server/jobs/types.js +12 -0
- package/dist/server/master-lock/guard.d.ts +53 -0
- package/dist/server/master-lock/guard.js +205 -0
- package/dist/server/master-lock/index.d.ts +21 -0
- package/dist/server/master-lock/index.js +21 -0
- package/dist/server/master-lock/lockPage.d.ts +34 -0
- package/dist/server/master-lock/lockPage.js +281 -0
- package/dist/server/master-lock/masterLock.d.ts +294 -0
- package/dist/server/master-lock/masterLock.js +523 -0
- package/dist/server/master-lock/principals.d.ts +76 -0
- package/dist/server/master-lock/principals.js +157 -0
- package/dist/server/master-lock/seed.d.ts +47 -0
- package/dist/server/master-lock/seed.js +93 -0
- package/dist/server/master-lock/store.d.ts +19 -0
- package/dist/server/master-lock/store.js +20 -0
- package/dist/server/media-bun/image.d.ts +7 -0
- package/dist/server/media-bun/image.js +96 -0
- package/dist/server/media-bun/index.d.ts +4 -0
- package/dist/server/media-bun/index.js +10 -0
- package/dist/server/media-bun/streaming.d.ts +86 -0
- package/dist/server/media-bun/streaming.js +237 -0
- package/dist/server/media-bun/video.d.ts +35 -0
- package/dist/server/media-bun/video.js +181 -0
- package/dist/server/metrics/metricsBuffer.d.ts +39 -0
- package/dist/server/metrics/metricsBuffer.js +36 -0
- package/dist/server/metrics/normalizeRoute.d.ts +5 -0
- package/dist/server/metrics/normalizeRoute.js +58 -0
- package/dist/server/middleware/correlationId.d.ts +9 -0
- package/dist/server/middleware/correlationId.js +20 -0
- package/dist/server/middleware/corsAllowlist.d.ts +38 -0
- package/dist/server/middleware/corsAllowlist.js +37 -0
- package/dist/server/middleware/errorEnvelope.d.ts +17 -0
- package/dist/server/middleware/errorEnvelope.js +32 -0
- package/dist/server/middleware/index.d.ts +9 -0
- package/dist/server/middleware/index.js +10 -0
- package/dist/server/middleware/localOrigin.d.ts +71 -0
- package/dist/server/middleware/localOrigin.js +119 -0
- package/dist/server/middleware/rateLimit.d.ts +56 -0
- package/dist/server/middleware/rateLimit.js +86 -0
- package/dist/server/middleware/requestLogger.d.ts +29 -0
- package/dist/server/middleware/requestLogger.js +47 -0
- package/dist/server/middleware/securityHeaders.d.ts +29 -0
- package/dist/server/middleware/securityHeaders.js +44 -0
- package/dist/server/middleware/zValidator.d.ts +29 -0
- package/dist/server/middleware/zValidator.js +57 -0
- package/dist/server/migration/runner.d.ts +42 -0
- package/dist/server/migration/runner.js +70 -0
- package/dist/server/migration/types.d.ts +16 -0
- package/dist/server/migration/types.js +1 -0
- package/dist/server/net/outboundGuard.d.ts +30 -0
- package/dist/server/net/outboundGuard.js +199 -0
- package/dist/server/notifications/hub.d.ts +67 -0
- package/dist/server/notifications/hub.js +187 -0
- package/dist/server/notifications/index.d.ts +47 -0
- package/dist/server/notifications/index.js +46 -0
- package/dist/server/notifications/migrations.d.ts +13 -0
- package/dist/server/notifications/migrations.js +20 -0
- package/dist/server/notifications/routes.d.ts +58 -0
- package/dist/server/notifications/routes.js +113 -0
- package/dist/server/notifications/service.d.ts +58 -0
- package/dist/server/notifications/service.js +41 -0
- package/dist/server/paths/appDataHome.d.ts +19 -0
- package/dist/server/paths/appDataHome.js +68 -0
- package/dist/server/private-media/crypto.d.ts +37 -0
- package/dist/server/private-media/crypto.js +111 -0
- package/dist/server/private-media/index-store.d.ts +31 -0
- package/dist/server/private-media/index-store.js +36 -0
- package/dist/server/private-media/mountPrivateRoutes.d.ts +26 -0
- package/dist/server/private-media/mountPrivateRoutes.js +219 -0
- package/dist/server/private-media/pipeline.d.ts +37 -0
- package/dist/server/private-media/pipeline.js +97 -0
- package/dist/server/private-media/unlock.d.ts +52 -0
- package/dist/server/private-media/unlock.js +55 -0
- package/dist/server/resume/index.d.ts +2 -0
- package/dist/server/resume/index.js +2 -0
- package/dist/server/resume/routes.d.ts +41 -0
- package/dist/server/resume/routes.js +50 -0
- package/dist/server/resume/store.d.ts +37 -0
- package/dist/server/resume/store.js +38 -0
- package/dist/server/retention/archive.d.ts +78 -0
- package/dist/server/retention/archive.js +284 -0
- package/dist/server/retention/fleet.d.ts +50 -0
- package/dist/server/retention/fleet.js +66 -0
- package/dist/server/retention/index.d.ts +38 -0
- package/dist/server/retention/index.js +38 -0
- package/dist/server/retention/policy.d.ts +263 -0
- package/dist/server/retention/policy.js +264 -0
- package/dist/server/retention/store.d.ts +101 -0
- package/dist/server/retention/store.js +251 -0
- package/dist/server/retention/sweeper.d.ts +69 -0
- package/dist/server/retention/sweeper.js +289 -0
- package/dist/server/retention/target.d.ts +143 -0
- package/dist/server/retention/target.js +438 -0
- package/dist/server/search/index.d.ts +2 -0
- package/dist/server/search/index.js +6 -0
- package/dist/server/search/pgSearchProvider.d.ts +91 -0
- package/dist/server/search/pgSearchProvider.js +330 -0
- package/dist/server/search/pgSearchProvider.pg.test.d.ts +1 -0
- package/dist/server/search/pgSearchProvider.pg.test.js +338 -0
- package/dist/server/search/pgSearchSchema.d.ts +81 -0
- package/dist/server/search/pgSearchSchema.js +89 -0
- package/dist/server/sharing/index.d.ts +18 -0
- package/dist/server/sharing/index.js +16 -0
- package/dist/server/sharing/migrations.d.ts +6 -0
- package/dist/server/sharing/migrations.js +79 -0
- package/dist/server/sharing/router.d.ts +111 -0
- package/dist/server/sharing/router.js +93 -0
- package/dist/server/sharing/sql.d.ts +67 -0
- package/dist/server/sharing/sql.js +88 -0
- package/dist/server/sharing/store.d.ts +173 -0
- package/dist/server/sharing/store.js +484 -0
- package/dist/server/sqlite/backup.d.ts +48 -0
- package/dist/server/sqlite/backup.js +97 -0
- package/dist/server/sqlite/browse.d.ts +42 -0
- package/dist/server/sqlite/browse.js +84 -0
- package/dist/server/sqlite/export.d.ts +32 -0
- package/dist/server/sqlite/export.js +42 -0
- package/dist/server/sqlite/fts5.d.ts +106 -0
- package/dist/server/sqlite/fts5.js +154 -0
- package/dist/server/sqlite/health.d.ts +44 -0
- package/dist/server/sqlite/health.js +52 -0
- package/dist/server/sqlite/import.d.ts +36 -0
- package/dist/server/sqlite/import.js +108 -0
- package/dist/server/sqlite/index.d.ts +9 -0
- package/dist/server/sqlite/index.js +15 -0
- package/dist/server/sqlite/readonlySql.d.ts +15 -0
- package/dist/server/sqlite/readonlySql.js +65 -0
- package/dist/server/sqlite/stats.d.ts +22 -0
- package/dist/server/sqlite/stats.js +32 -0
- package/dist/server/sqlite/viewer.d.ts +17 -0
- package/dist/server/sqlite/viewer.js +56 -0
- package/dist/server/storage/binaryServerProcessing.d.ts +23 -0
- package/dist/server/storage/binaryServerProcessing.js +81 -0
- package/dist/server/storage/binaryServerStore.d.ts +3 -0
- package/dist/server/storage/binaryServerStore.js +95 -0
- package/dist/server/storage/diskStore.d.ts +2 -0
- package/dist/server/storage/diskStore.js +88 -0
- package/dist/server/storage/files/adapter.d.ts +33 -0
- package/dist/server/storage/files/adapter.js +203 -0
- package/dist/server/storage/files/catalogue.d.ts +72 -0
- package/dist/server/storage/files/catalogue.js +1033 -0
- package/dist/server/storage/files/chunkedUpload.d.ts +56 -0
- package/dist/server/storage/files/chunkedUpload.js +70 -0
- package/dist/server/storage/files/fileRoutes.d.ts +51 -0
- package/dist/server/storage/files/fileRoutes.js +405 -0
- package/dist/server/storage/files/index.d.ts +9 -0
- package/dist/server/storage/files/index.js +11 -0
- package/dist/server/storage/files/migrations.d.ts +2 -0
- package/dist/server/storage/files/migrations.js +163 -0
- package/dist/server/storage/files/mimeByExt.d.ts +1 -0
- package/dist/server/storage/files/mimeByExt.js +13 -0
- package/dist/server/storage/files/stack.d.ts +136 -0
- package/dist/server/storage/files/stack.js +132 -0
- package/dist/server/storage/files/storageOps.d.ts +54 -0
- package/dist/server/storage/files/storageOps.js +110 -0
- package/dist/server/storage/files/types.d.ts +261 -0
- package/dist/server/storage/files/types.js +22 -0
- package/dist/server/storage/imageThumbnail.d.ts +3 -0
- package/dist/server/storage/imageThumbnail.js +50 -0
- package/dist/server/storage/index.d.ts +15 -0
- package/dist/server/storage/index.js +27 -0
- package/dist/server/storage/mountMediaRoutes.d.ts +28 -0
- package/dist/server/storage/mountMediaRoutes.js +141 -0
- package/dist/server/storage/objectStoreMediaStore.d.ts +4 -0
- package/dist/server/storage/objectStoreMediaStore.js +57 -0
- package/dist/server/storage/s3ObjectStore.d.ts +63 -0
- package/dist/server/storage/s3ObjectStore.js +187 -0
- package/dist/server/storage/sources.d.ts +47 -0
- package/dist/server/storage/sources.js +148 -0
- package/dist/server/storage/streamRange.d.ts +19 -0
- package/dist/server/storage/streamRange.js +69 -0
- package/dist/server/storage/streamToken.d.ts +16 -0
- package/dist/server/storage/streamToken.js +54 -0
- package/dist/server/storage/transcodeJobs.d.ts +65 -0
- package/dist/server/storage/transcodeJobs.js +104 -0
- package/dist/server/storage/types.d.ts +193 -0
- package/dist/server/storage/types.js +41 -0
- package/dist/server/storage/videoDerivativeKeys.d.ts +127 -0
- package/dist/server/storage/videoDerivativeKeys.js +149 -0
- package/dist/server/storage/videoProcessor.d.ts +30 -0
- package/dist/server/storage/videoProcessor.js +148 -0
- package/dist/server/sync/alarm.d.ts +34 -0
- package/dist/server/sync/alarm.js +91 -0
- package/dist/server/sync/commands.d.ts +87 -0
- package/dist/server/sync/commands.js +241 -0
- package/dist/server/sync/engine.d.ts +62 -0
- package/dist/server/sync/engine.js +184 -0
- package/dist/server/sync/http.d.ts +106 -0
- package/dist/server/sync/http.js +243 -0
- package/dist/server/sync/index.d.ts +41 -0
- package/dist/server/sync/index.js +41 -0
- package/dist/server/sync/opLog.d.ts +61 -0
- package/dist/server/sync/opLog.js +96 -0
- package/dist/server/sync/planner.d.ts +31 -0
- package/dist/server/sync/planner.js +30 -0
- package/dist/server/sync/status.d.ts +63 -0
- package/dist/server/sync/status.js +49 -0
- package/dist/server/sync/timer.d.ts +52 -0
- package/dist/server/sync/timer.js +170 -0
- package/dist/server/sync/tokens.d.ts +25 -0
- package/dist/server/sync/tokens.js +51 -0
- package/dist/server/sync/types.d.ts +101 -0
- package/dist/server/sync/types.js +29 -0
- package/dist/server/ws/eventBus.d.ts +14 -0
- package/dist/server/ws/eventBus.js +42 -0
- package/dist/server/ws/hub.d.ts +20 -0
- package/dist/server/ws/hub.js +154 -0
- package/dist/server/ws/index.d.ts +5 -0
- package/dist/server/ws/index.js +7 -0
- package/dist/server/ws/presence.d.ts +11 -0
- package/dist/server/ws/presence.js +20 -0
- package/dist/server/ws/room.d.ts +17 -0
- package/dist/server/ws/room.js +23 -0
- package/dist/server/ws/types.d.ts +122 -0
- package/dist/server/ws/types.js +1 -0
- package/package.json +234 -0
- package/src/declaredDepsAreImported.spec.ts +87 -0
- package/src/leafSubpathsImportNothing.spec.ts +358 -0
- package/src/lockfileIsTracked.spec.ts +50 -0
- package/src/namedSubpathsResolve.spec.ts +122 -0
- package/src/noPathDeps.spec.ts +24 -0
- package/src/publishShape.spec.ts +89 -0
- package/src/server/activity/index.ts +59 -0
- package/src/server/activity/migrations.ts +67 -0
- package/src/server/activity/shareSource.ts +57 -0
- package/src/server/activity/store.spec.ts +255 -0
- package/src/server/activity/store.ts +368 -0
- package/src/server/app.spec.ts +243 -0
- package/src/server/app.ts +89 -0
- package/src/server/audit/index.ts +2 -0
- package/src/server/audit/migrations.ts +88 -0
- package/src/server/audit/sqliteSink.test.ts +200 -0
- package/src/server/audit/sqliteSink.ts +162 -0
- package/src/server/auth/cookie.spec.ts +46 -0
- package/src/server/auth/cookie.ts +42 -0
- package/src/server/auth/csrf.spec.ts +81 -0
- package/src/server/auth/csrf.ts +83 -0
- package/src/server/auth/fileTokenSigner.ts +40 -0
- package/src/server/auth/getOnly.spec.ts +53 -0
- package/src/server/auth/getOnly.ts +32 -0
- package/src/server/auth/index.ts +51 -0
- package/src/server/auth/jwt.spec.ts +106 -0
- package/src/server/auth/jwt.ts +75 -0
- package/src/server/auth/loginThrottle.spec.ts +213 -0
- package/src/server/auth/loginThrottle.ts +261 -0
- package/src/server/auth/middleware.spec.ts +148 -0
- package/src/server/auth/middleware.ts +56 -0
- package/src/server/auth/password.spec.ts +48 -0
- package/src/server/auth/password.ts +71 -0
- package/src/server/auth/sessionStore.spec.ts +189 -0
- package/src/server/auth/sessionStore.ts +261 -0
- package/src/server/auth/totp.spec.ts +73 -0
- package/src/server/auth/totp.ts +92 -0
- package/src/server/auth/types.ts +146 -0
- package/src/server/auth/vault.spec.ts +51 -0
- package/src/server/auth/vault.ts +71 -0
- package/src/server/context.ts +37 -0
- package/src/server/ctgr.spec.ts +138 -0
- package/src/server/ctgr.ts +108 -0
- package/src/server/db/kysely.spec.ts +62 -0
- package/src/server/db/kysely.ts +21 -0
- package/src/server/db/plumbingTypes.ts +126 -0
- package/src/server/dropzone/duplicates.ts +121 -0
- package/src/server/dropzone/index.ts +30 -0
- package/src/server/dropzone/scan.spec.ts +255 -0
- package/src/server/dropzone/scan.ts +342 -0
- package/src/server/errors.spec.ts +76 -0
- package/src/server/errors.ts +22 -0
- package/src/server/guard/argon2Binding.spec.ts +117 -0
- package/src/server/guard/argon2Binding.ts +195 -0
- package/src/server/guard/argon2Kat.spec.ts +250 -0
- package/src/server/guard/guard.spec.ts +390 -0
- package/src/server/guard/index.ts +6 -0
- package/src/server/guard/kdfWorker.spec.ts +283 -0
- package/src/server/guard/kdfWorker.ts +367 -0
- package/src/server/guard/kdfWorkerEntry.ts +16 -0
- package/src/server/guard/passwordVerifier.spec.ts +71 -0
- package/src/server/guard/passwordVerifier.ts +175 -0
- package/src/server/guard/revocationStore.spec.ts +135 -0
- package/src/server/guard/revocationStore.ts +291 -0
- package/src/server/guard/sessionSigner.ts +425 -0
- package/src/server/guard/verifier.ts +27 -0
- package/src/server/incidents/channels.ts +487 -0
- package/src/server/incidents/checks.ts +120 -0
- package/src/server/incidents/incidents.spec.ts +463 -0
- package/src/server/incidents/index.ts +96 -0
- package/src/server/incidents/migrations.ts +66 -0
- package/src/server/incidents/notifier.ts +215 -0
- package/src/server/incidents/reporter.ts +110 -0
- package/src/server/incidents/routes.ts +314 -0
- package/src/server/incidents/store.ts +329 -0
- package/src/server/incidents/types.ts +116 -0
- package/src/server/index.ts +248 -0
- package/src/server/jobs/fireQueue.spec.ts +85 -0
- package/src/server/jobs/fireQueue.ts +37 -0
- package/src/server/jobs/index.ts +26 -0
- package/src/server/jobs/logger.ts +16 -0
- package/src/server/jobs/plainjobAdapter.spec.ts +34 -0
- package/src/server/jobs/plainjobAdapter.ts +54 -0
- package/src/server/jobs/polling.spec.ts +219 -0
- package/src/server/jobs/polling.ts +81 -0
- package/src/server/jobs/runner.spec.ts +213 -0
- package/src/server/jobs/runner.ts +213 -0
- package/src/server/jobs/timeout.spec.ts +35 -0
- package/src/server/jobs/timeout.ts +53 -0
- package/src/server/jobs/types.ts +188 -0
- package/src/server/master-lock/guard.spec.ts +347 -0
- package/src/server/master-lock/guard.ts +290 -0
- package/src/server/master-lock/index.ts +44 -0
- package/src/server/master-lock/lockPage.spec.ts +88 -0
- package/src/server/master-lock/lockPage.ts +313 -0
- package/src/server/master-lock/masterLock.spec.ts +338 -0
- package/src/server/master-lock/masterLock.ts +627 -0
- package/src/server/master-lock/principals.spec.ts +378 -0
- package/src/server/master-lock/principals.ts +175 -0
- package/src/server/master-lock/seed.spec.ts +69 -0
- package/src/server/master-lock/seed.ts +112 -0
- package/src/server/master-lock/store.ts +35 -0
- package/src/server/media-bun/image.spec.ts +115 -0
- package/src/server/media-bun/image.ts +134 -0
- package/src/server/media-bun/index.ts +52 -0
- package/src/server/media-bun/streaming.spec.ts +134 -0
- package/src/server/media-bun/streaming.ts +347 -0
- package/src/server/media-bun/video.spec.ts +115 -0
- package/src/server/media-bun/video.ts +232 -0
- package/src/server/metrics/metricsBuffer.ts +92 -0
- package/src/server/metrics/normalizeRoute.spec.ts +51 -0
- package/src/server/metrics/normalizeRoute.ts +63 -0
- package/src/server/middleware/correlationId.ts +24 -0
- package/src/server/middleware/corsAllowlist.spec.ts +118 -0
- package/src/server/middleware/corsAllowlist.ts +69 -0
- package/src/server/middleware/errorEnvelope.ts +38 -0
- package/src/server/middleware/index.ts +19 -0
- package/src/server/middleware/localOrigin.spec.ts +168 -0
- package/src/server/middleware/localOrigin.ts +139 -0
- package/src/server/middleware/rateLimit.spec.ts +72 -0
- package/src/server/middleware/rateLimit.ts +125 -0
- package/src/server/middleware/requestLogger.ts +95 -0
- package/src/server/middleware/securityHeaders.ts +78 -0
- package/src/server/middleware/zValidator.spec.ts +251 -0
- package/src/server/middleware/zValidator.ts +114 -0
- package/src/server/migration/runner.spec.ts +88 -0
- package/src/server/migration/runner.ts +87 -0
- package/src/server/migration/types.ts +17 -0
- package/src/server/net/outboundGuard.spec.ts +90 -0
- package/src/server/net/outboundGuard.ts +205 -0
- package/src/server/notifications/hub.ts +245 -0
- package/src/server/notifications/index.ts +77 -0
- package/src/server/notifications/migrations.ts +39 -0
- package/src/server/notifications/routes.ts +177 -0
- package/src/server/notifications/service.spec.ts +305 -0
- package/src/server/notifications/service.ts +112 -0
- package/src/server/paths/appDataHome.spec.ts +87 -0
- package/src/server/paths/appDataHome.ts +84 -0
- package/src/server/private-media/blobRoute.spec.ts +165 -0
- package/src/server/private-media/crypto.spec.ts +81 -0
- package/src/server/private-media/crypto.ts +155 -0
- package/src/server/private-media/gatePolicy.spec.ts +98 -0
- package/src/server/private-media/index-store.ts +83 -0
- package/src/server/private-media/mountPrivateRoutes.ts +265 -0
- package/src/server/private-media/pipeline.spec.ts +111 -0
- package/src/server/private-media/pipeline.ts +170 -0
- package/src/server/private-media/routes.spec.ts +265 -0
- package/src/server/private-media/unlock.ts +112 -0
- package/src/server/resume/index.ts +8 -0
- package/src/server/resume/resume.spec.ts +118 -0
- package/src/server/resume/routes.ts +92 -0
- package/src/server/resume/store.ts +82 -0
- package/src/server/retention/archive.spec.ts +181 -0
- package/src/server/retention/archive.ts +353 -0
- package/src/server/retention/fleet.ts +104 -0
- package/src/server/retention/index.ts +101 -0
- package/src/server/retention/policy.spec.ts +132 -0
- package/src/server/retention/policy.ts +392 -0
- package/src/server/retention/store.spec.ts +236 -0
- package/src/server/retention/store.ts +338 -0
- package/src/server/retention/sweeper.spec.ts +321 -0
- package/src/server/retention/sweeper.ts +367 -0
- package/src/server/retention/target.spec.ts +592 -0
- package/src/server/retention/target.ts +613 -0
- package/src/server/search/index.ts +12 -0
- package/src/server/search/pgSearchProvider.pg.test.ts +392 -0
- package/src/server/search/pgSearchProvider.ts +421 -0
- package/src/server/search/pgSearchSchema.ts +147 -0
- package/src/server/sharing/index.ts +65 -0
- package/src/server/sharing/migrations.ts +128 -0
- package/src/server/sharing/router.spec.ts +167 -0
- package/src/server/sharing/router.ts +194 -0
- package/src/server/sharing/sql.ts +109 -0
- package/src/server/sharing/store.spec.ts +408 -0
- package/src/server/sharing/store.ts +737 -0
- package/src/server/sqlite/backup.spec.ts +114 -0
- package/src/server/sqlite/backup.ts +154 -0
- package/src/server/sqlite/browse.spec.ts +70 -0
- package/src/server/sqlite/browse.ts +120 -0
- package/src/server/sqlite/export.spec.ts +43 -0
- package/src/server/sqlite/export.ts +74 -0
- package/src/server/sqlite/fts5.spec.ts +175 -0
- package/src/server/sqlite/fts5.ts +255 -0
- package/src/server/sqlite/health.spec.ts +79 -0
- package/src/server/sqlite/health.ts +105 -0
- package/src/server/sqlite/import.spec.ts +75 -0
- package/src/server/sqlite/import.ts +141 -0
- package/src/server/sqlite/index.ts +64 -0
- package/src/server/sqlite/readonlySql.spec.ts +58 -0
- package/src/server/sqlite/readonlySql.ts +67 -0
- package/src/server/sqlite/stats.spec.ts +21 -0
- package/src/server/sqlite/stats.ts +61 -0
- package/src/server/sqlite/viewer.spec.ts +127 -0
- package/src/server/sqlite/viewer.ts +72 -0
- package/src/server/storage/binaryServerProcessing.integration.spec.ts +222 -0
- package/src/server/storage/binaryServerProcessing.spec.ts +150 -0
- package/src/server/storage/binaryServerProcessing.ts +127 -0
- package/src/server/storage/binaryServerStore.ts +102 -0
- package/src/server/storage/chunked.integration.spec.ts +185 -0
- package/src/server/storage/diskStore.ts +83 -0
- package/src/server/storage/files/adapter.photo.display.spec.ts +169 -0
- package/src/server/storage/files/adapter.photo.spec.ts +128 -0
- package/src/server/storage/files/adapter.spec.ts +121 -0
- package/src/server/storage/files/adapter.ts +255 -0
- package/src/server/storage/files/catalogue.media-host.spec.ts +184 -0
- package/src/server/storage/files/catalogue.orderings.spec.ts +157 -0
- package/src/server/storage/files/catalogue.private.spec.ts +106 -0
- package/src/server/storage/files/catalogue.recover.spec.ts +131 -0
- package/src/server/storage/files/catalogue.sort.spec.ts +154 -0
- package/src/server/storage/files/catalogue.spec.ts +133 -0
- package/src/server/storage/files/catalogue.trash-import.spec.ts +125 -0
- package/src/server/storage/files/catalogue.ts +1326 -0
- package/src/server/storage/files/chunkedUpload.spec.ts +111 -0
- package/src/server/storage/files/chunkedUpload.ts +146 -0
- package/src/server/storage/files/collections.spec.ts +294 -0
- package/src/server/storage/files/fileRoutes.replace.spec.ts +88 -0
- package/src/server/storage/files/fileRoutes.spec.ts +327 -0
- package/src/server/storage/files/fileRoutes.ts +462 -0
- package/src/server/storage/files/index.ts +66 -0
- package/src/server/storage/files/migrations.ts +187 -0
- package/src/server/storage/files/mimeByExt.ts +14 -0
- package/src/server/storage/files/stack.spec.ts +258 -0
- package/src/server/storage/files/stack.ts +284 -0
- package/src/server/storage/files/storageOps.spec.ts +126 -0
- package/src/server/storage/files/storageOps.ts +213 -0
- package/src/server/storage/files/types.ts +285 -0
- package/src/server/storage/imageThumbnail.spec.ts +81 -0
- package/src/server/storage/imageThumbnail.ts +60 -0
- package/src/server/storage/index.ts +122 -0
- package/src/server/storage/mountMediaRoutes.ts +231 -0
- package/src/server/storage/objectStore.spec.ts +272 -0
- package/src/server/storage/objectStoreMediaStore.ts +63 -0
- package/src/server/storage/s3ObjectStore.ts +239 -0
- package/src/server/storage/sources.ts +245 -0
- package/src/server/storage/storage.spec.ts +219 -0
- package/src/server/storage/streamRange.ts +92 -0
- package/src/server/storage/streamToken.ts +76 -0
- package/src/server/storage/transcode.integration.spec.ts +134 -0
- package/src/server/storage/transcodeJobs.spec.ts +152 -0
- package/src/server/storage/transcodeJobs.ts +171 -0
- package/src/server/storage/types.ts +230 -0
- package/src/server/storage/videoDerivativeKeys.spec.ts +112 -0
- package/src/server/storage/videoDerivativeKeys.ts +172 -0
- package/src/server/storage/videoProcessor.posterOnly.spec.ts +106 -0
- package/src/server/storage/videoProcessor.ts +229 -0
- package/src/server/sync/alarm.spec.ts +149 -0
- package/src/server/sync/alarm.ts +137 -0
- package/src/server/sync/commands.spec.ts +145 -0
- package/src/server/sync/commands.ts +361 -0
- package/src/server/sync/engine.spec.ts +496 -0
- package/src/server/sync/engine.ts +255 -0
- package/src/server/sync/http.ts +316 -0
- package/src/server/sync/httpRemote.spec.ts +158 -0
- package/src/server/sync/index.ts +90 -0
- package/src/server/sync/opLog.spec.ts +110 -0
- package/src/server/sync/opLog.ts +189 -0
- package/src/server/sync/planner.spec.ts +53 -0
- package/src/server/sync/planner.ts +62 -0
- package/src/server/sync/status.spec.ts +92 -0
- package/src/server/sync/status.ts +108 -0
- package/src/server/sync/timer.spec.ts +150 -0
- package/src/server/sync/timer.ts +207 -0
- package/src/server/sync/tokens.spec.ts +38 -0
- package/src/server/sync/tokens.ts +94 -0
- package/src/server/sync/types.ts +108 -0
- package/src/server/ws/eventBus.spec.ts +67 -0
- package/src/server/ws/eventBus.ts +46 -0
- package/src/server/ws/hub.integration.spec.ts +93 -0
- package/src/server/ws/hub.spec.ts +282 -0
- package/src/server/ws/hub.ts +168 -0
- package/src/server/ws/index.ts +20 -0
- package/src/server/ws/presence.spec.ts +27 -0
- package/src/server/ws/presence.ts +26 -0
- package/src/server/ws/room.spec.ts +36 -0
- package/src/server/ws/room.ts +29 -0
- package/src/server/ws/types.ts +134 -0
- package/src/skippedTestsAreAnswered.spec.ts +215 -0
- package/src/testTmpRoot.spec.ts +102 -0
|
@@ -0,0 +1,462 @@
|
|
|
1
|
+
import type { Context, Hono } from 'hono';
|
|
2
|
+
import { MAX_SINGLE_UPLOAD_BYTES } from 'cursedbelt-core/media/upload-limits';
|
|
3
|
+
import type { UnlockPasswordPolicy, UnlockService } from '../../private-media/unlock';
|
|
4
|
+
import { serveRange } from '../streamRange';
|
|
5
|
+
import type { StreamTokenService } from '../streamToken';
|
|
6
|
+
import type { MediaCtx, MediaStore } from '../types';
|
|
7
|
+
import { fileBinaryKey } from './catalogue';
|
|
8
|
+
import type { ChunkedUploadService } from './chunkedUpload';
|
|
9
|
+
import {
|
|
10
|
+
FILE_SORTS,
|
|
11
|
+
type FileCatalogue,
|
|
12
|
+
type ListFilesOptions,
|
|
13
|
+
type PrivateFileCatalogue,
|
|
14
|
+
} from './types';
|
|
15
|
+
|
|
16
|
+
// ── File binary + upload routes ─────────────────────────────────────────────────
|
|
17
|
+
// The FileCatalogue's byte edge for GENERAL files (images/docs/audio/any) — the piece
|
|
18
|
+
// mountMediaRoutes (video-only: mp4/hls/poster/sprite) doesn't cover. A file's primary binary
|
|
19
|
+
// lives at fs/<owner>/<id> and is served here, token-authorized like the media routes (an <img>
|
|
20
|
+
// can't send a Bearer). Uploads DO carry the app Bearer, so the PUT routes are session-guarded.
|
|
21
|
+
//
|
|
22
|
+
// `sourceId` for a file token is the constant 'files'. The served Content-Type is carried as a
|
|
23
|
+
// signed-URL query (`?ct=`) — the catalogue knows each file's mime when it builds the URL, so the
|
|
24
|
+
// route needn't re-derive it. It only ever serves the token owner's own bytes.
|
|
25
|
+
|
|
26
|
+
const FILE_SOURCE_ID = 'files';
|
|
27
|
+
// Guard the echoed Content-Type: a plain type/subtype, no header injection.
|
|
28
|
+
const SAFE_MIME = /^[\w.+-]+\/[\w.+-]+$/;
|
|
29
|
+
|
|
30
|
+
/** The 413 message a single-PUT over the cap returns — the client treats it as a hard error. */
|
|
31
|
+
export const SINGLE_UPLOAD_TOO_LARGE = 'file too large for a single request — use chunked upload';
|
|
32
|
+
|
|
33
|
+
/** Thrown into the body stream when it exceeds the single-request cap mid-stream (no Content-Length
|
|
34
|
+
* to fail fast on). Handlers catch it and turn it into a clean 413 instead of a 500. */
|
|
35
|
+
class UploadTooLargeError extends Error {
|
|
36
|
+
constructor() {
|
|
37
|
+
super(SINGLE_UPLOAD_TOO_LARGE);
|
|
38
|
+
this.name = 'UploadTooLargeError';
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const isUploadTooLarge = (e: unknown): boolean =>
|
|
43
|
+
e instanceof UploadTooLargeError || (e instanceof Error && e.message === SINGLE_UPLOAD_TOO_LARGE);
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Enforce the single-request upload cap (MAX_SINGLE_UPLOAD_BYTES) on a streamed PUT body. The law:
|
|
47
|
+
* no single HTTP request may carry more than the cap — larger files MUST chunk. Fails FAST on the
|
|
48
|
+
* Content-Length header (browsers set it for a File/Blob body), and — for length-less chunked
|
|
49
|
+
* transfer — wraps the stream to abort once the accumulated bytes pass the cap (the wrapping error
|
|
50
|
+
* surfaces from `catalogue.ingest`, which the handlers turn into a 413). Returns either the body to
|
|
51
|
+
* hand to the catalogue, or a 413 Response to return immediately.
|
|
52
|
+
*/
|
|
53
|
+
function guardSingleUpload(
|
|
54
|
+
c: Context,
|
|
55
|
+
body: ReadableStream<Uint8Array>,
|
|
56
|
+
): ReadableStream<Uint8Array> | Response {
|
|
57
|
+
const declared = Number(c.req.header('content-length'));
|
|
58
|
+
if (Number.isFinite(declared) && declared > MAX_SINGLE_UPLOAD_BYTES) {
|
|
59
|
+
return c.json({ error: SINGLE_UPLOAD_TOO_LARGE }, 413);
|
|
60
|
+
}
|
|
61
|
+
let seen = 0;
|
|
62
|
+
return body.pipeThrough(
|
|
63
|
+
new TransformStream<Uint8Array, Uint8Array>({
|
|
64
|
+
transform(chunk, ctrl) {
|
|
65
|
+
seen += chunk.byteLength;
|
|
66
|
+
if (seen > MAX_SINGLE_UPLOAD_BYTES) {
|
|
67
|
+
ctrl.error(new UploadTooLargeError());
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
ctrl.enqueue(chunk);
|
|
71
|
+
},
|
|
72
|
+
}),
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface FileUrls {
|
|
77
|
+
/** A GET URL for a file's primary binary, carrying its mime as `?ct=`. */
|
|
78
|
+
build(ctx: MediaCtx, fileId: string, mime: string): Promise<{ url: string }>;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function createFileUrls(opts: {
|
|
82
|
+
/** Path-only ('/_files') same-origin, or an absolute origin+path for split-origin. */
|
|
83
|
+
baseUrl: string;
|
|
84
|
+
token: StreamTokenService;
|
|
85
|
+
owner?: (ctx: MediaCtx) => string;
|
|
86
|
+
}): FileUrls {
|
|
87
|
+
const ownerOf = opts.owner ?? ((c: MediaCtx) => c.tenantId);
|
|
88
|
+
const base = opts.baseUrl.replace(/\/$/, '');
|
|
89
|
+
return {
|
|
90
|
+
async build(ctx, fileId, mime) {
|
|
91
|
+
const token = await opts.token.sign({
|
|
92
|
+
owner: ownerOf(ctx),
|
|
93
|
+
sourceId: FILE_SOURCE_ID,
|
|
94
|
+
videoId: fileId,
|
|
95
|
+
});
|
|
96
|
+
return { url: `${base}/s/${token}/${fileId}?ct=${encodeURIComponent(mime)}` };
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export type MountFileRoutesOptions = {
|
|
102
|
+
store: MediaStore;
|
|
103
|
+
token: StreamTokenService;
|
|
104
|
+
/** Path mount (default '/_files'). */
|
|
105
|
+
basePath?: string;
|
|
106
|
+
/** Session auth for the upload PUTs (a fetch CAN send the Bearer). Return a ctx or null. */
|
|
107
|
+
authorizeUpload?: (c: Context) => Promise<MediaCtx | null> | MediaCtx | null;
|
|
108
|
+
/** The catalogue uploads land in. Required for the upload routes to be mounted. */
|
|
109
|
+
catalogue?: FileCatalogue & PrivateFileCatalogue;
|
|
110
|
+
/** Wire private uploads: derive a per-user set key from the request (password header ⇒ deniable,
|
|
111
|
+
* absent ⇒ standard server key). Omit ⇒ the private-upload route is disabled. */
|
|
112
|
+
privateUpload?: {
|
|
113
|
+
unlock: UnlockService;
|
|
114
|
+
authorize: (c: Context) => Promise<{ userId: string; userSalt: string } | null>;
|
|
115
|
+
/** Header carrying the plaintext password for a deniable set (default 'x-private-password'). */
|
|
116
|
+
passwordHeader?: string;
|
|
117
|
+
/** Header carrying an UNLOCK TOKEN (an already-unlocked session) — takes precedence over the
|
|
118
|
+
* password header; setKey + keyMode come from the verified grant (default 'x-unlock-token'). */
|
|
119
|
+
tokenHeader?: string;
|
|
120
|
+
/** What a supplied password means (default derive — deniable set). `gate` verifies it
|
|
121
|
+
* against the app's accounts and wraps under the recoverable server key instead. */
|
|
122
|
+
passwordPolicy?: UnlockPasswordPolicy;
|
|
123
|
+
};
|
|
124
|
+
/** Wire large-file chunked uploads (direct-to-backend). Omit ⇒ init/complete routes are absent
|
|
125
|
+
* and clients use the streamed `PUT /upload`. Uses the same `authorizeUpload` guard. */
|
|
126
|
+
chunked?: ChunkedUploadService;
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
export function mountFileRoutes(app: Hono, opts: MountFileRoutesOptions): void {
|
|
130
|
+
const base = opts.basePath ?? '/_files';
|
|
131
|
+
const { store, token, catalogue } = opts;
|
|
132
|
+
|
|
133
|
+
// GET a file's primary binary (Range-capable). Prefers a backend redirect (binary-server edge).
|
|
134
|
+
app.get(`${base}/s/:token/:id`, async (c) => {
|
|
135
|
+
const grant = await token.verify(c.req.param('token'));
|
|
136
|
+
if (!grant || grant.videoId !== c.req.param('id')) return c.body(null, 401);
|
|
137
|
+
const ct = c.req.query('ct');
|
|
138
|
+
const mime = ct && SAFE_MIME.test(ct) ? ct : 'application/octet-stream';
|
|
139
|
+
const key = fileBinaryKey(grant.owner, grant.videoId);
|
|
140
|
+
if (store.redirectUrl) {
|
|
141
|
+
const url = await store.redirectUrl(key, { mime });
|
|
142
|
+
if (url) return c.redirect(url, 303);
|
|
143
|
+
}
|
|
144
|
+
return serveRange(store, key, mime, c.req.header('range'), { contentDisposition: 'inline' });
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
// PUT a regular (public) upload → catalogue.ingest (any file type). Streamed, not buffered.
|
|
148
|
+
if (opts.authorizeUpload && catalogue) {
|
|
149
|
+
app.put(`${base}/upload`, async (c) => {
|
|
150
|
+
const ctx = await opts.authorizeUpload?.(c);
|
|
151
|
+
if (!ctx) return c.json({ error: 'unauthorized' }, 401);
|
|
152
|
+
const raw = c.req.raw.body;
|
|
153
|
+
if (!raw) return c.json({ error: 'empty body' }, 400);
|
|
154
|
+
const body = guardSingleUpload(c, raw);
|
|
155
|
+
if (body instanceof Response) return body;
|
|
156
|
+
const tags = c.req.header('x-tags');
|
|
157
|
+
try {
|
|
158
|
+
const file = await catalogue.ingest(ctx, {
|
|
159
|
+
name: c.req.header('x-filename') ?? 'upload',
|
|
160
|
+
mime: c.req.header('content-type') ?? 'application/octet-stream',
|
|
161
|
+
body,
|
|
162
|
+
tags: tags
|
|
163
|
+
? tags
|
|
164
|
+
.split(',')
|
|
165
|
+
.map((t) => t.trim())
|
|
166
|
+
.filter(Boolean)
|
|
167
|
+
: undefined,
|
|
168
|
+
});
|
|
169
|
+
return c.json({ file });
|
|
170
|
+
} catch (e) {
|
|
171
|
+
if (isUploadTooLarge(e)) return c.json({ error: SINGLE_UPLOAD_TOO_LARGE }, 413);
|
|
172
|
+
throw e;
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
// PUT new bytes OVER an existing file (the editor's "overwrite original"). Same id — tags,
|
|
177
|
+
// collections, favorites and view history survive; derivatives wipe + re-derive. Streamed.
|
|
178
|
+
app.put(`${base}/replace/:id`, async (c) => {
|
|
179
|
+
const ctx = await opts.authorizeUpload?.(c);
|
|
180
|
+
if (!ctx) return c.json({ error: 'unauthorized' }, 401);
|
|
181
|
+
const raw = c.req.raw.body;
|
|
182
|
+
if (!raw) return c.json({ error: 'empty body' }, 400);
|
|
183
|
+
const body = guardSingleUpload(c, raw);
|
|
184
|
+
if (body instanceof Response) return body;
|
|
185
|
+
try {
|
|
186
|
+
const file = await catalogue.replaceContent(ctx, c.req.param('id'), {
|
|
187
|
+
name: c.req.header('x-filename') || undefined,
|
|
188
|
+
mime: c.req.header('content-type') ?? 'application/octet-stream',
|
|
189
|
+
body,
|
|
190
|
+
});
|
|
191
|
+
if (!file) return c.json({ error: 'not found' }, 404);
|
|
192
|
+
return c.json({ file });
|
|
193
|
+
} catch (e) {
|
|
194
|
+
if (isUploadTooLarge(e)) return c.json({ error: SINGLE_UPLOAD_TOO_LARGE }, 413);
|
|
195
|
+
return c.json({ error: e instanceof Error ? e.message : 'invalid' }, 400);
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// PUT a PRIVATE upload → catalogue.ingestPrivate under a set key derived from the request:
|
|
201
|
+
// either an UNLOCK TOKEN (an already-unlocked session — same mechanism the content-key route
|
|
202
|
+
// uses) or a password header (deniable) / neither (standard server key).
|
|
203
|
+
const pv = opts.privateUpload;
|
|
204
|
+
if (pv && catalogue) {
|
|
205
|
+
const passwordHeader = pv.passwordHeader ?? 'x-private-password';
|
|
206
|
+
const tokenHeader = pv.tokenHeader ?? 'x-unlock-token';
|
|
207
|
+
app.put(`${base}/upload-private`, async (c) => {
|
|
208
|
+
const who = await pv.authorize(c);
|
|
209
|
+
if (!who) return c.json({ error: 'unauthorized' }, 401);
|
|
210
|
+
const raw = c.req.raw.body;
|
|
211
|
+
if (!raw) return c.json({ error: 'empty body' }, 400);
|
|
212
|
+
const body = guardSingleUpload(c, raw);
|
|
213
|
+
if (body instanceof Response) return body;
|
|
214
|
+
|
|
215
|
+
let setKey: Buffer;
|
|
216
|
+
let keyMode: 'standard' | 'deniable';
|
|
217
|
+
const unlockToken = c.req.header(tokenHeader) || undefined;
|
|
218
|
+
if (unlockToken) {
|
|
219
|
+
// Token path: recover the sealed set key + mode from the grant. The token must belong
|
|
220
|
+
// to the session caller.
|
|
221
|
+
const grant = await pv.unlock.verify(unlockToken);
|
|
222
|
+
if (!grant || grant.userId !== who.userId) {
|
|
223
|
+
return c.json({ error: 'invalid unlock token' }, 401);
|
|
224
|
+
}
|
|
225
|
+
setKey = grant.setKey;
|
|
226
|
+
keyMode = grant.mode;
|
|
227
|
+
} else {
|
|
228
|
+
// Password path: derive fresh from the header (deniable) or the server key. Under the
|
|
229
|
+
// gate policy the password is an access check only — verify, then wrap standard.
|
|
230
|
+
const password = c.req.header(passwordHeader) || undefined;
|
|
231
|
+
const policy = pv.passwordPolicy ?? { mode: 'derive' as const };
|
|
232
|
+
let derivePassword = password;
|
|
233
|
+
if (policy.mode === 'gate' && password) {
|
|
234
|
+
if (!(await policy.verifyPassword(who, password))) {
|
|
235
|
+
return c.json({ error: 'invalid password' }, 401);
|
|
236
|
+
}
|
|
237
|
+
derivePassword = undefined;
|
|
238
|
+
}
|
|
239
|
+
setKey = pv.unlock.deriveSetKey({
|
|
240
|
+
userId: who.userId,
|
|
241
|
+
userSalt: who.userSalt,
|
|
242
|
+
password: derivePassword,
|
|
243
|
+
});
|
|
244
|
+
keyMode = derivePassword ? 'deniable' : 'standard';
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// The private owner axis is the userId (deniability is personal), matching mountPrivateRoutes.
|
|
248
|
+
const ctx: MediaCtx = { userId: who.userId, tenantId: who.userId };
|
|
249
|
+
const tags = c.req.header('x-tags');
|
|
250
|
+
try {
|
|
251
|
+
const item = await catalogue.ingestPrivate(
|
|
252
|
+
ctx,
|
|
253
|
+
{
|
|
254
|
+
name: c.req.header('x-filename') ?? 'upload',
|
|
255
|
+
mime: c.req.header('content-type') ?? 'application/octet-stream',
|
|
256
|
+
body,
|
|
257
|
+
tags: tags
|
|
258
|
+
? tags
|
|
259
|
+
.split(',')
|
|
260
|
+
.map((t) => t.trim())
|
|
261
|
+
.filter(Boolean)
|
|
262
|
+
: undefined,
|
|
263
|
+
},
|
|
264
|
+
{ setKey, keyMode },
|
|
265
|
+
);
|
|
266
|
+
return c.json({ item });
|
|
267
|
+
} catch (e) {
|
|
268
|
+
if (isUploadTooLarge(e)) return c.json({ error: SINGLE_UPLOAD_TOO_LARGE }, 413);
|
|
269
|
+
throw e;
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Chunked upload (large files, direct-to-backend). init → the client PUTs each chunk straight to
|
|
275
|
+
// the backend (URLs the app minted) → complete registers the row over the assembled bytes. The
|
|
276
|
+
// bytes never traverse this origin.
|
|
277
|
+
const chunked = opts.chunked;
|
|
278
|
+
if (chunked && opts.authorizeUpload) {
|
|
279
|
+
app.post(`${base}/upload-init`, async (c) => {
|
|
280
|
+
const ctx = await opts.authorizeUpload?.(c);
|
|
281
|
+
if (!ctx) return c.json({ error: 'unauthorized' }, 401);
|
|
282
|
+
const body = (await c.req.json().catch(() => ({}))) as {
|
|
283
|
+
name?: string;
|
|
284
|
+
mime?: string;
|
|
285
|
+
size?: number;
|
|
286
|
+
tags?: string[];
|
|
287
|
+
};
|
|
288
|
+
if (!body.size || body.size <= 0) return c.json({ error: 'size required' }, 400);
|
|
289
|
+
const init = await chunked.init(ctx, {
|
|
290
|
+
name: body.name ?? 'upload',
|
|
291
|
+
mime: body.mime ?? 'application/octet-stream',
|
|
292
|
+
size: body.size,
|
|
293
|
+
tags: body.tags,
|
|
294
|
+
});
|
|
295
|
+
return c.json(init);
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
app.post(`${base}/upload-complete`, async (c) => {
|
|
299
|
+
const ctx = await opts.authorizeUpload?.(c);
|
|
300
|
+
if (!ctx) return c.json({ error: 'unauthorized' }, 401);
|
|
301
|
+
const body = (await c.req.json().catch(() => ({}))) as { sid?: string; size?: number };
|
|
302
|
+
if (!body.sid) return c.json({ error: 'sid required' }, 400);
|
|
303
|
+
const file = await chunked.complete(body.sid, { size: body.size });
|
|
304
|
+
if (!file) return c.json({ error: 'unknown session' }, 404);
|
|
305
|
+
return c.json({ file });
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
app.post(`${base}/upload-abort`, async (c) => {
|
|
309
|
+
const ctx = await opts.authorizeUpload?.(c);
|
|
310
|
+
if (!ctx) return c.json({ error: 'unauthorized' }, 401);
|
|
311
|
+
const body = (await c.req.json().catch(() => ({}))) as { sid?: string };
|
|
312
|
+
if (body.sid) chunked.abort(body.sid);
|
|
313
|
+
return c.json({ ok: true });
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// ── Listing + collection routes ───────────────────────────────────────────────
|
|
318
|
+
// All require authorizeUpload (session auth) and a catalogue. They follow the same
|
|
319
|
+
// auth/ctx/envelope pattern as the upload routes above. The `includeUnlockable` gate is the
|
|
320
|
+
// caller's responsibility — the route passes the query param through, and it's the app's session
|
|
321
|
+
// middleware that decides whether the caller has unlocked.
|
|
322
|
+
if (opts.authorizeUpload && catalogue) {
|
|
323
|
+
// GET /_files/list → cursor-paginated catalogue listing. Query params mirror
|
|
324
|
+
// ListFilesOptions: type, tag, query (q), collectionId, favorite, includeUnlockable,
|
|
325
|
+
// sort (recent|oldest|name|size|random|lastViewed), cursor, limit.
|
|
326
|
+
app.get(`${base}/list`, async (c) => {
|
|
327
|
+
const ctx = await opts.authorizeUpload?.(c);
|
|
328
|
+
if (!ctx) return c.json({ error: 'unauthorized' }, 401);
|
|
329
|
+
const q = c.req.query();
|
|
330
|
+
const sort = (FILE_SORTS as readonly string[]).includes(q.sort ?? '')
|
|
331
|
+
? (q.sort as ListFilesOptions['sort'])
|
|
332
|
+
: undefined;
|
|
333
|
+
const limit = q.limit ? Number(q.limit) : undefined;
|
|
334
|
+
const page = await catalogue.list(ctx, {
|
|
335
|
+
type: q.type as ListFilesOptions['type'],
|
|
336
|
+
tag: q.tag || undefined,
|
|
337
|
+
query: q.q || undefined,
|
|
338
|
+
collectionId: q.collectionId || undefined,
|
|
339
|
+
favorite: q.favorite === '1' ? true : undefined,
|
|
340
|
+
includeUnlockable: q.includeUnlockable === '1' ? true : undefined,
|
|
341
|
+
sort,
|
|
342
|
+
cursor: q.cursor || undefined,
|
|
343
|
+
limit: limit && Number.isFinite(limit) ? limit : undefined,
|
|
344
|
+
});
|
|
345
|
+
return c.json(page);
|
|
346
|
+
});
|
|
347
|
+
// GET /_files/collections → list all visible collections
|
|
348
|
+
app.get(`${base}/collections`, async (c) => {
|
|
349
|
+
const ctx = await opts.authorizeUpload?.(c);
|
|
350
|
+
if (!ctx) return c.json({ error: 'unauthorized' }, 401);
|
|
351
|
+
const includeUnlockable = c.req.query('includeUnlockable') === '1';
|
|
352
|
+
const collections = await catalogue.listCollections(ctx, { includeUnlockable });
|
|
353
|
+
return c.json({ collections });
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
// POST /_files/collections → create a collection
|
|
357
|
+
app.post(`${base}/collections`, async (c) => {
|
|
358
|
+
const ctx = await opts.authorizeUpload?.(c);
|
|
359
|
+
if (!ctx) return c.json({ error: 'unauthorized' }, 401);
|
|
360
|
+
const body = (await c.req.json().catch(() => ({}))) as {
|
|
361
|
+
name?: string;
|
|
362
|
+
visibility?: string;
|
|
363
|
+
};
|
|
364
|
+
if (!body.name) return c.json({ error: 'name required' }, 400);
|
|
365
|
+
try {
|
|
366
|
+
const collection = await catalogue.createCollection(ctx, {
|
|
367
|
+
name: body.name,
|
|
368
|
+
visibility:
|
|
369
|
+
body.visibility === 'unlock' || body.visibility === 'normal'
|
|
370
|
+
? body.visibility
|
|
371
|
+
: undefined,
|
|
372
|
+
});
|
|
373
|
+
return c.json({ collection }, 201);
|
|
374
|
+
} catch (e) {
|
|
375
|
+
return c.json({ error: e instanceof Error ? e.message : 'invalid' }, 400);
|
|
376
|
+
}
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
// PATCH /_files/collections/:id → rename / change visibility
|
|
380
|
+
app.patch(`${base}/collections/:id`, async (c) => {
|
|
381
|
+
const ctx = await opts.authorizeUpload?.(c);
|
|
382
|
+
if (!ctx) return c.json({ error: 'unauthorized' }, 401);
|
|
383
|
+
const body = (await c.req.json().catch(() => ({}))) as {
|
|
384
|
+
name?: string;
|
|
385
|
+
visibility?: string;
|
|
386
|
+
};
|
|
387
|
+
try {
|
|
388
|
+
const collection = await catalogue.updateCollection(ctx, c.req.param('id'), {
|
|
389
|
+
name: body.name,
|
|
390
|
+
visibility:
|
|
391
|
+
body.visibility === 'unlock' || body.visibility === 'normal'
|
|
392
|
+
? body.visibility
|
|
393
|
+
: undefined,
|
|
394
|
+
});
|
|
395
|
+
if (!collection) return c.json({ error: 'not found' }, 404);
|
|
396
|
+
return c.json({ collection });
|
|
397
|
+
} catch (e) {
|
|
398
|
+
return c.json({ error: e instanceof Error ? e.message : 'invalid' }, 400);
|
|
399
|
+
}
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
// DELETE /_files/collections/:id → remove collection (not files)
|
|
403
|
+
app.delete(`${base}/collections/:id`, async (c) => {
|
|
404
|
+
const ctx = await opts.authorizeUpload?.(c);
|
|
405
|
+
if (!ctx) return c.json({ error: 'unauthorized' }, 401);
|
|
406
|
+
try {
|
|
407
|
+
await catalogue.removeCollection(ctx, c.req.param('id'));
|
|
408
|
+
return c.json({ ok: true });
|
|
409
|
+
} catch (e) {
|
|
410
|
+
return c.json({ error: e instanceof Error ? e.message : 'invalid' }, 400);
|
|
411
|
+
}
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
// POST /_files/collections/:id/members → add files to a collection
|
|
415
|
+
app.post(`${base}/collections/:id/members`, async (c) => {
|
|
416
|
+
const ctx = await opts.authorizeUpload?.(c);
|
|
417
|
+
if (!ctx) return c.json({ error: 'unauthorized' }, 401);
|
|
418
|
+
const body = (await c.req.json().catch(() => ({}))) as { fileIds?: string[] };
|
|
419
|
+
if (!Array.isArray(body.fileIds) || body.fileIds.length === 0) {
|
|
420
|
+
return c.json({ error: 'fileIds required' }, 400);
|
|
421
|
+
}
|
|
422
|
+
await catalogue.addToCollection(ctx, c.req.param('id'), body.fileIds);
|
|
423
|
+
return c.json({ ok: true });
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
// DELETE /_files/collections/:id/members → remove files from a collection
|
|
427
|
+
app.delete(`${base}/collections/:id/members`, async (c) => {
|
|
428
|
+
const ctx = await opts.authorizeUpload?.(c);
|
|
429
|
+
if (!ctx) return c.json({ error: 'unauthorized' }, 401);
|
|
430
|
+
const body = (await c.req.json().catch(() => ({}))) as { fileIds?: string[] };
|
|
431
|
+
if (!Array.isArray(body.fileIds) || body.fileIds.length === 0) {
|
|
432
|
+
return c.json({ error: 'fileIds required' }, 400);
|
|
433
|
+
}
|
|
434
|
+
await catalogue.removeFromCollection(ctx, c.req.param('id'), body.fileIds);
|
|
435
|
+
return c.json({ ok: true });
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
// POST /_files/favorites → toggle favorite state for a single file
|
|
439
|
+
app.post(`${base}/favorites`, async (c) => {
|
|
440
|
+
const ctx = await opts.authorizeUpload?.(c);
|
|
441
|
+
if (!ctx) return c.json({ error: 'unauthorized' }, 401);
|
|
442
|
+
const body = (await c.req.json().catch(() => ({}))) as {
|
|
443
|
+
fileId?: string;
|
|
444
|
+
favorite?: boolean;
|
|
445
|
+
};
|
|
446
|
+
if (!body.fileId) return c.json({ error: 'fileId required' }, 400);
|
|
447
|
+
await catalogue.setFavorite(ctx, body.fileId, body.favorite ?? true);
|
|
448
|
+
return c.json({ ok: true });
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
// POST /_files/view → record a watch-open of a file (feeds the `lastViewed` sort). Idempotent
|
|
452
|
+
// in effect (upsert); an unknown/other-owner id records nothing but still returns ok.
|
|
453
|
+
app.post(`${base}/view`, async (c) => {
|
|
454
|
+
const ctx = await opts.authorizeUpload?.(c);
|
|
455
|
+
if (!ctx) return c.json({ error: 'unauthorized' }, 401);
|
|
456
|
+
const body = (await c.req.json().catch(() => ({}))) as { fileId?: string };
|
|
457
|
+
if (!body.fileId) return c.json({ error: 'fileId required' }, 400);
|
|
458
|
+
await catalogue.recordView(ctx, body.fileId);
|
|
459
|
+
return c.json({ ok: true });
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// cursedbelt-server/storage FileCatalogue — a SQLite-backed tagged flat file system over the
|
|
2
|
+
// MediaStore + media pipeline + private-media. One source of truth for a user's binaries; the
|
|
3
|
+
// video gallery is a video-filtered view over it.
|
|
4
|
+
export {
|
|
5
|
+
createCatalogueMediaSource,
|
|
6
|
+
createCataloguePhotoSource,
|
|
7
|
+
createCatalogueVideoSource,
|
|
8
|
+
createFileToPlayable,
|
|
9
|
+
} from './adapter';
|
|
10
|
+
export {
|
|
11
|
+
catalogueUploadKey,
|
|
12
|
+
createSqliteFileCatalogue,
|
|
13
|
+
fileBinaryKey,
|
|
14
|
+
type ImageDerivatives,
|
|
15
|
+
type ImageDerivativesOptions,
|
|
16
|
+
type SqliteFileCatalogueOptions,
|
|
17
|
+
type VideoProcessor,
|
|
18
|
+
} from './catalogue';
|
|
19
|
+
export type { CatalogueStats } from './types';
|
|
20
|
+
export {
|
|
21
|
+
type ChunkedInit,
|
|
22
|
+
type ChunkedUploadService,
|
|
23
|
+
createChunkedUploadService,
|
|
24
|
+
type MintChunkUpload,
|
|
25
|
+
} from './chunkedUpload';
|
|
26
|
+
export {
|
|
27
|
+
createFileUrls,
|
|
28
|
+
type FileUrls,
|
|
29
|
+
type MountFileRoutesOptions,
|
|
30
|
+
mountFileRoutes,
|
|
31
|
+
SINGLE_UPLOAD_TOO_LARGE,
|
|
32
|
+
} from './fileRoutes';
|
|
33
|
+
export { FILE_CATALOGUE_MIGRATIONS } from './migrations';
|
|
34
|
+
export {
|
|
35
|
+
createFilesStack,
|
|
36
|
+
type FilesStack,
|
|
37
|
+
type FilesStackMountAuth,
|
|
38
|
+
type FilesStackOptions,
|
|
39
|
+
} from './stack';
|
|
40
|
+
export {
|
|
41
|
+
createLedgeredDeletes,
|
|
42
|
+
createStorageOpsLedger,
|
|
43
|
+
createStorageReconciler,
|
|
44
|
+
type StorageAction,
|
|
45
|
+
type StorageOpRow,
|
|
46
|
+
type StorageOpsLedger,
|
|
47
|
+
type StorageReconciler,
|
|
48
|
+
} from './storageOps';
|
|
49
|
+
export {
|
|
50
|
+
type CollectionKind,
|
|
51
|
+
type CollectionVisibility,
|
|
52
|
+
classifyFileType,
|
|
53
|
+
FILE_SORTS,
|
|
54
|
+
type FileCatalogue,
|
|
55
|
+
type FileCollection,
|
|
56
|
+
type FilePage,
|
|
57
|
+
type FileRecord,
|
|
58
|
+
type FileSort,
|
|
59
|
+
type FileToPlayable,
|
|
60
|
+
type FileType,
|
|
61
|
+
type ListFilesOptions,
|
|
62
|
+
type PrivateFileCatalogue,
|
|
63
|
+
type PrivateFileItem,
|
|
64
|
+
type PrivateKeyMode,
|
|
65
|
+
type TagFacet,
|
|
66
|
+
} from './types';
|