Never crash boot on plugin dropzone setup; default PLUGINS_DIR in image (#71)
All checks were successful
CD / Build and push images (push) Successful in 2m41s
CI / Lint, typecheck, test (push) Successful in 3m20s
CI / Auth e2e pack (push) Successful in 4m9s
CI / Import/export fidelity gate (push) Successful in 54s
CI / Build container images (push) Has been skipped
CD / Deploy to Test (push) Successful in 8s
CD / Smoke tests against Test (push) Successful in 1m10s
CD / Promote to Int (push) Has been skipped
All checks were successful
CD / Build and push images (push) Successful in 2m41s
CI / Lint, typecheck, test (push) Successful in 3m20s
CI / Auth e2e pack (push) Successful in 4m9s
CI / Import/export fidelity gate (push) Successful in 54s
CI / Build container images (push) Has been skipped
CD / Deploy to Test (push) Successful in 8s
CD / Smoke tests against Test (push) Successful in 1m10s
CD / Promote to Int (push) Has been skipped
The Test stage crash-looped: PluginWatcherService.onModuleInit did `mkdir`
on the default `./data/plugins` (→ /app/data, not writable by the non-root
user) and an unhandled EACCES aborted bootstrap. Two fixes:
- Harden the watcher: its dropzone is an optional convenience over the GUI
upload, so a setup failure now logs a warning and disables drop-to-install
instead of taking down the api.
- Bake writable defaults (UPLOADS_DIR/PLUGINS_DIR=/data/…) into the api image
so it works out of the box even where compose does not set them; compose
still mounts named volumes there for persistence.
Migrations applied cleanly ("No pending migrations"); this was purely the
boot-time directory permission.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EwZ4jR4KFAPvpjWevfUGX1
This commit is contained in:
parent
d7aa1fb6da
commit
f3938b7fdb
@ -25,7 +25,10 @@ RUN pnpm install --frozen-lockfile --filter @dorfteich/api... \
|
|||||||
|
|
||||||
FROM node:22.15-alpine
|
FROM node:22.15-alpine
|
||||||
ARG APP_VERSION=0.0.0-dev
|
ARG APP_VERSION=0.0.0-dev
|
||||||
ENV NODE_ENV=production APP_VERSION=${APP_VERSION}
|
# Default the data dirs to the writable, node-owned locations created below, so
|
||||||
|
# the image works out of the box even where compose does not set them; compose
|
||||||
|
# still mounts named volumes here for persistence (UPLOADS_DIR/PLUGINS_DIR).
|
||||||
|
ENV NODE_ENV=production APP_VERSION=${APP_VERSION} UPLOADS_DIR=/data/uploads PLUGINS_DIR=/data/plugins
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY --from=build --chown=node:node /out /app
|
COPY --from=build --chown=node:node /out /app
|
||||||
# Generate the Prisma client for this image's platform.
|
# Generate the Prisma client for this image's platform.
|
||||||
|
|||||||
@ -39,12 +39,22 @@ export class PluginWatcherService implements OnModuleInit, OnModuleDestroy {
|
|||||||
|
|
||||||
async onModuleInit(): Promise<void> {
|
async onModuleInit(): Promise<void> {
|
||||||
if (this.config.env.NODE_ENV === 'test') return;
|
if (this.config.env.NODE_ENV === 'test') return;
|
||||||
|
// The dropzone is an optional convenience over the GUI upload. If its
|
||||||
|
// directory cannot be created or watched (e.g. PLUGINS_DIR is not writable
|
||||||
|
// on this deployment), log and carry on — it must never take down the api.
|
||||||
|
try {
|
||||||
await this.storage.ensureServiceDirs();
|
await this.storage.ensureServiceDirs();
|
||||||
this.watcher = watch(this.storage.dropzoneDir, (_event, filename) => {
|
this.watcher = watch(this.storage.dropzoneDir, (_event, filename) => {
|
||||||
if (!filename || !filename.endsWith('.zip')) return;
|
if (!filename || !filename.endsWith('.zip')) return;
|
||||||
this.schedule(filename.toString());
|
this.schedule(filename.toString());
|
||||||
});
|
});
|
||||||
this.logger.info({ dir: this.storage.dropzoneDir }, 'watching plugin dropzone');
|
this.logger.info({ dir: this.storage.dropzoneDir }, 'watching plugin dropzone');
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.warn(
|
||||||
|
{ err: error, dir: this.storage.dropzoneDir },
|
||||||
|
'plugin dropzone unavailable; drop-to-install disabled (GUI upload still works)',
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onModuleDestroy(): void {
|
onModuleDestroy(): void {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user