{"id":48125104,"url":"https://github.com/simonabler/simonapi","last_synced_at":"2026-04-04T16:25:29.972Z","repository":{"id":312724811,"uuid":"1046944157","full_name":"simonabler/simonapi","owner":"simonabler","description":"Self-hosted API hub: QR \u0026 GS1 barcodes, Signpacks e-sign document. Includes OpenAPI, API keys, rate-limits, and usage stats.","archived":false,"fork":false,"pushed_at":"2026-03-11T10:49:15.000Z","size":1168,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-03-11T11:45:13.866Z","etag":null,"topics":["angular","api-gateway","docker","microservices","nestjs","nx","self-hosted","typescript"],"latest_commit_sha":null,"homepage":"https://hub.abler.tirol","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/simonabler.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-08-29T13:35:10.000Z","updated_at":"2026-03-11T08:58:58.000Z","dependencies_parsed_at":"2025-09-01T17:22:28.039Z","dependency_job_id":"11c3439d-8339-4451-94ed-0aa154d7d347","html_url":"https://github.com/simonabler/simonapi","commit_stats":null,"previous_names":["simonabler/simonapi"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/simonabler/simonapi","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simonabler%2Fsimonapi","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simonabler%2Fsimonapi/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simonabler%2Fsimonapi/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simonabler%2Fsimonapi/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/simonabler","download_url":"https://codeload.github.com/simonabler/simonapi/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simonabler%2Fsimonapi/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31405700,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-04T10:20:44.708Z","status":"ssl_error","status_checked_at":"2026-04-04T10:20:06.846Z","response_time":60,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["angular","api-gateway","docker","microservices","nestjs","nx","self-hosted","typescript"],"created_at":"2026-04-04T16:25:29.290Z","updated_at":"2026-04-04T16:25:29.965Z","avatar_url":"https://github.com/simonabler.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Simon API Hub · api.abler.tirol\n\nProduction-grade NX monorepo — NestJS backend + Angular frontend.\n\nDeployed at **https://api.abler.tirol** (formerly `hub.abler.tirol` — both domains are active and point to the same service).\n\nBuilt by a Cyber Security Engineer from Tyrol focused on industrial high-availability systems, reverse engineering and secure API platforms.\n\n---\n\n## Stack\n\n| Layer | Technology |\n|---|---|\n| Backend | NestJS · TypeORM · SQLite / PostgreSQL |\n| Frontend | Angular 19 (standalone) · Bootstrap 5 |\n| Monorepo | NX · TypeScript |\n| Barcode engine | `bwip-js` |\n| API docs | Swagger UI at `/api` |\n| Rate limiting | `@nestjs/throttler` |\n| Font server | Self-hosted via `/fonts/*` — serves `DM Serif Display`, `Inter`, `DM Mono` as woff2 |\n\n---\n\n## Quick Start\n\n```bash\n# Backend (http://localhost:3000)\nnpx nx serve server\n\n# Frontend (http://localhost:4200)\nnpx nx serve simonapi\n\n# Run all backend tests\nnpx nx test server --no-coverage\n```\n\n---\n\n## API Modules\n\n### Barcode — `/api/barcode`\n\nStandard and GS1-compliant barcodes as PNG or SVG.\n\n#### Standard Barcodes\n\n```bash\n# Code128 PNG\ncurl \"https://api.abler.tirol/api/barcode/png?type=code128\u0026text=Hello123\u0026includetext=true\" -o out.png\n\n# EAN-13 SVG\ncurl \"https://api.abler.tirol/api/barcode/svg?type=ean13\u0026text=5901234123457\u0026includetext=true\" -o out.svg\n```\n\n**Query params:** `type` · `text` · `includetext` · `scale` · `height`\n\nSupported types: `code128` · `ean13` · `ean8` · `upca` · `code39` · `itf14` · `pdf417` · `datamatrix`\n\n#### GS1 Barcodes — `POST /api/barcode/gs1/render`\n\nFull AI validation, Mod-10 check digit computation, structured error responses.\n\n```bash\ncurl -X POST https://hub.abler.tirol/api/barcode/gs1/render \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"symbology\": \"gs1-128\",\n    \"format\": \"png\",\n    \"includetext\": true,\n    \"scale\": 3,\n    \"items\": [\n      { \"ai\": \"01\", \"value\": \"0950600013437\" },\n      { \"ai\": \"17\", \"value\": \"251231\" },\n      { \"ai\": \"10\", \"value\": \"BATCH42\" }\n    ]\n  }' -o gs1-128.png\n```\n\n**Symbologies:** `gs1-128` · `gs1datamatrix`  \n**Format:** `png` · `svg`\n\n#### GS1 Batch — `POST /api/barcode/gs1/batch` *(Pro)*\n\nUp 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.\n\n```bash\ncurl -X POST https://hub.abler.tirol/api/barcode/gs1/batch \\\n  -H \"Content-Type: application/json\" \\\n  -H \"x-api-key: sk_pro_...\" \\\n  -d '{\n    \"symbology\": \"gs1-128\",\n    \"format\": \"png\",\n    \"barcodes\": [\n      { \"ref\": \"p1\", \"items\": [{ \"ai\": \"01\", \"value\": \"09506000134376\" }, { \"ai\": \"17\", \"value\": \"261231\" }] },\n      { \"ref\": \"p2\", \"items\": [{ \"ai\": \"01\", \"value\": \"09506000134376\" }, { \"ai\": \"10\", \"value\": \"LOT-B\" }] }\n    ]\n  }'\n```\n\n#### GS1 Digital Link — `POST /api/barcode/gs1/digital-link/encode|decode` *(Pro)*\n\nConvert AI items ↔ GS1 Digital Link URL (e.g. `https://id.example.com/01/09506000134376/17/251231`).\n\n```bash\n# Encode\ncurl -X POST https://hub.abler.tirol/api/barcode/gs1/digital-link/encode \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"baseUrl\": \"https://id.example.com\",\n    \"items\": [{ \"ai\": \"01\", \"value\": \"09506000134376\" }, { \"ai\": \"17\", \"value\": \"251231\" }]\n  }'\n\n# Decode\ncurl -X POST https://hub.abler.tirol/api/barcode/gs1/digital-link/decode \\\n  -H \"Content-Type: application/json\" \\\n  -d '{ \"url\": \"https://id.example.com/01/09506000134376/17/251231\" }'\n```\n\n#### GS1 AI Registry — `GET /api/barcode/gs1/registry`\n\nFull GS1 Application Identifier registry (536 AIs) with patterns, labels, combination constraints and hints. Response is HTTP-cached for 24 h.\n\n#### SSCC Generator — `/api/barcode/sscc/*` *(Pro)*\n\nSerial 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).\n\n| Endpoint | Description |\n|---|---|\n| `POST /api/barcode/sscc/build` | Assemble SSCC from components, compute Mod-10 check digit, render barcode |\n| `POST /api/barcode/sscc/auto` | Auto-increment: atomically allocate next serial from DB counter, render |\n| `POST /api/barcode/sscc/validate` | Verify Mod-10 check digit of an existing 18-digit SSCC |\n| `POST /api/barcode/sscc/render` | Render a pre-built 18-digit SSCC as GS1-128 |\n| `GET /api/barcode/sscc/prefix-info?prefix=` | Look up GS1 Member Organisation for a company prefix |\n| `GET /api/barcode/sscc/counter?extensionDigit=\u0026companyPrefix=` | Inspect current auto-increment counter state |\n\n```bash\n# Build SSCC from components\ncurl -X POST https://hub.abler.tirol/api/barcode/sscc/build \\\n  -H \"Content-Type: application/json\" \\\n  -H \"x-api-key: sk_pro_...\" \\\n  -d '{\n    \"extensionDigit\": 3,\n    \"companyPrefix\": \"0350000\",\n    \"serialReference\": \"1\",\n    \"format\": \"png\"\n  }' -o sscc.png\n# Response headers: x-sscc, x-sscc-check-digit, x-sscc-member-org\n\n# Auto-increment (allocates next serial from DB)\ncurl -X POST https://hub.abler.tirol/api/barcode/sscc/auto \\\n  -H \"Content-Type: application/json\" \\\n  -H \"x-api-key: sk_pro_...\" \\\n  -d '{ \"extensionDigit\": 3, \"companyPrefix\": \"0350000\" }' -o sscc.png\n\n# Validate\ncurl -X POST https://hub.abler.tirol/api/barcode/sscc/validate \\\n  -H \"Content-Type: application/json\" \\\n  -d '{ \"sscc\": \"330350000000000014\" }'\n# → { \"valid\": true, \"checkDigit\": 4, \"expected\": 4 }\n```\n\n**SSCC structure:**\n```\n[ Extension (1) ][ GS1 Company Prefix (7–10) ][ Serial Reference ][ Check Digit (1) ]\n                                                = 18 digits total\n```\n\n---\n\n### QR — `POST /api/qr`\n\n```bash\ncurl -X POST https://hub.abler.tirol/api/qr \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"type\": \"url\",\n    \"payload\": { \"url\": \"https://example.com\" },\n    \"format\": \"svg\",\n    \"size\": 512,\n    \"ecc\": \"M\"\n  }'\n```\n\n**Types:** `url` · `text` · `email` · `phone` · `sms` · `vcard` · `wifi`  \n**Format:** `svg` (default) · `png`  \n**Options:** `size` · `margin` · `ecc` (L/M/Q/H)  \n**Download:** append `?download=1` to force `Content-Disposition: attachment`\n\n---\n\n### Watermark — `POST /api/watermark/apply`\n\nMultipart form upload. Accepts JPEG, PNG, WebP, AVIF — returns same format.\n\n```bash\n# Text watermark\ncurl -X POST https://hub.abler.tirol/api/watermark/apply \\\n  -F \"file=@photo.jpg\" \\\n  -F \"mode=text\" \\\n  -F \"text=© 2025\" \\\n  -F \"position=bottom-right\" \\\n  -F \"opacity=0.5\" \\\n  -o out.jpg\n\n# Logo watermark\ncurl -X POST https://hub.abler.tirol/api/watermark/apply \\\n  -F \"file=@photo.jpg\" \\\n  -F \"logo=@logo.png\" \\\n  -F \"mode=logo\" \\\n  -F \"scale=0.2\" \\\n  -F \"opacity=0.5\" \\\n  -o out.jpg\n\n# Tiled text pattern\ncurl -X POST https://hub.abler.tirol/api/watermark/apply \\\n  -F \"file=@photo.png\" \\\n  -F \"mode=text\" \\\n  -F \"text=CONFIDENTIAL\" \\\n  -F \"tile=true\" \\\n  -F \"gap=160\" \\\n  -F \"rotate=-30\" \\\n  -F \"opacity=0.2\" \\\n  -o out.png\n```\n\n**Positions:** `center` · `top-left` · `top-right` · `bottom-left` · `bottom-right` · `top-center` · `bottom-center` · `center-left` · `center-right`\n\n---\n\n### Locks — `/api/locks`\n\nSwipe-to-open access link management.\n\n| Endpoint | Description |\n|---|---|\n| `POST /api/locks` | Create lock |\n| `PATCH /api/locks/:id` | Update lock |\n| `GET /api/locks` | List locks (admin) |\n| `GET /api/locks/:id` | Get lock (admin) |\n| `POST /api/locks/open` | Open with swipe token |\n| `GET /api/locks/locks` | Public lock listing |\n\n---\n\n### Signpack — `/api/signpacks`\n\nUpload → sign → bundle workflow for document signing.\n\n```bash\n# Upload\ncurl -X POST https://hub.abler.tirol/api/signpacks \\\n  -F \"file=@document.pdf\" -F \"expiresInMinutes=60\"\n# → { id, token, ... }\n\n# Get metadata\ncurl \"https://hub.abler.tirol/api/signpacks/\u003cID\u003e/meta?token=\u003cTOKEN\u003e\"\n\n# Upload signed file\ncurl -X POST \"https://hub.abler.tirol/api/signpacks/\u003cID\u003e/sign?token=\u003cTOKEN\u003e\" \\\n  -F \"file=@signed.pdf\"\n\n# Download bundle\ncurl -L \"https://hub.abler.tirol/api/signpacks/\u003cID\u003e/bundle.zip?token=\u003cTOKEN\u003e\u0026destroy=true\" \\\n  -o bundle.zip\n\n# Delete\ncurl -X DELETE \"https://hub.abler.tirol/api/signpacks/\u003cID\u003e?token=\u003cTOKEN\u003e\"\n```\n\n---\n\n### Dev Utilities — `/api/utils`\n\n| Endpoint | Description |\n|---|---|\n| `GET /api/utils/echo` | Request info: IP, method, headers, timestamp |\n| `GET /api/utils/id?type=uuid\\|ulid\u0026count=1` | ID generator |\n| `POST /api/utils/slugify` | `{ text, lower?, strict?, delimiter? }` |\n| `POST /api/utils/hash?algo=sha256\\|md5\\|bcrypt` | `{ data, saltRounds? }` |\n| `POST /api/utils/md2html` | `{ markdown }` → sanitized HTML |\n\n---\n\n## API Keys \u0026 Subscription Tiers\n\nAPI keys are issued manually. Send a short email to **simon@abler.tirol** — response within 24 hours.\n\nUse the key via header: `x-api-key: sk_pro_...`\n\n| Feature | Free | Pro | Industrial |\n|---|---|---|---|\n| Standard Barcodes (PNG/SVG) | ✅ | ✅ | ✅ |\n| QR Codes | ✅ | ✅ | ✅ |\n| GS1 AI Registry Lookup | ✅ | ✅ | ✅ |\n| GS1-128 / DataMatrix | ❌ | ✅ | ✅ |\n| GS1 Digital Link encode/decode | ❌ | ✅ | ✅ |\n| SSCC Generator (build/validate/auto) | ❌ | ✅ | ✅ |\n| GS1 Batch (up to 100/request) | ❌ | ❌ | ✅ |\n| Rate limit | 10 req/min | 100 req/min · 10k/day | 1k req/min · unlimited |\n| Price | €0 | €29/month | €99/month |\n\n---\n\n## Font Server — `/fonts`\n\nThe backend self-hosts all fonts used across the `abler.tirol` ecosystem. No Google Fonts — DSGVO-konform by design.\n\n| Endpoint | Description |\n|---|---|\n| `GET /fonts/abler-stack.css` | Combined `@font-face` stylesheet for all three font families |\n| `GET /fonts/files/:filename` | Individual woff2 files |\n\n**Usage in any abler.tirol frontend:**\n\n```html\n\u003clink rel=\"preconnect\" href=\"https://api.abler.tirol\" /\u003e\n\u003clink rel=\"stylesheet\" href=\"https://api.abler.tirol/fonts/abler-stack.css\" /\u003e\n```\n\n**Font stack:**\n\n| Family | Weights | Role |\n|---|---|---|\n| `DM Serif Display` | 400 normal + italic | Headlines, Hero-Titel |\n| `Inter` | 300 · 400 · 500 · 600 | Body, UI |\n| `DM Mono` | 400 · 500 | Code, Tags, Badges |\n\nFont 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.\n\n---\n\n## Deployment\n\n### Domain Routing\n\nBoth `hub.abler.tirol` and `api.abler.tirol` are routed identically via Traefik:\n\n- `/api/*` and `/fonts/*` → backend (port 3000)\n- all other paths → frontend (port 80)\n\nSee `docker-compose.yml` for the exact Traefik label configuration.\n\n---\n\n### Docker\n\n```bash\n# Build backend\ndocker build -f dockerfiles/Dockerfile.backend -t simonapi-backend .\n\n# Run with SQLite (persistent volume)\ndocker run -d --name simonapi-backend -p 3000:3000 \\\n  -e NODE_ENV=production \\\n  -e TYPEORM_DB=/data/db.sqlite \\\n  -v $(pwd)/data:/data \\\n  simonapi-backend\n\n# Run with PostgreSQL\ndocker run -d --name simonapi-backend -p 3000:3000 \\\n  -e NODE_ENV=production \\\n  -e TYPEORM_URL=postgres://user:pass@host:5432/db \\\n  simonapi-backend\n```\n\n### Environment Variables\n\n| Variable | Default | Description |\n|---|---|---|\n| `NODE_ENV` | `development` | `production` enables optimisations |\n| `TYPEORM_DB` | `./data/db.sqlite` | SQLite file path |\n| `TYPEORM_URL` | — | PostgreSQL connection URL (overrides SQLite) |\n| `DATA_DIR` | `./data/signpacks` | Signpack file storage path |\n| `TOKEN_LENGTH` | `32` | Signpack token length |\n| `FILE_MAX_BYTES` | `26214400` | Max upload size (25 MB) |\n| `PURGE_CRON` | `0 * * * *` | Cron for expired signpack cleanup |\n\n---\n\n## Tests\n\n```bash\n# All backend tests\ncd apps/server \u0026\u0026 npx jest --no-coverage\n\n# Single suite\ncd apps/server \u0026\u0026 npx jest src/app/barcode/sscc.spec.ts --no-coverage\n```\n\nTest suites: `gs1-validate` · `gs1-digital-link` · `gs1-error` · `sscc` (100 tests: buildSscc / validateSscc / validateGs1Prefix / mod10CheckDigit)\n\n---\n\n## Project Structure\n\n```\napps/\n  server/                   # NestJS backend\n    src/app/\n      barcode/              # GS1 + SSCC + standard barcodes\n      qr/                   # QR code generator\n      watermark/            # Image watermarking\n      lock/                 # Swipe-to-open locks\n      signpack/             # Document signing workflow\n      utils/                # Dev utilities\n      metrics/              # Usage metrics \u0026 stats\n  simonapi/                 # Angular frontend\n    src/app/\n      features/\n        home/               # Landing page\n        barcode/            # Barcode + GS1 + SSCC generator UI\n        qr/                 # QR generator UI\n        watermark/          # Watermark UI\n        lock/               # Lock admin + public UI\n        stats/              # Stats dashboard\n        dev-utils/          # Dev utilities UI\n      layout/               # Navbar, footer, cookie banner\n```\n\n---\n\n## Fair Use\n\nFree 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.\n\nFor commercial integrations or SLA requirements please get in touch: **simon@abler.tirol**\n\n---\n\n## Privacy \u0026 Cookies\n\nThe 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.\n\n---\n\nSwagger UI: **https://api.abler.tirol/api** (also reachable at `https://hub.abler.tirol/api`)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsimonabler%2Fsimonapi","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsimonabler%2Fsimonapi","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsimonabler%2Fsimonapi/lists"}