https://github.com/simonabler/simonapi
Self-hosted API hub: QR & GS1 barcodes, Signpacks e-sign document. Includes OpenAPI, API keys, rate-limits, and usage stats.
https://github.com/simonabler/simonapi
angular api-gateway docker microservices nestjs nx self-hosted typescript
Last synced: 3 months ago
JSON representation
Self-hosted API hub: QR & GS1 barcodes, Signpacks e-sign document. Includes OpenAPI, API keys, rate-limits, and usage stats.
- Host: GitHub
- URL: https://github.com/simonabler/simonapi
- Owner: simonabler
- License: other
- Created: 2025-08-29T13:35:10.000Z (10 months ago)
- Default Branch: master
- Last Pushed: 2026-03-11T10:49:15.000Z (4 months ago)
- Last Synced: 2026-03-11T11:45:13.866Z (4 months ago)
- Topics: angular, api-gateway, docker, microservices, nestjs, nx, self-hosted, typescript
- Language: TypeScript
- Homepage: https://hub.abler.tirol
- Size: 1.11 MB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- License: LICENSE.md
Awesome Lists containing this project
README
# Simon API Hub · api.abler.tirol
Production-grade NX monorepo — NestJS backend + Angular frontend.
Deployed at **https://api.abler.tirol** (formerly `hub.abler.tirol` — both domains are active and point to the same service).
Built by a Cyber Security Engineer from Tyrol focused on industrial high-availability systems, reverse engineering and secure API platforms.
---
## Stack
| Layer | Technology |
|---|---|
| Backend | NestJS · TypeORM · SQLite / PostgreSQL |
| Frontend | Angular 19 (standalone) · Bootstrap 5 |
| Monorepo | NX · TypeScript |
| Barcode engine | `bwip-js` |
| API docs | Swagger UI at `/api` |
| Rate limiting | `@nestjs/throttler` |
| Font server | Self-hosted via `/fonts/*` — serves `DM Serif Display`, `Inter`, `DM Mono` as woff2 |
---
## Quick Start
```bash
# Backend (http://localhost:3000)
npx nx serve server
# Frontend (http://localhost:4200)
npx nx serve simonapi
# Run all backend tests
npx nx test server --no-coverage
```
---
## API Modules
### Barcode — `/api/barcode`
Standard and GS1-compliant barcodes as PNG or SVG.
#### Standard Barcodes
```bash
# Code128 PNG
curl "https://api.abler.tirol/api/barcode/png?type=code128&text=Hello123&includetext=true" -o out.png
# EAN-13 SVG
curl "https://api.abler.tirol/api/barcode/svg?type=ean13&text=5901234123457&includetext=true" -o out.svg
```
**Query params:** `type` · `text` · `includetext` · `scale` · `height`
Supported types: `code128` · `ean13` · `ean8` · `upca` · `code39` · `itf14` · `pdf417` · `datamatrix`
#### GS1 Barcodes — `POST /api/barcode/gs1/render`
Full AI validation, Mod-10 check digit computation, structured error responses.
```bash
curl -X POST https://hub.abler.tirol/api/barcode/gs1/render \
-H "Content-Type: application/json" \
-d '{
"symbology": "gs1-128",
"format": "png",
"includetext": true,
"scale": 3,
"items": [
{ "ai": "01", "value": "0950600013437" },
{ "ai": "17", "value": "251231" },
{ "ai": "10", "value": "BATCH42" }
]
}' -o gs1-128.png
```
**Symbologies:** `gs1-128` · `gs1datamatrix`
**Format:** `png` · `svg`
#### GS1 Batch — `POST /api/barcode/gs1/batch` *(Pro)*
Up to 100 barcodes per request. Returns Base64-PNG or raw SVG per item. Partial failures are reported per-item rather than aborting the entire batch.
```bash
curl -X POST https://hub.abler.tirol/api/barcode/gs1/batch \
-H "Content-Type: application/json" \
-H "x-api-key: sk_pro_..." \
-d '{
"symbology": "gs1-128",
"format": "png",
"barcodes": [
{ "ref": "p1", "items": [{ "ai": "01", "value": "09506000134376" }, { "ai": "17", "value": "261231" }] },
{ "ref": "p2", "items": [{ "ai": "01", "value": "09506000134376" }, { "ai": "10", "value": "LOT-B" }] }
]
}'
```
#### GS1 Digital Link — `POST /api/barcode/gs1/digital-link/encode|decode` *(Pro)*
Convert AI items ↔ GS1 Digital Link URL (e.g. `https://id.example.com/01/09506000134376/17/251231`).
```bash
# Encode
curl -X POST https://hub.abler.tirol/api/barcode/gs1/digital-link/encode \
-H "Content-Type: application/json" \
-d '{
"baseUrl": "https://id.example.com",
"items": [{ "ai": "01", "value": "09506000134376" }, { "ai": "17", "value": "251231" }]
}'
# Decode
curl -X POST https://hub.abler.tirol/api/barcode/gs1/digital-link/decode \
-H "Content-Type: application/json" \
-d '{ "url": "https://id.example.com/01/09506000134376/17/251231" }'
```
#### GS1 AI Registry — `GET /api/barcode/gs1/registry`
Full GS1 Application Identifier registry (536 AIs) with patterns, labels, combination constraints and hints. Response is HTTP-cached for 24 h.
#### SSCC Generator — `/api/barcode/sscc/*` *(Pro)*
Serial Shipping Container Code (AI `00`) — builds, validates and renders GS1-128 barcodes. GS1 Company Prefix is validated against the official Member Organisation range table (100+ entries).
| Endpoint | Description |
|---|---|
| `POST /api/barcode/sscc/build` | Assemble SSCC from components, compute Mod-10 check digit, render barcode |
| `POST /api/barcode/sscc/auto` | Auto-increment: atomically allocate next serial from DB counter, render |
| `POST /api/barcode/sscc/validate` | Verify Mod-10 check digit of an existing 18-digit SSCC |
| `POST /api/barcode/sscc/render` | Render a pre-built 18-digit SSCC as GS1-128 |
| `GET /api/barcode/sscc/prefix-info?prefix=` | Look up GS1 Member Organisation for a company prefix |
| `GET /api/barcode/sscc/counter?extensionDigit=&companyPrefix=` | Inspect current auto-increment counter state |
```bash
# Build SSCC from components
curl -X POST https://hub.abler.tirol/api/barcode/sscc/build \
-H "Content-Type: application/json" \
-H "x-api-key: sk_pro_..." \
-d '{
"extensionDigit": 3,
"companyPrefix": "0350000",
"serialReference": "1",
"format": "png"
}' -o sscc.png
# Response headers: x-sscc, x-sscc-check-digit, x-sscc-member-org
# Auto-increment (allocates next serial from DB)
curl -X POST https://hub.abler.tirol/api/barcode/sscc/auto \
-H "Content-Type: application/json" \
-H "x-api-key: sk_pro_..." \
-d '{ "extensionDigit": 3, "companyPrefix": "0350000" }' -o sscc.png
# Validate
curl -X POST https://hub.abler.tirol/api/barcode/sscc/validate \
-H "Content-Type: application/json" \
-d '{ "sscc": "330350000000000014" }'
# → { "valid": true, "checkDigit": 4, "expected": 4 }
```
**SSCC structure:**
```
[ Extension (1) ][ GS1 Company Prefix (7–10) ][ Serial Reference ][ Check Digit (1) ]
= 18 digits total
```
---
### QR — `POST /api/qr`
```bash
curl -X POST https://hub.abler.tirol/api/qr \
-H "Content-Type: application/json" \
-d '{
"type": "url",
"payload": { "url": "https://example.com" },
"format": "svg",
"size": 512,
"ecc": "M"
}'
```
**Types:** `url` · `text` · `email` · `phone` · `sms` · `vcard` · `wifi`
**Format:** `svg` (default) · `png`
**Options:** `size` · `margin` · `ecc` (L/M/Q/H)
**Download:** append `?download=1` to force `Content-Disposition: attachment`
---
### Watermark — `POST /api/watermark/apply`
Multipart form upload. Accepts JPEG, PNG, WebP, AVIF — returns same format.
```bash
# Text watermark
curl -X POST https://hub.abler.tirol/api/watermark/apply \
-F "file=@photo.jpg" \
-F "mode=text" \
-F "text=© 2025" \
-F "position=bottom-right" \
-F "opacity=0.5" \
-o out.jpg
# Logo watermark
curl -X POST https://hub.abler.tirol/api/watermark/apply \
-F "file=@photo.jpg" \
-F "logo=@logo.png" \
-F "mode=logo" \
-F "scale=0.2" \
-F "opacity=0.5" \
-o out.jpg
# Tiled text pattern
curl -X POST https://hub.abler.tirol/api/watermark/apply \
-F "file=@photo.png" \
-F "mode=text" \
-F "text=CONFIDENTIAL" \
-F "tile=true" \
-F "gap=160" \
-F "rotate=-30" \
-F "opacity=0.2" \
-o out.png
```
**Positions:** `center` · `top-left` · `top-right` · `bottom-left` · `bottom-right` · `top-center` · `bottom-center` · `center-left` · `center-right`
---
### Locks — `/api/locks`
Swipe-to-open access link management.
| Endpoint | Description |
|---|---|
| `POST /api/locks` | Create lock |
| `PATCH /api/locks/:id` | Update lock |
| `GET /api/locks` | List locks (admin) |
| `GET /api/locks/:id` | Get lock (admin) |
| `POST /api/locks/open` | Open with swipe token |
| `GET /api/locks/locks` | Public lock listing |
---
### Signpack — `/api/signpacks`
Upload → sign → bundle workflow for document signing.
```bash
# Upload
curl -X POST https://hub.abler.tirol/api/signpacks \
-F "file=@document.pdf" -F "expiresInMinutes=60"
# → { id, token, ... }
# Get metadata
curl "https://hub.abler.tirol/api/signpacks//meta?token="
# Upload signed file
curl -X POST "https://hub.abler.tirol/api/signpacks//sign?token=" \
-F "file=@signed.pdf"
# Download bundle
curl -L "https://hub.abler.tirol/api/signpacks//bundle.zip?token=&destroy=true" \
-o bundle.zip
# Delete
curl -X DELETE "https://hub.abler.tirol/api/signpacks/?token="
```
---
### Dev Utilities — `/api/utils`
| Endpoint | Description |
|---|---|
| `GET /api/utils/echo` | Request info: IP, method, headers, timestamp |
| `GET /api/utils/id?type=uuid\|ulid&count=1` | ID generator |
| `POST /api/utils/slugify` | `{ text, lower?, strict?, delimiter? }` |
| `POST /api/utils/hash?algo=sha256\|md5\|bcrypt` | `{ data, saltRounds? }` |
| `POST /api/utils/md2html` | `{ markdown }` → sanitized HTML |
---
## API Keys & Subscription Tiers
API keys are issued manually. Send a short email to **simon@abler.tirol** — response within 24 hours.
Use the key via header: `x-api-key: sk_pro_...`
| Feature | Free | Pro | Industrial |
|---|---|---|---|
| Standard Barcodes (PNG/SVG) | ✅ | ✅ | ✅ |
| QR Codes | ✅ | ✅ | ✅ |
| GS1 AI Registry Lookup | ✅ | ✅ | ✅ |
| GS1-128 / DataMatrix | ❌ | ✅ | ✅ |
| GS1 Digital Link encode/decode | ❌ | ✅ | ✅ |
| SSCC Generator (build/validate/auto) | ❌ | ✅ | ✅ |
| GS1 Batch (up to 100/request) | ❌ | ❌ | ✅ |
| Rate limit | 10 req/min | 100 req/min · 10k/day | 1k req/min · unlimited |
| Price | €0 | €29/month | €99/month |
---
## Font Server — `/fonts`
The backend self-hosts all fonts used across the `abler.tirol` ecosystem. No Google Fonts — DSGVO-konform by design.
| Endpoint | Description |
|---|---|
| `GET /fonts/abler-stack.css` | Combined `@font-face` stylesheet for all three font families |
| `GET /fonts/files/:filename` | Individual woff2 files |
**Usage in any abler.tirol frontend:**
```html
```
**Font stack:**
| Family | Weights | Role |
|---|---|---|
| `DM Serif Display` | 400 normal + italic | Headlines, Hero-Titel |
| `Inter` | 300 · 400 · 500 · 600 | Body, UI |
| `DM Mono` | 400 · 500 | Code, Tags, Badges |
Font files are sourced from `@fontsource` npm packages and committed to `apps/server/src/assets/fonts/files/`. They are copied to `dist/` at build time via the webpack asset pipeline.
---
## Deployment
### Domain Routing
Both `hub.abler.tirol` and `api.abler.tirol` are routed identically via Traefik:
- `/api/*` and `/fonts/*` → backend (port 3000)
- all other paths → frontend (port 80)
See `docker-compose.yml` for the exact Traefik label configuration.
---
### Docker
```bash
# Build backend
docker build -f dockerfiles/Dockerfile.backend -t simonapi-backend .
# Run with SQLite (persistent volume)
docker run -d --name simonapi-backend -p 3000:3000 \
-e NODE_ENV=production \
-e TYPEORM_DB=/data/db.sqlite \
-v $(pwd)/data:/data \
simonapi-backend
# Run with PostgreSQL
docker run -d --name simonapi-backend -p 3000:3000 \
-e NODE_ENV=production \
-e TYPEORM_URL=postgres://user:pass@host:5432/db \
simonapi-backend
```
### Environment Variables
| Variable | Default | Description |
|---|---|---|
| `NODE_ENV` | `development` | `production` enables optimisations |
| `TYPEORM_DB` | `./data/db.sqlite` | SQLite file path |
| `TYPEORM_URL` | — | PostgreSQL connection URL (overrides SQLite) |
| `DATA_DIR` | `./data/signpacks` | Signpack file storage path |
| `TOKEN_LENGTH` | `32` | Signpack token length |
| `FILE_MAX_BYTES` | `26214400` | Max upload size (25 MB) |
| `PURGE_CRON` | `0 * * * *` | Cron for expired signpack cleanup |
---
## Tests
```bash
# All backend tests
cd apps/server && npx jest --no-coverage
# Single suite
cd apps/server && npx jest src/app/barcode/sscc.spec.ts --no-coverage
```
Test suites: `gs1-validate` · `gs1-digital-link` · `gs1-error` · `sscc` (100 tests: buildSscc / validateSscc / validateGs1Prefix / mod10CheckDigit)
---
## Project Structure
```
apps/
server/ # NestJS backend
src/app/
barcode/ # GS1 + SSCC + standard barcodes
qr/ # QR code generator
watermark/ # Image watermarking
lock/ # Swipe-to-open locks
signpack/ # Document signing workflow
utils/ # Dev utilities
metrics/ # Usage metrics & stats
simonapi/ # Angular frontend
src/app/
features/
home/ # Landing page
barcode/ # Barcode + GS1 + SSCC generator UI
qr/ # QR generator UI
watermark/ # Watermark UI
lock/ # Lock admin + public UI
stats/ # Stats dashboard
dev-utils/ # Dev utilities UI
layout/ # Navbar, footer, cookie banner
```
---
## Fair Use
Free tier is open for personal and light commercial use — no scraping, no abuse. Rate limits and changes may apply at any time. No warranties given; use at your own risk.
For commercial integrations or SLA requirements please get in touch: **simon@abler.tirol**
---
## Privacy & Cookies
The Angular frontend ships with a first-party cookie banner. The consent status is stored in `simonapi-consent` (SameSite=Lax, 180 days). No analytics scripts, no third-party cookies. The home page loads an avatar from `gravatar.com` which may process the visitor's IP.
---
Swagger UI: **https://api.abler.tirol/api** (also reachable at `https://hub.abler.tirol/api`)