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,187 @@
|
|
|
1
|
+
import type { Database } from 'bun:sqlite';
|
|
2
|
+
import type { Migration } from '../../migration/types';
|
|
3
|
+
|
|
4
|
+
// Schema for the FileCatalogue. Runs through cursedbelt's migration runner (idempotent, recorded
|
|
5
|
+
// in `_migrations`). Improves on ca's fs_nodes: the smart-view `file_type` is stored + indexed
|
|
6
|
+
// (not re-derived per list), tags live in an indexed junction (fast facets, not a JSON scan),
|
|
7
|
+
// processing state is typed columns (not an opaque blob), and a storage-operations ledger backs
|
|
8
|
+
// durable cross-boundary (binary-server/S3) ops. Flat — no parent_id tree.
|
|
9
|
+
|
|
10
|
+
export const FILE_CATALOGUE_MIGRATIONS: readonly Migration[] = [
|
|
11
|
+
{
|
|
12
|
+
name: '0001_file_catalogue',
|
|
13
|
+
up(db: Database): void {
|
|
14
|
+
db.run(`
|
|
15
|
+
CREATE TABLE IF NOT EXISTS files (
|
|
16
|
+
id TEXT PRIMARY KEY,
|
|
17
|
+
owner TEXT NOT NULL,
|
|
18
|
+
name TEXT NOT NULL DEFAULT '',
|
|
19
|
+
mime TEXT NOT NULL DEFAULT '',
|
|
20
|
+
size INTEGER NOT NULL DEFAULT 0,
|
|
21
|
+
file_type TEXT NOT NULL DEFAULT 'other',
|
|
22
|
+
is_private INTEGER NOT NULL DEFAULT 0,
|
|
23
|
+
has_thumbnail INTEGER NOT NULL DEFAULT 0,
|
|
24
|
+
has_hls INTEGER NOT NULL DEFAULT 0,
|
|
25
|
+
has_sprites INTEGER NOT NULL DEFAULT 0,
|
|
26
|
+
duration REAL,
|
|
27
|
+
processing_status TEXT NOT NULL DEFAULT 'ready',
|
|
28
|
+
processing_progress INTEGER NOT NULL DEFAULT 100,
|
|
29
|
+
storage_backend TEXT NOT NULL DEFAULT 'disk',
|
|
30
|
+
enc_name TEXT NOT NULL DEFAULT '',
|
|
31
|
+
enc_dek TEXT NOT NULL DEFAULT '',
|
|
32
|
+
enc_key_type TEXT NOT NULL DEFAULT '',
|
|
33
|
+
created_at INTEGER NOT NULL,
|
|
34
|
+
updated_at INTEGER NOT NULL
|
|
35
|
+
)
|
|
36
|
+
`);
|
|
37
|
+
// Listing is cursor-keyed on (created_at, id); these cover the flat feed + smart-view filter.
|
|
38
|
+
db.run(
|
|
39
|
+
`CREATE INDEX IF NOT EXISTS idx_files_owner_created ON files(owner, created_at DESC, id DESC)`,
|
|
40
|
+
);
|
|
41
|
+
db.run(
|
|
42
|
+
`CREATE INDEX IF NOT EXISTS idx_files_owner_type_created ON files(owner, file_type, created_at DESC, id DESC)`,
|
|
43
|
+
);
|
|
44
|
+
db.run(`CREATE INDEX IF NOT EXISTS idx_files_owner_private ON files(owner, is_private)`);
|
|
45
|
+
|
|
46
|
+
// Tags as a junction (indexed) — single-tag filter + facet counts are index lookups, not a
|
|
47
|
+
// full-table JSON parse (ca's weakness). `owner` denormalized so facet queries stay covering.
|
|
48
|
+
db.run(`
|
|
49
|
+
CREATE TABLE IF NOT EXISTS file_tags (
|
|
50
|
+
file_id TEXT NOT NULL REFERENCES files(id) ON DELETE CASCADE,
|
|
51
|
+
tag TEXT NOT NULL,
|
|
52
|
+
owner TEXT NOT NULL,
|
|
53
|
+
is_private INTEGER NOT NULL DEFAULT 0,
|
|
54
|
+
PRIMARY KEY (file_id, tag)
|
|
55
|
+
)
|
|
56
|
+
`);
|
|
57
|
+
db.run(`CREATE INDEX IF NOT EXISTS idx_file_tags_owner_tag ON file_tags(owner, tag)`);
|
|
58
|
+
|
|
59
|
+
// Durable ledger for cross-boundary storage ops (binary-server/S3 put/delete) — the
|
|
60
|
+
// reconciler heals `pending` rows the server never got an ack for.
|
|
61
|
+
db.run(`
|
|
62
|
+
CREATE TABLE IF NOT EXISTS storage_operations (
|
|
63
|
+
op_id TEXT PRIMARY KEY,
|
|
64
|
+
owner TEXT NOT NULL,
|
|
65
|
+
file_id TEXT,
|
|
66
|
+
action TEXT NOT NULL,
|
|
67
|
+
key TEXT NOT NULL,
|
|
68
|
+
backend TEXT NOT NULL,
|
|
69
|
+
status TEXT NOT NULL DEFAULT 'pending',
|
|
70
|
+
attempts INTEGER NOT NULL DEFAULT 0,
|
|
71
|
+
error TEXT,
|
|
72
|
+
created_at INTEGER NOT NULL,
|
|
73
|
+
updated_at INTEGER NOT NULL
|
|
74
|
+
)
|
|
75
|
+
`);
|
|
76
|
+
db.run(
|
|
77
|
+
`CREATE INDEX IF NOT EXISTS idx_storage_ops_status ON storage_operations(status, created_at)`,
|
|
78
|
+
);
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
name: '0002_file_collections',
|
|
83
|
+
up(db: Database): void {
|
|
84
|
+
// Collections (Apple-Photos-style groupings). kind='favorites' is a system singleton per owner;
|
|
85
|
+
// kind='custom' are user-defined. visibility='unlock' hides the collection unless explicitly
|
|
86
|
+
// requested (like a private shelf). Membership is in file_collection_files; files are never
|
|
87
|
+
// deleted by removing a collection.
|
|
88
|
+
db.run(`
|
|
89
|
+
CREATE TABLE IF NOT EXISTS file_collections (
|
|
90
|
+
id TEXT PRIMARY KEY,
|
|
91
|
+
owner TEXT NOT NULL,
|
|
92
|
+
name TEXT NOT NULL,
|
|
93
|
+
kind TEXT NOT NULL DEFAULT 'custom',
|
|
94
|
+
visibility TEXT NOT NULL DEFAULT 'normal',
|
|
95
|
+
created_at INTEGER NOT NULL,
|
|
96
|
+
updated_at INTEGER NOT NULL
|
|
97
|
+
)
|
|
98
|
+
`);
|
|
99
|
+
db.run(
|
|
100
|
+
`CREATE INDEX IF NOT EXISTS idx_file_collections_owner ON file_collections(owner, name)`,
|
|
101
|
+
);
|
|
102
|
+
// Unique partial index: at most one 'favorites' collection per owner.
|
|
103
|
+
db.run(
|
|
104
|
+
`CREATE UNIQUE INDEX IF NOT EXISTS idx_file_collections_owner_favorites
|
|
105
|
+
ON file_collections(owner) WHERE kind = 'favorites'`,
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
// Junction: which files belong to which collection. owner is denormalized so membership
|
|
109
|
+
// queries can stay covering without a join back to files. Foreign-key cascades are declared
|
|
110
|
+
// but SQLite's FK pragma may be off at runtime, so catalogue.remove() explicitly cleans
|
|
111
|
+
// file_collection_files rows (matching the file_tags pattern).
|
|
112
|
+
db.run(`
|
|
113
|
+
CREATE TABLE IF NOT EXISTS file_collection_files (
|
|
114
|
+
collection_id TEXT NOT NULL REFERENCES file_collections(id) ON DELETE CASCADE,
|
|
115
|
+
file_id TEXT NOT NULL REFERENCES files(id) ON DELETE CASCADE,
|
|
116
|
+
owner TEXT NOT NULL,
|
|
117
|
+
added_at INTEGER NOT NULL,
|
|
118
|
+
PRIMARY KEY (collection_id, file_id)
|
|
119
|
+
)
|
|
120
|
+
`);
|
|
121
|
+
db.run(
|
|
122
|
+
`CREATE INDEX IF NOT EXISTS idx_fcf_owner_file ON file_collection_files(owner, file_id)`,
|
|
123
|
+
);
|
|
124
|
+
db.run(
|
|
125
|
+
`CREATE INDEX IF NOT EXISTS idx_fcf_collection ON file_collection_files(collection_id, added_at DESC)`,
|
|
126
|
+
);
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
name: '0003_file_sort_indexes',
|
|
131
|
+
up(db: Database): void {
|
|
132
|
+
// Keyset pagination under the `name` / `size` sorts stays an index walk, mirroring
|
|
133
|
+
// idx_files_owner_created for the default `recent` sort.
|
|
134
|
+
db.run(`CREATE INDEX IF NOT EXISTS idx_files_owner_name ON files(owner, name ASC, id ASC)`);
|
|
135
|
+
db.run(`CREATE INDEX IF NOT EXISTS idx_files_owner_size ON files(owner, size DESC, id DESC)`);
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
name: '0004_file_random_and_views',
|
|
140
|
+
up(db: Database): void {
|
|
141
|
+
// Per-row stable random key backing the seeded `random` gallery order. A session mints a
|
|
142
|
+
// seed (folded into the cursor); the sort key is `rand_key` remapped by that seed, so the
|
|
143
|
+
// order is a stable total order for keyset pagination while each visit reshuffles. ADD COLUMN
|
|
144
|
+
// can't take a non-constant DEFAULT, so seed existing rows here; new rows set it at insert.
|
|
145
|
+
db.run(`ALTER TABLE files ADD COLUMN rand_key INTEGER NOT NULL DEFAULT 0`);
|
|
146
|
+
// random() is a 64-bit int; mask to a non-negative 31-bit value so the seeded remap
|
|
147
|
+
// (rand_key * mul + add) stays well inside SQLite's signed-64-bit integer range.
|
|
148
|
+
db.run(`UPDATE files SET rand_key = (random() & 2147483647)`);
|
|
149
|
+
|
|
150
|
+
// View-event ledger: last-viewed timestamp + count per file, powering the `lastViewed` sort.
|
|
151
|
+
// One row per file (upserted on each open). owner is denormalized so the sort's join stays
|
|
152
|
+
// owner-scoped. FK cascade is declared but the pragma may be off, so catalogue.remove() also
|
|
153
|
+
// cleans these rows explicitly (matching the file_tags / file_collection_files pattern).
|
|
154
|
+
db.run(`
|
|
155
|
+
CREATE TABLE IF NOT EXISTS file_views (
|
|
156
|
+
file_id TEXT PRIMARY KEY REFERENCES files(id) ON DELETE CASCADE,
|
|
157
|
+
owner TEXT NOT NULL,
|
|
158
|
+
viewed_at INTEGER NOT NULL,
|
|
159
|
+
view_count INTEGER NOT NULL DEFAULT 0
|
|
160
|
+
)
|
|
161
|
+
`);
|
|
162
|
+
db.run(
|
|
163
|
+
`CREATE INDEX IF NOT EXISTS idx_file_views_owner_viewed ON file_views(owner, viewed_at DESC)`,
|
|
164
|
+
);
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
name: '0005_processing_attempts',
|
|
169
|
+
up(db: Database): void {
|
|
170
|
+
// Counts transcode kicks per file so recoverProcessing can stop re-kicking a job that
|
|
171
|
+
// keeps dying (e.g. ffmpeg OOM-killed on a small host → service restart → re-kick loop).
|
|
172
|
+
db.run(`ALTER TABLE files ADD COLUMN processing_attempts INTEGER NOT NULL DEFAULT 0`);
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
name: '0006_file_trash',
|
|
177
|
+
up(db: Database): void {
|
|
178
|
+
// Trash-first delete: `trash()` sets trashed_at instead of dropping the row, so a mistaken
|
|
179
|
+
// delete is recoverable via `restore()`. `remove()` stays the PERMANENT hard-delete — now a
|
|
180
|
+
// deliberate second action (typically only invoked on an already-trashed row) rather than the
|
|
181
|
+
// primary delete path. `list()` excludes trashed rows by default (NULL check stays an index
|
|
182
|
+
// hit); pass `trashedOnly` to browse the trash bin itself.
|
|
183
|
+
db.run(`ALTER TABLE files ADD COLUMN trashed_at INTEGER`);
|
|
184
|
+
db.run(`CREATE INDEX IF NOT EXISTS idx_files_owner_trashed ON files(owner, trashed_at)`);
|
|
185
|
+
},
|
|
186
|
+
},
|
|
187
|
+
];
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { mimeForFilename } from 'cwip/string';
|
|
2
|
+
|
|
3
|
+
// Ext → mime for local-disk import (catalogue.importFromPath) — the catalogue needs SOME mime
|
|
4
|
+
// to classify + serve a file that never passed through a browser's upload Content-Type.
|
|
5
|
+
//
|
|
6
|
+
// The table itself moved DOWN to `cwip/string`'s `fileKind` (2026-07-31): the viewer UI and the
|
|
7
|
+
// notes ingest pipeline need the same knowledge, and cwip is the only layer all three can read
|
|
8
|
+
// from. This wrapper stays so existing server call sites keep working; it is now one line, and
|
|
9
|
+
// the lookup it delegates to is a superset of what used to live here (code, data, archive and
|
|
10
|
+
// markup types the old table lacked). Unknown still falls back to 'application/octet-stream',
|
|
11
|
+
// which `classifyFileType` maps to 'other' — importable, just without image/video processing.
|
|
12
|
+
export function mimeByExt(name: string): string {
|
|
13
|
+
return mimeForFilename(name);
|
|
14
|
+
}
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import { Database } from 'bun:sqlite';
|
|
2
|
+
import { afterAll, beforeAll, describe, expect, test } from 'bun:test';
|
|
3
|
+
import { mkdtemp, rm } from 'node:fs/promises';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { Hono } from 'hono';
|
|
7
|
+
import { runMigrations } from '../../migration/runner';
|
|
8
|
+
import { generateUserSalt } from '../../private-media/crypto';
|
|
9
|
+
import { createDiskStore } from '../diskStore';
|
|
10
|
+
import type { MediaCtx } from '../types';
|
|
11
|
+
import { FILE_CATALOGUE_MIGRATIONS } from './migrations';
|
|
12
|
+
import { createFilesStack, type FilesStack } from './stack';
|
|
13
|
+
|
|
14
|
+
const CTX: MediaCtx = { userId: 'u1', tenantId: 't1' };
|
|
15
|
+
const salt = generateUserSalt();
|
|
16
|
+
|
|
17
|
+
// Seed a row recoverProcessing acts on WITHOUT ffmpeg: a private video stuck in `processing`
|
|
18
|
+
// is marked failed synchronously (its set key isn't available at boot).
|
|
19
|
+
function seedStuckPrivateRow(db: Database, id: string): void {
|
|
20
|
+
runMigrations(db, FILE_CATALOGUE_MIGRATIONS); // idempotent — the stack re-runs it harmlessly
|
|
21
|
+
db.run(
|
|
22
|
+
`INSERT INTO files (id, owner, name, mime, size, file_type, is_private, processing_status,
|
|
23
|
+
processing_progress, storage_backend, enc_name, enc_dek, enc_key_type, created_at, updated_at)
|
|
24
|
+
VALUES (?, 'u1', '', '', 1, 'video', 1, 'processing', 40, 'disk', 'x', 'x', 'deniable', 1, 1)`,
|
|
25
|
+
[id],
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const statusOf = (db: Database, id: string): string | undefined =>
|
|
30
|
+
db
|
|
31
|
+
.query<{ processing_status: string }, [string]>(
|
|
32
|
+
'SELECT processing_status FROM files WHERE id = ?',
|
|
33
|
+
)
|
|
34
|
+
.get(id)?.processing_status;
|
|
35
|
+
|
|
36
|
+
describe('createFilesStack — public stack', () => {
|
|
37
|
+
let db: Database;
|
|
38
|
+
let root: string;
|
|
39
|
+
let stack: FilesStack;
|
|
40
|
+
const app = new Hono();
|
|
41
|
+
|
|
42
|
+
beforeAll(async () => {
|
|
43
|
+
db = new Database(':memory:');
|
|
44
|
+
root = await mkdtemp(path.join(tmpdir(), 'stack-'));
|
|
45
|
+
stack = createFilesStack({
|
|
46
|
+
db,
|
|
47
|
+
store: createDiskStore(root),
|
|
48
|
+
workDir: path.join(root, 'work'),
|
|
49
|
+
streamSecret: 'stream-secret',
|
|
50
|
+
});
|
|
51
|
+
stack.mount(app, { authorizeUpload: () => CTX });
|
|
52
|
+
});
|
|
53
|
+
afterAll(async () => {
|
|
54
|
+
db.close();
|
|
55
|
+
await rm(root, { recursive: true, force: true });
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test('migrations ran — catalogue.list works on a fresh db', async () => {
|
|
59
|
+
const page = await stack.catalogue.list(CTX);
|
|
60
|
+
expect(page.items).toEqual([]);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test('no private layer configured ⇒ unlock is null', () => {
|
|
64
|
+
expect(stack.unlock).toBeNull();
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test('URL builders are wired to the default bases', async () => {
|
|
68
|
+
const f = await stack.fileUrls.build(CTX, 'fid', 'image/png');
|
|
69
|
+
expect(f.url).toStartWith('/_files/s/');
|
|
70
|
+
expect(f.url).toContain('ct=image%2Fpng');
|
|
71
|
+
const m = await stack.mediaUrls.build(CTX, 'files', 'vid');
|
|
72
|
+
expect(m.streamUrl).toStartWith('/_media/s/');
|
|
73
|
+
expect(m.hlsUrl).toEndWith('/hls/files/vid/master.m3u8');
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test('videoSource is registered in the registry under the default sourceId', () => {
|
|
77
|
+
expect(stack.videoSource.id).toBe('files');
|
|
78
|
+
expect(stack.videoSource.title).toBe('Files');
|
|
79
|
+
expect(stack.registry.get('files')).toBe(stack.videoSource);
|
|
80
|
+
expect(stack.registry.list()).toEqual([stack.videoSource]);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test('mounted routes: upload → list shows it → binary route serves the bytes', async () => {
|
|
84
|
+
const up = await app.request('/_files/upload', {
|
|
85
|
+
method: 'PUT',
|
|
86
|
+
headers: { 'content-type': 'image/png', 'x-filename': 'pic.png', 'x-tags': 'demo' },
|
|
87
|
+
body: 'png-bytes',
|
|
88
|
+
});
|
|
89
|
+
expect(up.status).toBe(200);
|
|
90
|
+
const { file } = (await up.json()) as { file: { id: string; name: string } };
|
|
91
|
+
expect(file.name).toBe('pic.png');
|
|
92
|
+
|
|
93
|
+
const page = await stack.catalogue.list(CTX);
|
|
94
|
+
expect(page.items.map((f) => f.id)).toEqual([file.id]);
|
|
95
|
+
|
|
96
|
+
const { url } = await stack.fileUrls.build(CTX, file.id, 'image/png');
|
|
97
|
+
const res = await app.request(url);
|
|
98
|
+
expect(res.status).toBe(200);
|
|
99
|
+
expect(res.headers.get('content-type')).toBe('image/png');
|
|
100
|
+
expect(await res.text()).toBe('png-bytes');
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test('upload is rejected when the host auth says no', async () => {
|
|
104
|
+
const denied = new Hono();
|
|
105
|
+
stack.mount(denied, { authorizeUpload: () => null });
|
|
106
|
+
const res = await denied.request('/_files/upload', { method: 'PUT', body: 'x' });
|
|
107
|
+
expect(res.status).toBe(401);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
test('custom sourceId/basePaths flow through', async () => {
|
|
111
|
+
const db2 = new Database(':memory:');
|
|
112
|
+
const s = createFilesStack({
|
|
113
|
+
db: db2,
|
|
114
|
+
store: createDiskStore(root),
|
|
115
|
+
workDir: path.join(root, 'work2'),
|
|
116
|
+
streamSecret: 's',
|
|
117
|
+
sourceId: 'vault',
|
|
118
|
+
sourceTitle: 'Vault',
|
|
119
|
+
mediaBasePath: '/media2',
|
|
120
|
+
filesBasePath: '/files2',
|
|
121
|
+
});
|
|
122
|
+
expect(s.registry.get('vault')?.title).toBe('Vault');
|
|
123
|
+
expect((await s.fileUrls.build(CTX, 'f', 'a/b')).url).toStartWith('/files2/s/');
|
|
124
|
+
expect((await s.mediaUrls.build(CTX, 'vault', 'v')).streamUrl).toStartWith('/media2/s/');
|
|
125
|
+
db2.close();
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
describe('createFilesStack — recoverProcessing', () => {
|
|
130
|
+
let root: string;
|
|
131
|
+
beforeAll(async () => {
|
|
132
|
+
root = await mkdtemp(path.join(tmpdir(), 'stack-recover-'));
|
|
133
|
+
});
|
|
134
|
+
afterAll(async () => {
|
|
135
|
+
await rm(root, { recursive: true, force: true });
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
const build = (db: Database, recoverProcessing?: boolean) =>
|
|
139
|
+
createFilesStack({
|
|
140
|
+
db,
|
|
141
|
+
store: createDiskStore(root),
|
|
142
|
+
workDir: path.join(root, 'work'),
|
|
143
|
+
streamSecret: 's',
|
|
144
|
+
recoverProcessing,
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
test('runs by default — a stuck private row is marked failed at build', () => {
|
|
148
|
+
const db = new Database(':memory:');
|
|
149
|
+
seedStuckPrivateRow(db, 'stuck1');
|
|
150
|
+
build(db);
|
|
151
|
+
expect(statusOf(db, 'stuck1')).toBe('failed');
|
|
152
|
+
db.close();
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
test('recoverProcessing: false skips it', () => {
|
|
156
|
+
const db = new Database(':memory:');
|
|
157
|
+
seedStuckPrivateRow(db, 'stuck2');
|
|
158
|
+
build(db, false);
|
|
159
|
+
expect(statusOf(db, 'stuck2')).toBe('processing');
|
|
160
|
+
db.close();
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
describe('createFilesStack — private stack', () => {
|
|
165
|
+
let db: Database;
|
|
166
|
+
let root: string;
|
|
167
|
+
let stack: FilesStack;
|
|
168
|
+
const app = new Hono();
|
|
169
|
+
|
|
170
|
+
beforeAll(async () => {
|
|
171
|
+
db = new Database(':memory:');
|
|
172
|
+
root = await mkdtemp(path.join(tmpdir(), 'stack-priv-'));
|
|
173
|
+
stack = createFilesStack({
|
|
174
|
+
db,
|
|
175
|
+
store: createDiskStore(root),
|
|
176
|
+
workDir: path.join(root, 'work'),
|
|
177
|
+
streamSecret: 'stream-secret',
|
|
178
|
+
privateMedia: { serverSecret: 'srv', unlockSecret: 'unl' },
|
|
179
|
+
});
|
|
180
|
+
stack.mount(app, {
|
|
181
|
+
authorizeUpload: () => CTX,
|
|
182
|
+
authorizePrivate: async () => ({ userId: CTX.userId, userSalt: salt }),
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
afterAll(async () => {
|
|
186
|
+
db.close();
|
|
187
|
+
await rm(root, { recursive: true, force: true });
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
test('unlock service exists and its tokens verify', async () => {
|
|
191
|
+
expect(stack.unlock).not.toBeNull();
|
|
192
|
+
const { token, mode } = await stack.unlock!.unlock({
|
|
193
|
+
userId: CTX.userId,
|
|
194
|
+
userSalt: salt,
|
|
195
|
+
password: 'pw',
|
|
196
|
+
});
|
|
197
|
+
expect(mode).toBe('deniable');
|
|
198
|
+
expect((await stack.unlock!.verify(token))?.userId).toBe(CTX.userId);
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
test('POST /_private/unlock with a password mints a deniable token', async () => {
|
|
202
|
+
const res = await app.request('/_private/unlock', {
|
|
203
|
+
method: 'POST',
|
|
204
|
+
headers: { 'content-type': 'application/json' },
|
|
205
|
+
body: JSON.stringify({ password: 'hunter2' }),
|
|
206
|
+
});
|
|
207
|
+
expect(res.status).toBe(200);
|
|
208
|
+
const body = (await res.json()) as { token: string; mode: string };
|
|
209
|
+
expect(body.mode).toBe('deniable');
|
|
210
|
+
expect((await stack.unlock!.verify(body.token))?.mode).toBe('deniable');
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
test('reveal route is mounted (password reveal over an empty set)', async () => {
|
|
214
|
+
const res = await app.request('/_private/reveal', {
|
|
215
|
+
method: 'POST',
|
|
216
|
+
headers: { 'content-type': 'application/json' },
|
|
217
|
+
body: JSON.stringify({ password: 'hunter2' }),
|
|
218
|
+
});
|
|
219
|
+
expect(res.status).toBe(200);
|
|
220
|
+
expect((await res.json()) as { items: unknown[] }).toEqual({ items: [] });
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
test('private upload route is mounted and session-guarded', async () => {
|
|
224
|
+
const denied = new Hono();
|
|
225
|
+
stack.mount(denied, {
|
|
226
|
+
authorizeUpload: () => CTX,
|
|
227
|
+
authorizePrivate: async () => null,
|
|
228
|
+
});
|
|
229
|
+
const res = await denied.request('/_files/upload-private', { method: 'PUT', body: 'x' });
|
|
230
|
+
expect(res.status).toBe(401); // mounted (not 404) but rejected by the host auth
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
test('mount() throws without authorizePrivate when privateMedia is configured', () => {
|
|
234
|
+
expect(() => stack.mount(new Hono(), { authorizeUpload: () => CTX })).toThrow(
|
|
235
|
+
/authorizePrivate/,
|
|
236
|
+
);
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
test('privateBasePath flows through to the private mount', async () => {
|
|
240
|
+
const db2 = new Database(':memory:');
|
|
241
|
+
const s = createFilesStack({
|
|
242
|
+
db: db2,
|
|
243
|
+
store: createDiskStore(root),
|
|
244
|
+
workDir: path.join(root, 'work2'),
|
|
245
|
+
streamSecret: 's',
|
|
246
|
+
privateBasePath: '/vault',
|
|
247
|
+
privateMedia: { serverSecret: 'srv', unlockSecret: 'unl' },
|
|
248
|
+
});
|
|
249
|
+
const a = new Hono();
|
|
250
|
+
s.mount(a, {
|
|
251
|
+
authorizeUpload: () => CTX,
|
|
252
|
+
authorizePrivate: async () => ({ userId: CTX.userId, userSalt: salt }),
|
|
253
|
+
});
|
|
254
|
+
const res = await a.request('/vault/unlock', { method: 'POST', body: '{}' });
|
|
255
|
+
expect(res.status).toBe(200);
|
|
256
|
+
db2.close();
|
|
257
|
+
});
|
|
258
|
+
});
|