fix: fileStorage path issues between local and staging environments

- Store relative paths in database instead of absolute paths
- Reconstruct absolute paths at runtime using UPLOAD_DIR env var
- Add VOLUME for /app/uploads in Dockerfile for persistent storage
- Create uploads directory with proper permissions in Docker
- Add error handling for missing UPLOAD_DIR in findUniq.ts
- Simplify GitHub workflow memory in QWEN.md (manual handling)

This fixes the 500 errors on staging for file create/delete operations
caused by environment-specific absolute paths stored in database.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
2026-04-14 10:57:51 +08:00
parent 656ffcc561
commit 09c7fd8f3a
5 changed files with 20 additions and 42 deletions

View File

@@ -61,8 +61,14 @@ COPY --from=builder --chown=nextjs:nodejs /app/prisma ./prisma
COPY --from=builder --chown=nextjs:nodejs /app/next.config.* ./
COPY --chmod=755 docker-entrypoint.sh ./docker-entrypoint.sh
# Create uploads directory with proper permissions
RUN mkdir -p /app/uploads && chown nextjs:nodejs /app/uploads
USER nextjs
# Persistent storage for uploaded files
VOLUME ["/app/uploads"]
EXPOSE 3000
CMD ["/app/docker-entrypoint.sh"]

40
QWEN.md
View File

@@ -232,42 +232,4 @@ Common issues and solutions:
6. Verify responsive design on different screen sizes
## Qwen Added Memories
- **GitHub Workflow Execution**: Project ini memiliki 3 workflow GitHub Action:
1. `publish.yml` - Build & push Docker image ke GHCR (manual trigger, butuh input: stack_env + tag)
2. `re-pull.yml` - Re-pull Docker image di Portainer (manual trigger, butuh input: stack_name + stack_env)
3. `docker-publish.yml` - Auto build & push saat ada tag versi v*
Workflow bisa dijalankan via GitHub CLI: `gh workflow run <nama.yml> -f param=value --ref branch`
Setelah commit ke branch deployment (dev/stg/prod), otomatis trigger workflow publish + re-pull untuk deploy ke server.
- **Deployment Workflow Sistematis**:
1. **Version Bump** - Update `version` di `package.json` sebelum deploy (ikuti semver: major.minor.patch)
2. **Commit** - Commit perubahan + version bump dengan pesan yang jelas
3. **Push ke Branch** - Push ke branch target (biasanya `stg` untuk staging atau `prod` untuk production)
4. **Trigger Publish** - Jalankan `gh workflow run publish.yml --ref <branch> -f stack_env=<env> -f tag=<version>`
5. **Trigger Re-Pull** - Jalankan `gh workflow run re-pull.yml -f stack_name=desa-darmasaba -f stack_env=<env>`
6. **Verifikasi** - Cek workflow berhasil dan aplikasi berjalan
Branch deployment: `stg` (staging) atau `prod` (production)
Version format di package.json: `"version": "major.minor.patch"`
- **Deployment Workflow HARUS Sequential (Berurutan)**:
Saat deploy ke stg atau prod, workflow TIDAK BOLEH dijalankan bersamaan. Harus menunggu yang pertama SELESAI total baru trigger yang kedua.
**Urutan yang BENAR:**
1. ✅ **publish.yml** - Tunggu sampai SELESAI (status: ✓ success)
2. ✅ **Setelah publish selesai**, baru trigger **re-pull.yml**
**JANGAN trigger keduanya bersamaan!** Ini akan menyebabkan race condition karena re-pull akan menarik image yang belum selesai di-build.
**Cara cek workflow selesai:**
```bash
gh run view <run_id> --json status --jq '.status'
# Harus return "completed" baru lanjut ke re-pull
```
**Atau polling sampai selesai:**
```bash
gh run watch <publish_run_id>
# Tunggu sampai ada checkmark ✓
```
- **GitHub Workflows**: Project ini memiliki workflow GitHub Action untuk deployment. User akan menangani workflow secara manual di GitHub.

View File

@@ -79,7 +79,7 @@ const fileStorageCreate = async (context: Context) => {
data: {
name: finalName,
realName: file.name,
path: rootPath,
path: pathName, // Store relative path (e.g., "images", "audio", "documents")
mimeType: finalMimeType,
category,
link: `/api/fileStorage/findUnique/${finalName}`,

View File

@@ -36,7 +36,7 @@ const fileStorageDelete = async (context: Context) => {
};
}
const filePath = path.join(file.path, file.name);
const filePath = path.join(UPLOAD_DIR, file.path, file.name);
try {
// Hapus file dari filesystem

View File

@@ -3,6 +3,8 @@ import { Context } from "elysia";
import fs from "fs/promises";
import path from "path";
const UPLOAD_DIR = process.env.WIBU_UPLOAD_DIR;
const fileStorageFindUnique = async (context: Context) => {
const { name } = context.params;
@@ -20,9 +22,17 @@ const fileStorageFindUnique = async (context: Context) => {
};
}
if (!UPLOAD_DIR) {
context.set.status = "Internal Server Error";
return {
status: 500,
message: "UPLOAD_DIR is not defined",
};
}
console.log(data);
const file = await fs.readFile(path.join(data.path, data.name));
const file = await fs.readFile(path.join(UPLOAD_DIR, data.path, data.name));
context.set.headers = {
"Content-Type": data.mimeType,
"Content-Length": file.length,