Files
pnger/README.md

6.0 KiB

PNGer - Modern PNG Editor & Resizer

A simple, reactive, modern PNG editor and resizer with direct upload and download features. Built with TypeScript and deployed as a single Docker container on Unraid.

Features

  • Drag & Drop Upload: Intuitive file upload interface
  • Resize Operations: Width, height, and aspect ratio controls
  • Crop to Fit: Smart cropping with position control (center, top, bottom, etc.)
  • Format Conversion: PNG, WebP, and JPEG output
  • Quality Control: Adjustable compression settings
  • Direct Download: No server-side storage, immediate download
  • Modern UI: Sleek, responsive TypeScript/Svelte design

Tech Stack

  • Frontend: Svelte 4 + Vite + TypeScript
  • Backend: Node.js + Express + TypeScript
  • Image Processing: Sharp (high-performance image library)
  • Container: Docker (Alpine-based, multi-stage build)
  • Deployment: Unraid via Docker Compose

Quick Start

  1. Clone or pull this repository to your Unraid server:

    cd /mnt/user/appdata
    git clone https://git.alwisp.com/jason/pnger.git
    cd pnger
    
  2. Build the Docker image:

    docker build -t pnger:latest .
    
  3. Run via docker-compose:

    docker compose up -d
    
  4. Access the application:

    • Navigate to http://[unraid-ip]:8080

Unraid Environment Variables (Configurable via UI)

When setting up in Unraid Docker UI, you can configure:

Variable Default Description
HOST_PORT 8080 External port to access the app
MAX_FILE_SIZE 10485760 Max upload size in bytes (10MB default)
MEM_LIMIT 512m Memory limit for container
CPU_LIMIT 1.0 CPU limit (1.0 = 1 full core)
NODE_ENV production Node environment

Unraid Docker Template Example

<?xml version="1.0"?>
<Container version="2">
  <Name>pnger</Name>
  <Repository>pnger:latest</Repository>
  <Network>bridge</Network>
  <Privileged>false</Privileged>
  <WebUI>http://[IP]:[PORT:8080]</WebUI>
  <Config Name="WebUI Port" Target="3000" Default="8080" Mode="tcp" Description="Port for web interface" Type="Port" Display="always" Required="true" Mask="false">8080</Config>
  <Config Name="Max File Size" Target="MAX_FILE_SIZE" Default="10485760" Mode="" Description="Maximum upload file size in bytes" Type="Variable" Display="advanced" Required="false" Mask="false">10485760</Config>
  <Config Name="Memory Limit" Target="" Default="512m" Mode="" Description="Container memory limit" Type="Variable" Display="advanced" Required="false" Mask="false">512m</Config>
</Container>

Local Development

Prerequisites

  • Node.js 20+
  • npm or yarn

Setup

  1. Install backend dependencies:

    cd backend
    npm install
    
  2. Install frontend dependencies:

    cd frontend
    npm install
    
  3. Run development servers:

    Terminal 1 (Backend):

    cd backend
    npm run dev
    

    Terminal 2 (Frontend):

    cd frontend
    npm run dev
    
  4. Access dev server:

    • Frontend: http://localhost:5173
    • Backend API: http://localhost:3000/api

Building for Production

# Backend TypeScript compilation
cd backend
npm run build

# Frontend build
cd frontend
npm run build

Docker Deployment (Manual)

# Build the image (all dependencies and builds are handled internally)
docker build -t pnger:latest .

# Run the container
docker run -d \
  --name pnger \
  -p 8080:3000 \
  -e MAX_FILE_SIZE=10485760 \
  --restart unless-stopped \
  pnger:latest

Project Structure

pnger/
├── frontend/              # Svelte + TypeScript application
│   ├── src/
│   │   ├── App.svelte    # Main UI component
│   │   ├── main.ts       # Entry point
│   │   └── lib/
│   │       └── api.ts    # API client
│   ├── package.json
│   ├── tsconfig.json
│   └── vite.config.ts
├── backend/               # Express + TypeScript API server
│   ├── src/
│   │   ├── index.ts      # Express server
│   │   ├── routes/
│   │   │   └── image.ts  # Image transform endpoint
│   │   └── types/
│   │       └── image.ts  # TypeScript types
│   ├── package.json
│   └── tsconfig.json
├── Dockerfile             # Multi-stage build (frontend + backend)
├── docker-compose.yml     # Unraid deployment config
└── INSTRUCTIONS.md        # Development guide

How It Works

  1. User uploads an image via the web interface
  2. Frontend sends image + transform parameters to backend API
  3. Backend processes image using Sharp (resize, crop, compress, convert format)
  4. Processed image is returned directly to browser
  5. Browser triggers automatic download
  6. No files stored on server (stateless operation)

API Endpoints

POST /api/transform

Transform an image with resize, crop, and format conversion.

Request:

  • Method: POST
  • Content-Type: multipart/form-data
  • Body:
    • file: Image file (PNG/JPEG/WebP)
    • width: Target width (optional)
    • height: Target height (optional)
    • quality: Quality 10-100 (optional, default: 80)
    • format: Output format png|webp|jpeg (optional, default: png)
    • fit: Resize mode inside|cover (optional, default: inside)
    • position: Crop position when fit=cover (optional, default: center)

Response:

  • Content-Type: image/[format]
  • Body: Transformed image binary

Configuration

All configuration is handled via environment variables passed through Docker/Unraid:

  • PORT: Server port (default: 3000, internal)
  • MAX_FILE_SIZE: Maximum upload size in bytes (default: 10485760 = 10MB)
  • TEMP_DIR: Temporary directory for uploads (default: /app/temp)
  • NODE_ENV: Node environment (default: production)

License

MIT License - See LICENSE file for details

Repository

https://git.alwisp.com/jason/pnger