Skip to main content

Lania Storage Service

File storage service yang berfungsi sebagai middleman untuk service lain (upload) atau frontend (upload, read, download) untuk integrasi dan olah file ke object storage (S3-compatible).

Features

  • File Upload - Upload file dengan validasi tipe dan ukuran
  • File Read - Baca file untuk ditampilkan inline (untuk gambar, dll)
  • File Download - Download file sebagai attachment
  • PDF Reader - Endpoint khusus untuk membaca PDF dengan proper headers
  • File Delete - Soft delete file dari storage dan database
  • Object Storage Integration - Support S3-compatible storage (AWS S3, Supabase Storage, dll)
  • Local Storage Fallback - Otomatis fallback ke local storage jika object storage tidak dikonfigurasi
  • Image Compression - Otomatis compress gambar (PNG, JPG, JPEG) menggunakan Sharp
  • Database Integration - Menggunakan Prisma untuk menyimpan metadata file
  • Swagger Documentation - API documentation tersedia di /api-docs
  • CORS Support - Support untuk frontend integration
  • Health Check - Endpoint /health untuk monitoring

Tech Stack

  • Runtime: Node.js dengan Express.js
  • Database: PostgreSQL dengan Prisma ORM
  • Object Storage: AWS S3 SDK (support S3-compatible storage)
  • Image Processing: Sharp
  • File Upload: Multer
  • Logging: Winston dengan daily rotation
  • Documentation: Swagger/OpenAPI

Prerequisites

  • Node.js (v18 atau lebih baru)
  • PostgreSQL database (menggunakan schema dari lania_common)
  • (Optional) S3-compatible object storage (AWS S3, Supabase Storage, dll)

Installation

  1. Clone repository dan install dependencies:
npm install
  1. Setup environment variables:

Copy .env.example ke .env dan isi dengan konfigurasi yang sesuai:

# Server Configuration
PORT=8090
NODE_ENV=development

# File Storage
FILES_DIRECTORY=./files

# Database (PostgreSQL)
DATABASE_URL=postgresql://user:password@localhost:5432/lania_common

# Object Storage (S3-compatible) - Optional
# Jika tidak dikonfigurasi, akan menggunakan local storage
AWS_S3_BUCKET_NAME=lanya-bucket
AWS_S3_REGION=ap-southeast-1
AWS_S3_ENDPOINT=https://your-storage-endpoint.com/storage/v1/s3
AWS_ACCESS_KEY_ID=your-access-key
AWS_SECRET_ACCESS_KEY=your-secret-key

# CORS Configuration
CORS_ORIGIN=* # Set specific origin in production, e.g., http://localhost:3000

# Logging
LOG_LEVEL=debug
  1. Generate Prisma Client:
npx prisma generate
  1. Pastikan database schema sudah ada (table file_uploads di database lania_common)

Running the Application

Development Mode

npm run dev

Server akan berjalan di http://localhost:8090 (atau port yang dikonfigurasi di .env)

Production Mode

npm run build
npm start

API Endpoints

Health Check

GET /health

Response:

{
"status": "ok",
"timestamp": "2025-01-15T10:30:00.000Z",
"service": "lania-storage",
"version": "1.0.0"
}

Upload File

POST /api/files
Content-Type: multipart/form-data

Form Data:

  • file (required): File yang akan diupload
  • tenant_id (optional): UUID tenant ID
  • user_id (optional): UUID user ID
  • is_public (optional): Boolean, default false

Supported File Types:

  • Images: PNG, JPG, JPEG
  • Documents: PDF, DOC, DOCX, XLS, XLSX, CSV

File Size Limit: 5MB (default, bisa dikonfigurasi via FILE_SIZE_LIMIT_MB)

Response:

{
"message": "File uploaded successfully",
"data": {
"id": "uuid",
"file_name": "original-filename.pdf",
"file_path": "data-tenant/{tenant-id}/1234567890-123456789.pdf",
"file_type": "pdf",
"file_size": 1024000,
"mime_type": "application/pdf",
"description": null,
"is_public": false,
"created_at": "2025-01-15T10:30:00.000Z",
"updated_at": "2025-01-15T10:30:00.000Z"
}
}

Read File (Inline Display)

GET /api/read/{path}

Mengembalikan file untuk ditampilkan inline di browser (cocok untuk gambar, PDF viewer, dll).

Example:

GET /api/read/data-tenant/123/1234567890-123456789.pdf

Download File

GET /api/download/{path}

Mengembalikan file sebagai download attachment.

Example:

GET /api/download/data-tenant/123/1234567890-123456789.pdf

Read PDF (PDF Viewer)

GET /api/pdf/{path}

Endpoint khusus untuk membaca PDF dengan headers yang optimal untuk PDF viewer di browser.

Example:

GET /api/pdf/data-tenant/123/1234567890-123456789.pdf

Delete File

DELETE /api/files/{path}

Soft delete file dari storage dan database.

Response:

{
"message": "File deleted successfully"
}

Object Storage Structure

File yang diupload ke object storage akan disimpan dengan struktur:

data-tenant/{tenant-id}/{unique-filename}

Jika tenant_id tidak disediakan, akan menggunakan folder data-tenant/default/.

Local Storage vs Object Storage

Service ini mendukung dua mode storage:

  1. Object Storage Mode (Recommended untuk production)

    • File disimpan di S3-compatible storage
    • Otomatis digunakan jika AWS credentials dikonfigurasi
    • File path format: data-tenant/{tenant-id}/{filename}
  2. Local Storage Mode (Fallback)

    • File disimpan di local filesystem
    • Digunakan jika object storage tidak dikonfigurasi
    • File path format: relative path dari FILES_DIRECTORY

Image Compression

File gambar (PNG, JPG, JPEG) akan otomatis di-compress menggunakan Sharp dengan quality 80% untuk menghemat storage space.

Database Schema

Service ini menggunakan table file_uploads dari database lania_common:

CREATE TABLE file_uploads (
id CHAR(36) PRIMARY KEY,
tenant_id CHAR(36),
user_id CHAR(36),
file_name VARCHAR(255),
file_path VARCHAR(2048),
file_type VARCHAR(100),
file_size BIGINT,
mime_type VARCHAR(255),
description TEXT,
is_public BOOLEAN DEFAULT false,
created_at TIMESTAMP,
updated_at TIMESTAMP,
deleted_at TIMESTAMP
);

Security Notes

⚠️ Authorization: Saat ini authorization check untuk file access masih dalam TODO. Untuk production, disarankan untuk:

  1. Menambahkan authentication middleware (JWT, dll)
  2. Validasi tenant_id dan user_id untuk memastikan user hanya bisa mengakses file mereka sendiri
  3. Validasi is_public flag untuk file yang bisa diakses publik

API Documentation

Swagger documentation tersedia di:

http://localhost:8090/api-docs

Logging

Logs disimpan di folder logs/ dengan daily rotation:

  • {DATE}-error.log: Hanya error logs
  • {DATE}-combined.log: Semua logs

Log retention: 14 hari (otomatis dihapus setelah 2 minggu)

Error Handling

Service menggunakan centralized error handling dengan logging ke Winston. Error response format:

{
"message": "Error message",
"status": 500
}

Development

Project Structure

lania-storage/
├── src/
│ ├── common/
│ │ ├── logger/ # Winston logger configuration
│ │ └── middleware/ # Express middleware
│ ├── config/ # Configuration files
│ ├── controllers/ # Request handlers
│ ├── prisma/ # Prisma client setup
│ ├── routes.ts # Route definitions
│ ├── server.ts # Express app setup
│ └── services/ # Business logic
├── prisma/
│ └── schema.prisma # Prisma schema
└── files/ # Local storage directory

Scripts

  • npm run dev: Run development server dengan nodemon
  • npm run build: Build TypeScript ke JavaScript
  • npm start: Run production server

Integration Examples

Frontend (React/Next.js)

// Upload file
const formData = new FormData()
formData.append('file', file)
formData.append('tenant_id', tenantId)
formData.append('user_id', userId)
formData.append('is_public', 'false')

const response = await fetch('http://localhost:8090/api/files', {
method: 'POST',
body: formData,
})

// Read file (display image)
<img src={`http://localhost:8090/api/read/${filePath}`} />

// Download file
window.open(`http://localhost:8090/api/download/${filePath}`)

Backend Service (Node.js)

import FormData from 'form-data'
import fs from 'fs'

const formData = new FormData()
formData.append('file', fs.createReadStream('path/to/file.pdf'))
formData.append('tenant_id', tenantId)

const response = await fetch('http://localhost:8090/api/files', {
method: 'POST',
body: formData,
headers: formData.getHeaders(),
})

Troubleshooting

File tidak terupload ke object storage

  1. Pastikan AWS credentials sudah dikonfigurasi di .env
  2. Pastikan AWS_S3_BUCKET_NAME sudah benar
  3. Check logs untuk error message
  4. Service akan otomatis fallback ke local storage jika object storage gagal

Database connection error

  1. Pastikan DATABASE_URL sudah benar
  2. Pastikan database lania_common sudah ada
  3. Pastikan table file_uploads sudah ada di database

License

ISC