From fa1d6d4927c4e785288ef27024e3592b82464c37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dalibor=20Markovi=C4=87?= Date: Fri, 19 Jun 2026 02:33:00 +0200 Subject: [PATCH] =?UTF-8?q?Dokumentacija:=20a=C5=BEurirani=20README,=20dod?= =?UTF-8?q?ata=20start.sh=20skripta?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Readme.md i Readme_sr.md prošireni: demo mod, Docker uputstvo za produkciju i demo, promenljive okruženja (NTECH_SECRET, NTECH_TOTP_KEY), start.sh u strukturi projekta - start.sh dodata u repozitorijum (uklonjena iz .git/info/exclude) --- Readme.md | 145 ++++++++++++++++++++++++++++++++++-- Readme_sr.md | 122 +++++++++++++++++++++++------- start.sh | 207 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 443 insertions(+), 31 deletions(-) create mode 100755 start.sh diff --git a/Readme.md b/Readme.md index 9db30aa..55ef0f8 100644 --- a/Readme.md +++ b/Readme.md @@ -58,6 +58,7 @@ The goal is simple: everything the repair shop needs to track is located in one - Charts — monthly revenue on reports (Chart.js) - Structured logging — `log/slog` (JSON in production, text in development); separate auth log in fail2ban format - Automated tests — unit and integration over a SQLite database (crypto, RBAC, login flows, form validators, reports) +- **Demo mode** (`NTECH_ENV=demo`) — auto-created demo user, pre-filled login form, restricted backup count, blocked password/2FA changes ### Planned @@ -97,11 +98,143 @@ The goal is simple: everything the repair shop needs to track is located in one git clone cd GoNtech -# 2. Copy the configuration file -cp ntech.env.example ntech.env -# Open ntech.env and set the values (see the table below) - -# 3. Load environment variables and run in the development environment -export $(grep -v '^#' ntech.env | xargs) +# 2. Run in development mode (reads files from disk, no HTTPS required) go run ./cmd/ntech ``` + +The application opens at `http://localhost:8080`. On first run the setup wizard starts automatically. + +### Production Build + +Use the interactive build script: + +```bash +./start.sh +``` + +It asks for the version, environment (production/development), platform (Linux/Windows/both), optional UPX compression, and whether to push a Docker image to Gitea and GitHub Container Registry. + +Or build manually: + +```bash +CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \ + -ldflags "-X main.Verzija=1.0.0 -s -w" \ + -trimpath \ + -o ntech ./cmd/ntech +``` + +The result is a single static binary with no external dependencies. + +--- + +## Environment Variables + +The application reads environment variables on startup. In development, place them in `ntech.env` alongside the SQLite database file. In production/demo the program creates `ntech.env` automatically in the same directory as the database. + +`ntech.env` is **never committed** to Git. + +| Variable | Default | Description | +| ---------------- | ------------- | ----------------------------------------------------------------- | +| `NTECH_ENV` | `development` | Mode: `development`, `production`, or `demo` | +| `NTECH_PORT` | `8080` | HTTP port | +| `NTECH_DB` | `sqlite` | Database type: `sqlite` or `postgres` | +| `NTECH_SQLITE` | `ntech.db` | Path to the SQLite file | +| `NTECH_DSN` | — | PostgreSQL connection string | +| `NTECH_SECRET` | — | Session signing key (min. 32 bytes); auto-generated if missing | +| `NTECH_TOTP_KEY` | — | AES-256 key for TOTP secret encryption; auto-generated if missing | + +`NTECH_SECRET` and `NTECH_TOTP_KEY` are generated automatically on the first run and saved to `ntech.env`. **Back this file up** — losing `NTECH_TOTP_KEY` invalidates all 2FA secrets stored in the database. + +--- + +## Docker Deployment + +Docker images are published to: +- `ghcr.io/dalibor31/ntech:latest` +- `git.vm-net.in.rs/dasko/ntech:latest` + +### Production + +```yaml +# docker-compose.yml +services: + ntech: + image: ghcr.io/dalibor31/ntech:latest + restart: unless-stopped + environment: + NTECH_ENV: production + NTECH_PORT: "8000" + NTECH_SQLITE: /app/data/ntech.db + volumes: + - ./data:/app/data # database + ntech.env (secrets) + - ./uploads:/app/uploads # uploaded images + - ./logs:/app/logs # structured + auth logs + - ./backups:/app/backups # automatic database backups + ports: + - "8000:8000" +``` + +On the **first start** the setup wizard runs and creates the first admin user. After that, `./data/ntech.env` contains the auto-generated secrets — **back it up**. + +Place the app behind a reverse proxy (Caddy, nginx) that terminates HTTPS. Secure cookies require HTTPS. + +Example Caddy config: + +``` +your.domain.com { + reverse_proxy ntech:8000 +} +``` + +### Demo Mode + +Demo mode runs a fully functional copy with a pre-created `Demo` / `Demo1234` admin account. Password and 2FA changes are blocked. Backup is limited to 2 copies. + +```yaml +# docker-compose.yml (demo) +services: + ntech-demo: + image: ghcr.io/dalibor31/ntech:latest + restart: unless-stopped + environment: + NTECH_ENV: demo + NTECH_PORT: "8000" + NTECH_SQLITE: /app/data/ntech.db + volumes: + - ./data:/app/data + - ./uploads:/app/uploads + - ./logs:/app/logs + - ./backups:/app/backups + ports: + - "8000:8000" +``` + +Demo also requires HTTPS (Caddy or similar) because Secure cookies are enabled. + +--- + +## Project Structure + +``` +ntech/ +├── cmd/ +│ └── ntech/ # entry point +├── internal/ +│ ├── auth/ # login, sessions, fail2ban log +│ ├── config/ # settings, setup wizard +│ ├── db/ # database layer +│ │ └── sqlite/ # SQLite implementation +│ ├── handler/ # HTTP handlers +│ ├── middleware/ # CSRF, security headers, authentication +│ └── model/ # shared data types +├── web/ +│ ├── static/ # CSS, JavaScript, images, logos +│ └── templates/ # HTML templates +├── migrations/ # SQL migrations (001_desc.sql, 002_desc.sql, ...) +├── logs/ # auth.log and other logs +├── backups/ # database backups +├── start.sh # interactive build and Docker push script +├── Dockerfile +├── go.mod +└── go.sum +``` diff --git a/Readme_sr.md b/Readme_sr.md index c171953..aaf7cb8 100644 --- a/Readme_sr.md +++ b/Readme_sr.md @@ -58,6 +58,7 @@ Cilj je jednostavan: sve što servis treba da prati nalazi se na jednom mestu, b - Grafikoni — mesečni prihod na izveštajima (Chart.js) - Strukturisano logovanje — `log/slog` (JSON u produkciji, tekst u razvoju); zaseban auth log u fail2ban formatu - Automatski testovi — jedinični i integracioni nad SQLite bazom (kripto, RBAC, tokovi prijave, validatori forme, izveštaji) +- **Demo mod** (`NTECH_ENV=demo`) — automatski kreiran demo korisnik, pre-popunjeni login, ograničen bekap, blokirana promena lozinke i 2FA ### Planirano @@ -97,30 +98,29 @@ Cilj je jednostavan: sve što servis treba da prati nalazi se na jednom mestu, b git clone cd GoNtech -# 2. Kopiranje konfiguracionog fajla -cp ntech.env.example ntech.env -# Otvori ntech.env i postavi vrednosti (videti tabelu ispod) - -# 3. Učitavanje promenljivih i pokretanje u razvojnom okruženju -export $(grep -v '^#' ntech.env | xargs) +# 2. Pokretanje u razvojnom modu (čita fajlove sa diska, ne zahteva HTTPS) go run ./cmd/ntech ``` -Program se otvara na `http://localhost:8080` (ili na portu definisanom u `ntech.env`). - -Pri prvom pokretanju automatski se pokreće setup wizard. +Program se otvara na `http://localhost:8080`. Pri prvom pokretanju automatski se pokreće setup wizard. ### Produkcioni build -```bash -# Pomoću build.sh skripte (prima opcioni argument verzije) -./build.sh 1.0.0 +Koristi interaktivnu skriptu: -# Ili ručno -CGO_ENABLED=0 GOARCH=amd64 GOOS=linux go build \ +```bash +./start.sh +``` + +Skripta pita za verziju, okruženje (production/development), platformu (Linux/Windows/obe), opcionalnu UPX kompresiju i da li da gurne Docker image na Gitea i GitHub Container Registry. + +Ili ručno: + +```bash +CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \ -ldflags "-X main.Verzija=1.0.0 -s -w" \ + -trimpath \ -o ntech ./cmd/ntech -./ntech ``` Rezultat je jedan statički binarni fajl bez zavisnosti. @@ -129,15 +129,87 @@ Rezultat je jedan statički binarni fajl bez zavisnosti. ## Promenljive okruženja -Kopirati `ntech.env.example` u `ntech.env` i popuniti vrednosti. Fajl `ntech.env` se **ne commituje** u Git. +Program čita promenljive okruženja pri pokretanju. U razvojnom modu staviti ih u `ntech.env` pored SQLite baze. U production/demo modu program sam kreira `ntech.env` u istom folderu gde je baza. -| Promenljiva | Podrazumevano | Opis | -| -------------- | ------------- | -------------------------------------------- | -| `NTECH_ENV` | `development` | Okruženje: `development` ili `production` | -| `NTECH_PORT` | `8080` | HTTP port | -| `NTECH_DB` | `sqlite` | Tip baze: `sqlite` ili `postgres` | -| `NTECH_SQLITE` | `ntech.db` | Putanja do SQLite fajla | -| `NTECH_DSN` | — | PostgreSQL connection string | +Fajl `ntech.env` se **ne commituje** u Git. + +| Promenljiva | Podrazumevano | Opis | +| ---------------- | ------------- | ------------------------------------------------------------------ | +| `NTECH_ENV` | `development` | Mod: `development`, `production` ili `demo` | +| `NTECH_PORT` | `8080` | HTTP port | +| `NTECH_DB` | `sqlite` | Tip baze: `sqlite` ili `postgres` | +| `NTECH_SQLITE` | `ntech.db` | Putanja do SQLite fajla | +| `NTECH_DSN` | — | PostgreSQL connection string | +| `NTECH_SECRET` | — | Ključ za potpisivanje sesija (min. 32 bajta); auto-generiše se | +| `NTECH_TOTP_KEY` | — | AES-256 ključ za šifrovanje TOTP tajni; auto-generiše se | + +`NTECH_SECRET` i `NTECH_TOTP_KEY` se automatski generišu pri prvom pokretanju i upisuju u `ntech.env`. **Sačuvaj backup ovog fajla** — gubitak `NTECH_TOTP_KEY` onemogućuje prijavu svim korisnicima koji imaju 2FA. + +--- + +## Docker deployment + +Docker image je dostupan na: +- `ghcr.io/dalibor31/ntech:latest` +- `git.vm-net.in.rs/dasko/ntech:latest` + +### Produkcija + +```yaml +# docker-compose.yml +services: + ntech: + image: ghcr.io/dalibor31/ntech:latest + restart: unless-stopped + environment: + NTECH_ENV: production + NTECH_PORT: "8000" + NTECH_SQLITE: /app/data/ntech.db + volumes: + - ./data:/app/data # baza + ntech.env (tajne) + - ./uploads:/app/uploads # uploadovane slike + - ./logs:/app/logs # strukturisani + auth log + - ./backups:/app/backups # automatski bekap baze + ports: + - "8000:8000" +``` + +Pri **prvom pokretanju** pokreće se setup wizard za kreiranje prvog admin korisnika. Nakon toga, `./data/ntech.env` sadrži auto-generisane tajne — **sačuvaj backup**. + +Stavi program iza reverznog proksija (Caddy, nginx) koji terminira HTTPS. Secure kolačići zahtevaju HTTPS. + +Primer Caddy konfiguracije: + +``` +tvoj.domen.com { + reverse_proxy ntech:8000 +} +``` + +### Demo mod + +Demo mod pokreće potpuno funkcionalnu kopiju sa pre-kreiranim nalogom `Demo` / `Demo1234` (admin). Promena lozinke i 2FA su blokirani. Bekap je ograničen na 2 kopije. + +```yaml +# docker-compose.yml (demo) +services: + ntech-demo: + image: ghcr.io/dalibor31/ntech:latest + restart: unless-stopped + environment: + NTECH_ENV: demo + NTECH_PORT: "8000" + NTECH_SQLITE: /app/data/ntech.db + volumes: + - ./data:/app/data + - ./uploads:/app/uploads + - ./logs:/app/logs + - ./backups:/app/backups + ports: + - "8000:8000" +``` + +Demo takođe zahteva HTTPS (Caddy ili slično) jer su Secure kolačići uključeni. --- @@ -161,8 +233,8 @@ ntech/ ├── migrations/ # SQL migracije (001_opis.sql, 002_opis.sql, ...) ├── logs/ # auth.log i ostali logovi ├── backups/ # rezervne kopije baze -├── build.sh # skripta za produkcioni build -├── ntech.env # lokalna konfiguracija (ne commituje se) +├── start.sh # interaktivna skripta za build i Docker push +├── Dockerfile ├── go.mod └── go.sum ``` diff --git a/start.sh b/start.sh new file mode 100755 index 0000000..cdeae2e --- /dev/null +++ b/start.sh @@ -0,0 +1,207 @@ +#!/bin/bash +set -e + +GITEA_IMAGE="git.vm-net.in.rs/dasko/ntech" +GITHUB_IMAGE="ghcr.io/dalibor31/ntech" +VER_FAJL="VERSION" + +clear +echo "╔══════════════════════════════════════╗" +echo "║ NTech — Build alat ║" +echo "╚══════════════════════════════════════╝" +echo "" + +# 1. Verzija +VERZIJA_DEFAULT=$(cat "$VER_FAJL" 2>/dev/null || echo "0.0.0") +read -p "1) Verzija [${VERZIJA_DEFAULT}]: " VERZIJA +VERZIJA="${VERZIJA:-$VERZIJA_DEFAULT}" +if [ "$VERZIJA" != "$VERZIJA_DEFAULT" ]; then + echo "$VERZIJA" > "$VER_FAJL" + echo " → VERSION ažuriran na: $VERZIJA" +fi +echo "" + +# 2. Okruženje +echo "2) Okruženje:" +echo " 1) Production (podrazumevano)" +echo " 2) Development" +read -p " Izbor [1/2]: " OKR_IZBOR +OKR_IZBOR="${OKR_IZBOR:-1}" +echo "" + +# 3. Platforma +echo "3) Platforma:" +echo " 1) Linux (podrazumevano)" +echo " 2) Windows" +echo " 3) Obe" +read -p " Izbor [1/2/3]: " PLATFORMA_IZBOR +PLATFORMA_IZBOR="${PLATFORMA_IZBOR:-1}" +echo "" + +# 4. UPX +read -p "4) Kompresovati UPX-om? [d/N]: " UPX_IZBOR +UPX_IZBOR="${UPX_IZBOR:-n}" +echo "" + +# 5. Build +read -p "5) Pokrenuti build? [D/n]: " BUILD_IZBOR +BUILD_IZBOR="${BUILD_IZBOR:-d}" +echo "" + +# 6. Docker push +read -p "6) Push Docker image (Gitea + GitHub)? [d/N]: " DOCKER_IZBOR +DOCKER_IZBOR="${DOCKER_IZBOR:-n}" +echo "" + +# ── Izračunaj vrednosti ────────────────────────── +if [ "$OKR_IZBOR" = "2" ]; then + OKRUZENJE="development" + VERZIJA_BUILD="dev-${VERZIJA}" + LDFLAGS="-X main.Verzija=dev-${VERZIJA}" + TRIMPATH="" +else + OKRUZENJE="production" + VERZIJA_BUILD="${VERZIJA}" + LDFLAGS="-X main.Verzija=${VERZIJA} -s -w" + TRIMPATH="-trimpath" +fi + +case "$PLATFORMA_IZBOR" in + 2) PLATFORMA_NAZIV="Windows" ;; + 3) PLATFORMA_NAZIV="Linux + Windows" ;; + *) PLATFORMA_NAZIV="Linux" ;; +esac + +if [[ "$UPX_IZBOR" =~ ^[dDyY] ]]; then UPX_NAZIV="da"; else UPX_NAZIV="ne"; fi +if [[ "$BUILD_IZBOR" =~ ^[dDyY] ]]; then BUILD_NAZIV="da"; else BUILD_NAZIV="ne"; fi +if [[ "$DOCKER_IZBOR" =~ ^[dDyY] ]]; then DOCKER_NAZIV="da"; else DOCKER_NAZIV="ne"; fi + +# ── Sažetak ────────────────────────────────────── +echo "──────────────────────────────────────────" +echo " Verzija : ${VERZIJA_BUILD}" +echo " Okruženje : ${OKRUZENJE}" +echo " Platforma : ${PLATFORMA_NAZIV}" +echo " UPX : ${UPX_NAZIV}" +echo " Build : ${BUILD_NAZIV}" +echo " Docker : ${DOCKER_NAZIV}" +echo "──────────────────────────────────────────" +echo "" +read -p "Pokrenuti? [D/n]: " POTVRDA +POTVRDA="${POTVRDA:-d}" +if [[ ! "$POTVRDA" =~ ^[dDyY] ]]; then + echo "Otkazano." + exit 0 +fi +echo "" + +# ── UPX: instaliraj ako treba ──────────────────── +if [[ "$UPX_IZBOR" =~ ^[dDyY] ]] && [[ "$BUILD_IZBOR" =~ ^[dDyY] ]]; then + if ! command -v upx &>/dev/null; then + echo "→ UPX nije instaliran. Instaliram..." + if command -v apt-get &>/dev/null; then + sudo apt-get install -y upx + elif command -v dnf &>/dev/null; then + sudo dnf install -y upx + elif command -v pacman &>/dev/null; then + sudo pacman -S --noconfirm upx + elif command -v brew &>/dev/null; then + brew install upx + else + echo " UPOZORENJE: Ne mogu da instaliram UPX — nepoznat menadžer paketa. Kompresija preskočena." + UPX_IZBOR="n" + fi + echo "" + fi +fi + +# ── Build funkcija ─────────────────────────────── +build_za() { + local GOOS_VAL="$1" + local NAZIV="$2" + echo "→ Build ${GOOS_VAL}/amd64: ${NAZIV}" + CGO_ENABLED=0 GOOS="${GOOS_VAL}" GOARCH=amd64 go build \ + -ldflags "${LDFLAGS}" \ + ${TRIMPATH} \ + -o "${NAZIV}" \ + ./cmd/ntech + ls -lh "${NAZIV}" + + if [[ "$UPX_IZBOR" =~ ^[dDyY] ]] && command -v upx &>/dev/null; then + echo " Kompresovanje sa UPX..." + upx --best "${NAZIV}" + ls -lh "${NAZIV}" + fi +} + +# ── 5. Build ───────────────────────────────────── +if [[ "$BUILD_IZBOR" =~ ^[dDyY] ]]; then + echo "=== Build ===" + case "$PLATFORMA_IZBOR" in + 2) + build_za "windows" "ntech.exe" + ;; + 3) + build_za "linux" "ntech" & + PID_LINUX=$! + build_za "windows" "ntech.exe" & + PID_WIN=$! + wait $PID_LINUX $PID_WIN + ;; + *) + build_za "linux" "ntech" + ;; + esac + echo "" +fi + +# ── 6. Docker push ─────────────────────────────── +if [[ "$DOCKER_IZBOR" =~ ^[dDyY] ]]; then + echo "=== Docker ===" + + # Ako Linux binary nije izgrađen u ovom pozivu (build=ne ili platforma=Windows), izgradi ga + LINUX_VEC_IZGRADJEN=0 + if [[ "$BUILD_IZBOR" =~ ^[dDyY] ]] && [ "$PLATFORMA_IZBOR" != "2" ]; then + LINUX_VEC_IZGRADJEN=1 + fi + + if [ "$LINUX_VEC_IZGRADJEN" = "0" ]; then + echo "→ Gradim Linux binary za Docker (sa UPX ako je uključen)..." + # instaliraj UPX ako treba, a još nije instaliran + if [[ "$UPX_IZBOR" =~ ^[dDyY] ]] && ! command -v upx &>/dev/null; then + echo "→ UPX nije instaliran. Instaliram..." + if command -v apt-get &>/dev/null; then + sudo apt-get install -y upx + elif command -v dnf &>/dev/null; then + sudo dnf install -y upx + elif command -v pacman &>/dev/null; then + sudo pacman -S --noconfirm upx + elif command -v brew &>/dev/null; then + brew install upx + else + echo " UPOZORENJE: Ne mogu da instaliram UPX — kompresija preskočena." + UPX_IZBOR="n" + fi + fi + build_za "linux" "ntech" + echo "" + fi + + echo "→ Build Docker image..." + docker build --build-arg="VERZIJA=${VERZIJA}" \ + -t "${GITEA_IMAGE}:${VERZIJA}" \ + -t "${GITEA_IMAGE}:latest" \ + -t "${GITHUB_IMAGE}:${VERZIJA}" \ + -t "${GITHUB_IMAGE}:latest" \ + . + + echo "→ Push na Gitea..." + docker push "${GITEA_IMAGE}:${VERZIJA}" + docker push "${GITEA_IMAGE}:latest" + + echo "→ Push na GitHub..." + docker push "${GITHUB_IMAGE}:${VERZIJA}" + docker push "${GITHUB_IMAGE}:latest" + echo "" +fi + +echo "==> Gotovo! NTech v${VERZIJA_BUILD}"