diff --git a/.dockerignore b/.dockerignore index 05df9e931..5b1aea136 100644 --- a/.dockerignore +++ b/.dockerignore @@ -30,7 +30,6 @@ Thumbs.db sonar-project.properties server/tests/ server/vitest.config.ts -server/reset-admin.js **/*.test.ts **/*.spec.ts wiki/ diff --git a/.github/workflows/publish-plugin-sdk.yml b/.github/workflows/publish-plugin-sdk.yml new file mode 100644 index 000000000..7be1cb01c --- /dev/null +++ b/.github/workflows/publish-plugin-sdk.yml @@ -0,0 +1,32 @@ +name: Publish plugin-sdk to npm + +# Publishes trek-plugin-sdk when a tag like `plugin-sdk-v1.2.0` is pushed. +# One-time setup: add an npm automation token as the repo secret NPM_TOKEN. +# The package's prepublishOnly hook builds + tests before publishing. + +on: + push: + tags: + - 'plugin-sdk-v*' + +permissions: + contents: read + +jobs: + publish: + runs-on: ubuntu-latest + defaults: + run: + working-directory: plugin-sdk + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + # 22 matches the TREK server runtime and has node:sqlite, which the + # dev-server tests exercise. + node-version: 22 + registry-url: 'https://registry.npmjs.org' + - run: npm ci + - run: npm publish + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.gitignore b/.gitignore index b1e4bdf26..fef272b87 100644 --- a/.gitignore +++ b/.gitignore @@ -65,4 +65,6 @@ coverage test-data .run -.full-review \ No newline at end of file +.full-review +# Wiki offline snapshot is baked in at build, not committed (duplicates wiki/) +server/assets/wiki/ diff --git a/Dockerfile b/Dockerfile index e6e8f1a2c..c05da667a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -46,23 +46,11 @@ COPY package.json package-lock.json ./ COPY shared/package.json ./shared/ COPY server/package.json ./server/ -# better-sqlite3 native addon requires build tools (purged after compile). -# kitinerary-extractor for booking-confirmation import: -# amd64 — static binary from KDE CDN (glibc 2.17+; wget stays for healthcheck) -# arm64 — apt package (KDE publishes no arm64 static binary) RUN apt-get update && \ - apt-get install -y --no-install-recommends tzdata dumb-init wget ca-certificates python3 build-essential && \ + apt-get install -y --no-install-recommends tzdata dumb-init wget ca-certificates python3 build-essential \ + libkitinerary-bin && \ npm ci --workspace=server --omit=dev && \ - ARCH=$(dpkg --print-architecture) && \ - if [ "$ARCH" = "amd64" ]; then \ - wget -qO /tmp/ki.tgz https://cdn.kde.org/ci-builds/pim/kitinerary/release-26.04/linux/kitinerary-extractor-x86_64-26.04.2.tgz && \ - echo "ba5cfb4a2353157c8f54cbeaea0097c5bf2c3a810e0342f63d6e524826176628 /tmp/ki.tgz" | sha256sum -c && \ - tar -xz -C /usr/local -f /tmp/ki.tgz bin/kitinerary-extractor share/locale && \ - rm /tmp/ki.tgz; \ - else \ - apt-get install -y --no-install-recommends libkitinerary-bin && \ - ln -sf "$(find /usr/lib -name kitinerary-extractor -type f | head -1)" /usr/local/bin/kitinerary-extractor; \ - fi && \ + ln -sf "$(find /usr/lib -name kitinerary-extractor -type f | head -1)" /usr/local/bin/kitinerary-extractor; \ apt-get purge -y python3 build-essential && \ apt-get autoremove -y && \ rm -rf /var/lib/apt/lists/* /usr/local/lib/node_modules/npm /usr/local/bin/npm /usr/local/bin/npx @@ -89,6 +77,8 @@ COPY server/tsconfig.json ./server/ # raw .ts source — it never enters dist, so it must be copied in explicitly or # `node --import tsx scripts/migrate-encryption.ts` fails with module-not-found. COPY server/scripts/migrate-encryption.ts ./server/scripts/migrate-encryption.ts +# Admin recovery script (node server/reset-admin.js) for locked-out installs. +COPY server/reset-admin.js ./server/reset-admin.js COPY --from=shared-builder /app/shared/dist ./shared/dist COPY --from=client-builder /app/client/dist ./server/public COPY --from=client-builder /app/client/public/fonts ./server/public/fonts diff --git a/README.md b/README.md index 3f4d95499..31f8c82d2 100644 --- a/README.md +++ b/README.md @@ -49,12 +49,12 @@ A self-hosted, real-time collaborative travel planner — with maps, budgets, pa
diff --git a/charts/README.md b/charts/README.md index 6de5de278..a54ec9b18 100644 --- a/charts/README.md +++ b/charts/README.md @@ -40,6 +40,8 @@ See `values.yaml` for more options. ## Notes - Ingress is off by default. Enable and configure hosts for your domain. - PVCs use the cluster's default StorageClass. Set `persistence.data.storageClassName` and/or `persistence.uploads.storageClassName` to bind a specific class. +- To use your own PVCs, set `persistence.data.existingClaim` and/or `persistence.uploads.existingClaim`. The other values for that volume (size, storageClassName, annotations) are then ignored. +- With `persistence.enabled: false`, the data and uploads volumes use an `emptyDir` — storage is ephemeral and lost on pod restart. Intended for testing only. - `JWT_SECRET` is managed entirely by the server — auto-generated into the data PVC on first start and rotatable via the admin panel (Settings → Danger Zone). No Helm configuration needed. - `ENCRYPTION_KEY` encrypts stored secrets (API keys, MFA, SMTP, OIDC) at rest. Recommended: set via `secretEnv.ENCRYPTION_KEY` or `existingSecret`. If left empty, the server falls back automatically: existing installs use `data/.jwt_secret` (no action needed on upgrade); fresh installs auto-generate a key persisted to the data PVC. - If using ingress, you must manually keep `env.ALLOWED_ORIGINS` and `ingress.hosts` in sync to ensure CORS works correctly. The chart does not sync these automatically. diff --git a/charts/trek/templates/NOTES.txt b/charts/trek/templates/NOTES.txt index 0e258f437..0c11d0dc8 100644 --- a/charts/trek/templates/NOTES.txt +++ b/charts/trek/templates/NOTES.txt @@ -21,3 +21,9 @@ 4. Only one method should be used at a time. If both `generateEncryptionKey` and `existingSecret` are set, `existingSecret` takes precedence. Ensure the referenced secret and key exist in the namespace. + +5. Persistence: + - To bind your own PVCs, set `persistence.data.existingClaim` and/or `persistence.uploads.existingClaim`. + The other values for that volume (size, storageClassName, annotations) are then ignored. + - With `persistence.enabled=false` the volumes use an emptyDir — storage is ephemeral and is lost + when the pod restarts. Use only for testing. diff --git a/charts/trek/templates/deployment.yaml b/charts/trek/templates/deployment.yaml index d79ae3441..09d90dc86 100644 --- a/charts/trek/templates/deployment.yaml +++ b/charts/trek/templates/deployment.yaml @@ -82,8 +82,16 @@ spec: periodSeconds: 10 volumes: - name: data + {{- if .Values.persistence.enabled }} persistentVolumeClaim: - claimName: {{ include "trek.fullname" . }}-data + claimName: {{ default (printf "%s-data" (include "trek.fullname" .)) .Values.persistence.data.existingClaim }} + {{- else }} + emptyDir: {} + {{- end }} - name: uploads + {{- if .Values.persistence.enabled }} persistentVolumeClaim: - claimName: {{ include "trek.fullname" . }}-uploads + claimName: {{ default (printf "%s-uploads" (include "trek.fullname" .)) .Values.persistence.uploads.existingClaim }} + {{- else }} + emptyDir: {} + {{- end }} diff --git a/charts/trek/templates/pvc.yaml b/charts/trek/templates/pvc.yaml index b3a069064..5e6872dea 100644 --- a/charts/trek/templates/pvc.yaml +++ b/charts/trek/templates/pvc.yaml @@ -1,4 +1,4 @@ -{{- if .Values.persistence.enabled }} +{{- if and .Values.persistence.enabled (not .Values.persistence.data.existingClaim) }} apiVersion: v1 kind: PersistentVolumeClaim metadata: @@ -18,7 +18,9 @@ spec: resources: requests: storage: {{ .Values.persistence.data.size }} +{{- end }} --- +{{- if and .Values.persistence.enabled (not .Values.persistence.uploads.existingClaim) }} apiVersion: v1 kind: PersistentVolumeClaim metadata: diff --git a/charts/trek/values.yaml b/charts/trek/values.yaml index a87a4665d..e141aa88f 100644 --- a/charts/trek/values.yaml +++ b/charts/trek/values.yaml @@ -101,15 +101,20 @@ existingSecret: "" existingSecretKey: ENCRYPTION_KEY persistence: + # When disabled, volumes fall back to an ephemeral emptyDir (data lost on pod restart). enabled: true data: size: 1Gi # Leave empty to use the cluster's default StorageClass; set to bind a specific class. storageClassName: "" + # Bind an existing PVC. The other values (size, storageClassName, annotations) are then ignored. + existingClaim: "" annotations: {} uploads: size: 1Gi storageClassName: "" + # Specify an existing PVC to bind. The other values are then ignored. + existingClaim: "" annotations: {} resources: diff --git a/client/index.html b/client/index.html index b52109eae..afbfc3541 100644 --- a/client/index.html +++ b/client/index.html @@ -5,6 +5,10 @@+ Set instance-wide config (applies to all users). Leave blank to let each user configure their own provider. +
+ + {/* Connection */} +Anthropic reads PDFs (including scans) natively. Local/OpenAI models receive extracted text — scanned PDFs need Anthropic.
+ )} +{modelsErr}
} + {!modelsErr && installed.length === 0 && !loadingModels && ( +No models installed yet — pull one below.
+ )} + {installed.length > 0 && ( +{t('admin.plugins.subtitle')}
+{t('admin.plugins.disabledTitle')}
+{t('admin.plugins.disabledBody')}
+{t('admin.plugins.noMatchInstalled')}
+{t('admin.plugins.noErrors')}
: + errorsFor.rows.map((r, i) => ( +{t('admin.plugins.empty')}
+ +{p.description}
} + {p.status === 'error' && p.last_error ? ( +{filtered ? t('admin.plugins.noMatchRegistry') : t('admin.plugins.registryEmpty')}
+{item.description}
+{perm}
+}
+
+function PluginDetailModal({ item, installed, busy, onInstall, onClose, t, locale }: {
+ item: RegistryItem; installed: boolean; busy: string | null
+ onInstall: (id: string) => void; onClose: () => void; t: T; locale: string
+}) {
+ const [detail, setDetail] = useState{item.author}{item.latest ? ` · v${item.latest}` : ''}
+{item.description}
+ {failed &&{t('admin.plugins.detailError')}
} + + {manifest && ( +{t('admin.plugins.noAccess')}
+ ) : ( +{h}
+ ))}
+ {t('admin.plugins.updateConsentBody', { name: data.plugin.name, version: data.version })}
+{host})}
+ {t('admin.plugins.reviewedMeaning')}
+{t(b as never)}
++
{restoreConfirm.filename}
+
{t('backup.restoreWarning')}
-