From 9a2d77ffec27349ee2b1a845cb8a6585a25d9189 Mon Sep 17 00:00:00 2001 From: max Date: Sat, 10 Oct 2026 22:38:01 +0200 Subject: [PATCH] ci(docker): add production dockerfile, compose config, and gitea actions pipeline --- .dockerignore | 7 +++ .gitea/workflows/build-runner.yml | 64 +++++++++++++++++++ Dockerfile | 38 +++++++++++ README.md | 101 +++++++++++++++++++++++++++++- docker-compose.yml | 17 +++++ 5 files changed, 225 insertions(+), 2 deletions(-) create mode 100644 .dockerignore create mode 100644 .gitea/workflows/build-runner.yml create mode 100644 Dockerfile create mode 100644 docker-compose.yml diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..bcacf57 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,7 @@ +node_modules +data +.git +.gitignore +test.js +.gitea +*.md diff --git a/.gitea/workflows/build-runner.yml b/.gitea/workflows/build-runner.yml new file mode 100644 index 0000000..b4d5165 --- /dev/null +++ b/.gitea/workflows/build-runner.yml @@ -0,0 +1,64 @@ +name: Build and Push Fridger Container + +on: + push: + branches: + - main + paths-ignore: + - 'README.md' + - '.gitignore' + workflow_dispatch: + +jobs: + build-and-push: + name: Build & Push Fridger Image + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Ensure Docker CLI is available + run: | + if ! command -v docker >/dev/null 2>&1; then + echo "Docker CLI not found. Downloading static Docker CLI binary..." + curl -fSL --connect-timeout 15 --max-time 120 --retry 3 https://download.docker.com/linux/static/stable/x86_64/docker-27.3.1.tgz -o /tmp/docker.tgz + tar -xzf /tmp/docker.tgz -C /usr/local/bin --strip-components=1 docker/docker + chmod +x /usr/local/bin/docker + rm -f /tmp/docker.tgz + fi + docker version + + - name: Log in to Gitea Container Registry + run: | + REGISTRY="gitea.eibl.tech" + USER="${{ secrets.GITEAUSER || secrets.PACKAGESUSER || github.actor }}" + PASS="${{ secrets.GITEATOKEN || secrets.PACKAGESTOKEN || secrets.TAG_TOKEN || github.token }}" + echo "Logging in to ${REGISTRY} as ${USER}..." + echo "${PASS}" | docker login "${REGISTRY}" -u "${USER}" --password-stdin + + - name: Build Docker Image + env: + DOCKER_BUILDKIT: "0" + run: | + IMAGE_TAG="gitea.eibl.tech/me0nline/fridger:latest" + echo "Building ${IMAGE_TAG}..." + docker build \ + -t "${IMAGE_TAG}" \ + -f Dockerfile \ + . + + - name: Push to Gitea Container Registry + run: | + IMAGE_TAG="gitea.eibl.tech/me0nline/fridger:latest" + echo "Pushing ${IMAGE_TAG} to Gitea Container Registry..." + docker push "${IMAGE_TAG}" + echo "Successfully pushed ${IMAGE_TAG} to Gitea Container Registry!" + + - name: Clean up temporary base images from Docker host + if: always() + run: | + echo "[*] Cleaning up temporary base images from Docker host daemon..." + docker images --filter "reference=*bullseye*" -q | xargs -r docker rmi -f 2>/dev/null || true + docker rmi -f node:16-bullseye node:16-bullseye-slim 2>/dev/null || true + docker image prune -f 2>/dev/null || true + echo "[+] Cleaned up intermediate build images from host daemon." diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..bed783b --- /dev/null +++ b/Dockerfile @@ -0,0 +1,38 @@ +# Multi-stage lightweight Docker image for Fridger +FROM node:22-alpine AS runner + +# Create app directory +WORKDIR /app + +# Set environment +ENV NODE_ENV=production \ + PORT=3000 \ + FRIDGER_DATA_DIR=/data \ + FRIDGER_DB_PATH=/data/fridger.db + +# Install dependencies first for layer caching +COPY package.json package-lock.json ./ +RUN npm ci --omit=dev && npm cache clean --force + +# Copy application source +COPY server.js db.js barcode.js ./ +COPY public/ ./public/ + +# Setup persistent data storage directory with proper permissions +RUN mkdir -p /data && chown -R node:node /app /data + +# Use non-root user +USER node + +# Expose web port +EXPOSE 3000 + +# Declare persistent volume +VOLUME ["/data"] + +# Health check +HEALTHCHECK --interval=30s --timeout=5s --start-period=5s --retries=3 \ + CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1 + +# Start the application +CMD ["node", "server.js"] diff --git a/README.md b/README.md index df9b81f..8140ff2 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,100 @@ -# Fridger +# 🧊 Fridger -A docker web-app that is built to scan and store what you have in your fridge \ No newline at end of file +> Smart Fridge Inventory & Grams Tracker with live Barcode Scanner and Dual View/Manage modes. + +![Docker](https://img.shields.io/badge/Docker-Ready-2496ED?logo=docker) +![Node.js](https://img.shields.io/badge/Node.js-22+-339933?logo=node.js) +![SQLite](https://img.shields.io/badge/Database-SQLite-003B57?logo=sqlite) + +--- + +## ✨ Features + +- **👁️ Fridge View Mode ("What's in my fridge?")**: + - Visual layout organized by shelf/compartment (Top Shelf, Middle Shelf, Bottom Shelf, Crisper Drawer, Door Rack, Freezer). + - Clear stock level and remaining grams/ml progress indicators with percentage fullness. + - Expiration countdown badges (Fresh, Expiring Soon, Expired). + - Quick consume buttons (`-50g`, `-100g`, `-1 item`, or custom slider). + +- **⚙️ Manage Fridge Mode**: + - Full inventory CRUD management. + - **Dynamic Grams/Weight Tracking**: When an item has weight (e.g. 250g cheese pack, 1kg flour, 500ml cream), easily slide or click quick-adjustment chips (`-10g`, `-25g`, `-50g`, `-100g`, `Half`, `Empty`) to log partial usage after cooking. + - Opened vs. unopened item tracking with open date. + +- **📸 Live Barcode Scanner & Search**: + - Camera-based barcode scanner (`html5-qrcode`) directly in your browser (mobile & desktop). + - Open Food Facts API integration for instant autofill: product title, brand, package net weight (grams/ml/units), category, and image. + - No nutrition/macro clutter — purely focused on what is in your fridge and how much is left! + +- **🚀 Self-Contained Container & CI/CD**: + - Lightweight Alpine container (`node:22-alpine`) with built-in SQLite engine. + - Automated build & push pipeline via Gitea Actions to `gitea.eibl.tech/me0nline/fridger:latest`. + +--- + +## 🐳 Quick Start with Docker + +### Using Docker Run +```bash +docker run -d \ + --name fridger \ + -p 3000:3000 \ + -v fridger-data:/data \ + gitea.eibl.tech/me0nline/fridger:latest +``` + +### Using Docker Compose +```yaml +services: + fridger: + image: gitea.eibl.tech/me0nline/fridger:latest + container_name: fridger + restart: unless-stopped + ports: + - "3000:3000" + volumes: + - fridger-data:/data + +volumes: + fridger-data: + name: fridger-data +``` + +Access the web interface at **`http://localhost:3000`**. + +--- + +## 🛠️ Local Development + +```bash +# 1. Install dependencies +npm install + +# 2. Run backend test suite +npm test + +# 3. Start development server +npm start +``` + +--- + +## 📡 REST API Summary + +| Method | Endpoint | Description | +|---|---|---| +| `GET` | `/api/items` | List items (supports `search`, `category`, `location`, `status`) | +| `GET` | `/api/items/:id` | Get single item details | +| `POST` | `/api/items` | Add item | +| `PUT` | `/api/items/:id` | Update item details | +| `PATCH` | `/api/items/:id/quantity` | Adjust grams/count (`{ delta: -50 }` or `{ newQuantity: 150 }`) | +| `DELETE` | `/api/items/:id` | Remove item | +| `GET` | `/api/barcode/:code` | Barcode lookup (local cache + Open Food Facts) | +| `GET` | `/api/stats` | Summary stats (total, fresh, expiring soon, opened, low) | +| `GET` | `/api/meta` | Predefined categories, shelf locations, and units | +| `GET` | `/health` | Healthcheck status | + +--- + +## 📜 License +MIT \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..6717485 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,17 @@ +services: + fridger: + image: gitea.eibl.tech/me0nline/fridger:latest + container_name: fridger + restart: unless-stopped + ports: + - "3000:3000" + environment: + - NODE_ENV=production + - PORT=3000 + - FRIDGER_DATA_DIR=/data + volumes: + - fridger-data:/data + +volumes: + fridger-data: + name: fridger-data