Skip to content

Releases

v2.4.0

Choose a tag to compare

@SatangBudsai SatangBudsai released this 20 Oct 06:15

Added

  • Sync and async code examples on the template page. The Code tab of the Word/PDF and Excel template pages now
    has two modes: Sync — wait for the file (the call answers with a download link; the examples also download
    the file) and Async + webhook (step 1 queues the job with your webhook URL, step 2 is a ready-made webhook
    receiver for Node.js/Express and ASP.NET Core with signature check, plus a signed test call for any language).
    Examples are built from the template's current test values in cURL, Node.js and .NET.
  • Docs: "Sync vs async & webhooks". When to use which, the full async flow, the webhook body, retry rules and
    receiver examples.

Docker: docker pull qorstack/report:2.4.0 · .NET SDK: dotnet add package Qorstack.Report.Sdk --version 2.4.0 · Node SDK: npm i qorstack-report-sdk@2.4.0

v2.3.2

Choose a tag to compare

@SatangBudsai SatangBudsai released this 20 Oct 05:42

Changed

  • Images and webhooks may point anywhere. The backend no longer refuses private / internal addresses for image
    URLs, QR logos and webhooks, and no longer rejects minio: paths outside the template's own builder assets.
    IMAGE_FETCH_ALLOW_PRIVATE_NETWORKS and WEBHOOK_ALLOW_PRIVATE_NETWORKS were removed (no longer read).
    Only http/https URLs, size and time limits still apply.

Fixed

  • Rendering failed with 400 when an image URL was a presigned link to another app's bucket on the same S3 server
    (for example a signature image an API caller keeps in its own MinIO bucket): the link was turned into a storage
    path of ours and then rejected. Such links now stay ordinary URLs and are downloaded.

Docker: docker pull qorstack/report:2.3.2 · .NET SDK: dotnet add package Qorstack.Report.Sdk --version 2.3.2 · Node SDK: npm i qorstack-report-sdk@2.3.2

v2.3.1

Choose a tag to compare

@SatangBudsai SatangBudsai released this 19 Oct 17:10

Fixed

  • Project import preview (POST /api/projects/{id}/import?dryRun=true) returned 400 in 2.3.0: the new optional
    stream query flag was declared as required. A test now checks every endpoint for value-type query parameters
    that are required by accident.

Docker: docker pull qorstack/report:2.3.1 · .NET SDK: dotnet add package Qorstack.Report.Sdk --version 2.3.1 · Node SDK: npm i qorstack-report-sdk@2.3.1

v2.3.0

Choose a tag to compare

@SatangBudsai SatangBudsai released this 19 Oct 15:25

Added

  • Live import progress (Pro). Importing a project now shows every font and template as it is imported, with a
    progress bar and the reason for anything that failed, then a summary with Go to Templates.
    API: POST /api/projects/{id}/import?dryRun=false&stream=true streams application/x-ndjson progress lines.
  • Regenerate previews. Re-render template previews with the fonts installed now — after uploading or importing
    fonts, previews made before the renderer had them show a substitute. Templates page: Regenerate previews (every
    template of the project, with progress); template editor: ⋯ → Regenerate preview;
    API: POST /api/templates/{key}/preview. After an import that added fonts, the Templates page offers it.

Changed

  • Import commits each font and template on its own, fonts first. A template that fails no longer undoes the
    others; it is listed with its reason, and importing the same file again picks up only what is missing.

Fixed

  • Copy buttons (API key on Create project, API keys page, template keys, code blocks) did nothing on installs
    opened over plain HTTP (a NodePort, an intranet IP); they fall back to a method that works without HTTPS.

Docker: docker pull qorstack/report:2.3.0 · .NET SDK: dotnet add package Qorstack.Report.Sdk --version 2.3.0 · Node SDK: npm i qorstack-report-sdk@2.3.0

v2.2.2

Choose a tag to compare

@SatangBudsai SatangBudsai released this 19 Oct 13:46

Added

  • STORAGE_PUBLIC_ENDPOINT (optional). Set it to the address your storage already serves to browsers
    (e.g. https://storage.example.com) and download links become S3 presigned URLs for that address, as in
    v1: browsers download from storage directly and PUBLIC_URL is not needed for downloads. Unset (default),
    links stay {PUBLIC_URL}/api/files/{token} and storage needs no public address. MINIO_PUBLIC_ENDPOINT /
    Minio__PublicEndpoint from v1 are read as fallbacks.

Changed

  • Image URLs that point at this deployment's own storage (STORAGE_ENDPOINT / STORAGE_PUBLIC_ENDPOINT)
    are fetched even on a private network, so IMAGE_FETCH_ALLOW_PRIVATE_NETWORKS is only needed for images on
    other intranet servers.
  • Enterprise source builds run Pro without LICENSE=pro (set LICENSE=oss to turn it off). A license file
    (PRO_LICENSE_FILE) is still validated as before; the published images are unchanged and still need one.

Fixed

  • The docs page had an empty band under the navbar.

Docker: docker pull qorstack/report:2.2.2 · .NET SDK: dotnet add package Qorstack.Report.Sdk --version 2.2.2 · Node SDK: npm i qorstack-report-sdk@2.2.2

v2.2.1

Choose a tag to compare

@SatangBudsai SatangBudsai released this 18 Oct 21:02

Fixed

  • Sign-in over plain HTTP (a Kubernetes NodePort, an intranet IP). Sign-in cookies were always Secure
    outside localhost, so browsers dropped them on http:// and the web UI could not stay signed in. The
    cookies now follow the address in the browser (from the request's Origin / Referer): Secure on
    https://, plain + SameSite=Lax on http:// — no setting needed, behind any number of proxies.
    AUTH_COOKIE_SECURE=true|false (new, optional) forces either way.
  • The template editor's preview head squeezed the Template / Result tabs under the Auto-preview switch on
    1920px screens. Its labels now follow the panel's own width (CSS container queries), and a very narrow
    panel wraps the actions instead of overlapping.
  • The docs navbar is as wide as the docs columns below it.

Docker: docker pull qorstack/report:2.2.1 · .NET SDK: dotnet add package Qorstack.Report.Sdk --version 2.2.1 · Node SDK: npm i qorstack-report-sdk@2.2.1

v2.2.0

Choose a tag to compare

@SatangBudsai SatangBudsai released this 18 Oct 20:35

Added

  • Import shows what differs before you confirm (Pro). The review lists every template as New, Changed,
    No changes or Skipped, with what changed: template file, fields added / removed, name, saved test data,
    and a note when only the name matches a template with another key. Identical templates are folded away.
    The API's dry run (POST /api/projects/{id}/import?dryRun=true) returns the same detail in
    templates[].changes and templates[].unchanged.
  • Export & import has its own page in the project sidebar (/project/{id}/transfer), apart from
    Project settings.

Changed

  • Import compares content, not version numbers. Version numbers are counted per deployment, so:
    identical templates are skipped as "no changes" whatever their numbers; a changed template with the same
    number is imported as the next free version (it used to be skipped as "already up to date"). A newer
    version in the target is still never overwritten. The template name is updated on import.
  • Easier-to-read dashboard. Brighter secondary text and placeholders, quieter dividers, fields without
    a border until hover / focus, sentence-case page titles, filled tabs and badges. Corners stay square.
  • Template editor. Version and Replace file stay one click away; Rename, Download original and Delete
    moved into the ⋯ menu. The preview head is one row (Template / Result, Auto-preview, Download, Generate).

Fixed

  • Opening a template from the Templates list without reloading asked the API for the project id and showed
    "not found" until refresh.
  • The editor header no longer overlaps the template name on laptop-width screens.
  • The Fonts page had two scrollbars.

Docker: docker pull qorstack/report:2.2.0 · .NET SDK: dotnet add package Qorstack.Report.Sdk --version 2.2.0 · Node SDK: npm i qorstack-report-sdk@2.2.0

v2.1.1-rc.1

v2.1.1-rc.1 Pre-release
Pre-release

Choose a tag to compare

@SatangBudsai SatangBudsai released this 18 Oct 08:01

Pre-release that exercises the automated release pipeline (Docker Hub, NuGet, npm trusted publishing).
No product changes from 2.1.0 — stay on 2.1.0 / latest.


Docker: docker pull qorstack/report:2.1.1-rc.1 · .NET SDK: dotnet add package Qorstack.Report.Sdk --version 2.1.1-rc.1 · Node SDK: npm i qorstack-report-sdk@2.1.1-rc.1

v2.1.0

Choose a tag to compare

@SatangBudsai SatangBudsai released this 17 Oct 21:45

Changed

  • Live Preview is free. Auto-render while you edit a template now works without a Pro license.
    /features still returns livePreview (now always true).

Fixed

  • og:site_name is the product name (was "Qorstack Report Engine").

Docker: docker pull qorstack/report:2.1.0 · .NET SDK: dotnet add package Qorstack.Report.Sdk · Node SDK: npm i qorstack-report-sdk

v2.0.0

Choose a tag to compare

@SatangBudsai SatangBudsai released this 17 Oct 21:45

Upgrade guide: README › Upgrading to v2.
Staying on v1? Pin image tags to 1.1.0 (the last v1 release).

⚠ BREAKING

  • One image, one port; the API moved under /api. Web UI and API ship as one image,
    qorstack/report (root Dockerfile), on port 8080: web UI at /, API at /api
    (e.g. POST /api/render/word/template, downloads at /api/files/{token}, health at /api/health,
    reference at /api/docs). qorstack/report-api / qorstack/report-web and port 3000 are gone.
    Compose has one app service; API_URL, FRONTEND_PORT, REPORT_API_TAG/REPORT_WEB_TAG and
    NEXT_PUBLIC_SERVICE were removed — set PUBLIC_URL (and APP_PORT). CORS_ORIGINS is now only
    for other origins. API clients add /api to the base URL; SDK 1.0 and the MCP server do it
    automatically. GitHub sign-in config moved to the backend (GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET,
    GOOGLE_CLIENT_ID), and the code exchange now runs server-side.

  • Storage uses one bucket on any S3 server. STORAGE_BUCKET (default qorstack-report)
    with folders template/, generated-report/ (incl. temp-download/, deleted after 1 day)
    and fonts/ replaces the templates / reports / fonts buckets. MINIO_BUCKET_TEMPLATE,
    MINIO_BUCKET_REPORT, Minio:TemplateBucket and Minio:ReportBucket were removed.
    Copy existing data once with selfhost/migrate-storage.sh keep-minio or to-rustfs (rclone);
    the backend logs a warning when old-bucket data was not migrated.

  • RustFS is the default storage. The self-host compose starts RustFS (Apache-2.0) with no
    extra settings; MinIO stays available through the docker-compose.minio.yml override (image pinned
    from quay.io — MinIO left Docker Hub), and docker-compose.external-s3.yml points STORAGE_* at
    any external S3. Unreachable storage now gives a clear 503 and an unhealthy storage health check. Settings are STORAGE_* / Storage:*; v1 MINIO_* / Minio:* still work as fallbacks.

  • Downloads go through the API. Every link (downloadUrl, previews, fonts, history) is now
    {PUBLIC_API_URL}/files/{token} — signed, time-limited, streamed by the backend with Range
    support — instead of a presigned storage URL. Storage no longer needs a public address:
    MINIO_PUBLIC_ENDPOINT is no longer needed and port 9000 is not published.

  • Postgres is no longer published on the host (port 5432), so it can't clash with a local
    Postgres. Tools that connected from the host (DBeaver, psql) need the commented
    127.0.0.1:${DB_HOST_PORT:-5432}:5432 line under postgres in docker-compose.yml, or docker exec.

  • font-syncer uses rclone (rclone/rclone) instead of the MinIO client, so it works with
    any S3 server.

  • Render request shape. Every v1 alternative form was removed and now returns 400
    naming the field and the expected shape:

    replace:              { "customer": "ACME" }            → [{ "key": "customer", "value": "ACME" }]
    image/qrcode/barcode: { "logo": { "src": "…" } }        → [{ "key": "logo", "src": "…" }]
    table rows:           [{ "item": "A", "qty": 2 }]       → "columns": ["item", "qty"], "rows": [["A", 2]]
    generateTotals / numberFormat: { "amount": "sum" }      → [{ "key": "amount", "value": "SUM" }]
    sort:                 "date desc"                       → [{ "field": "date", "direction": "desc" }]
    verticalMerge / collapse: "group"                       → ["group"]
    

    Row length must match columns; sort / merge / totals must name existing columns.

  • POST /render/excel/template is Excel in, Excel out — fileType and the PDF
    options were removed (always .xlsx).

  • Image URLs in requests may no longer point at private or internal addresses
    (localhost, 10.x, 172.16–31.x, 192.168.x, 100.64–127.x, cloud metadata). Set
    IMAGE_FETCH_ALLOW_PRIVATE_NETWORKS=true for intranet image servers. minio: paths are
    accepted only for the template's own builder assets.

  • zipOutput is a Pro feature (it was documented as Pro but never enforced); without Pro it
    now returns 403.

  • Website is dark only (the light theme and theme toggle were removed).

  • SDK 1.0.0 (Node / .NET) uses the new types. Stay on API 1.1.0 / SDK 0.x if you still
    send the old forms.

Added

  • Project export & import (Pro): Project settings → Export & import. Export downloads one ZIP
    (manifest + latest active version of every template, its test data and builder images, uploaded
    fonts); import shows a preview (create / update / skip) and then creates new templates with the same
    key and adds newer versions — equal or older versions are skipped. GET /projects/{id}/export,
    POST /projects/{id}/import?dryRun=true|false; size limit PROJECT_IMPORT_MAX_MB (200).
  • Request limits (env, 0 = unlimited): RENDER_MAX_BODY_MB (30), RENDER_MAX_TABLE_ROWS
    (50,000 per table), RENDER_MAX_ITEMS_PER_FIELD (1,000 per replace / image / qrcode / barcode).
    Over a limit → 400 naming the field and the setting.
  • Validation: all errors in one 400, keyed by JSON path (table[0].rows[12], replace[3].key).
  • templateVersion (optional) renders a stored older version; unknown version → 404.
  • Async renders (Pro): "async": true returns 202 { jobId, status: "pending", statusUrl };
    poll GET /render/jobs/{id} (fresh downloadUrl on success). Optional webhookUrl receives
    POST { jobId, status, downloadUrl?, expiresIn?, error? }, signed with
    X-Qorstack-Signature: sha256=<HMAC> when WEBHOOK_SIGNING_SECRET is set; delivery state is stored
    on the job (migration 006_webhook-delivery.sql, 3 tries: now, +10 s, +60 s), so retries survive restarts.
    The queue lives in PostgreSQL (report_jobs, migration 005_render-queue.sql): jobs survive restarts,
    several backend instances can share it (FOR UPDATE SKIP LOCKED, LISTEN/NOTIFY), a crashed worker's job
    is picked up again when its lease ends, and infrastructure errors are retried (10 s, 60 s; 3 tries).
    Settings: RENDER_ASYNC_WORKERS (2), RENDER_JOB_LEASE_SECONDS (300), RENDER_POLL_SECONDS (5),
    RENDER_MAX_PENDING_JOBS_PER_USER (100 → 429).
  • Website: new Press design, new logo/icons and a generated link-preview image.
  • selfhost/migrate-storage.sh, fully commented .env.example files, init.sh / migration script
    downloadable from /selfhost/.

Fixed

  • Public Excel API calls no longer overwrite the template's saved builder payload.
  • Server-side request forgery through image URLs and cross-template minio: paths.
  • System.Security.Cryptography.Pkcs pinned to 10.0.13 (CVE-2026-84008, CVE-2026-70162; it comes in
    through Microsoft.Data.SqlClient). Image builds failed restore on these advisories.
  • Startup: database migrations finish before anything else starts (render worker, storage, HTTP),
    so a fresh upgrade no longer logs column ... does not exist; a failed migration stops startup.
  • Project pages follow the project in the URL: a stale or deleted project link shows
    "Project not found" instead of another project (Settings › Delete acted on the wrong one).
  • Account: a failed password change no longer reports success; a failed GitHub sign-in shows an
    error instead of spinning; the system admin is told their login comes from ADMIN_EMAIL /
    ADMIN_PASSWORD; Google/GitHub buttons show only when configured.
  • Editor: test values save automatically (with a leave-page warning while unsaved); Rename no
    longer sends inline images in the URL; "Make current" asks first; Generate opens the preview on
    mobile; Excel tables get the same summary and filter as PDF.
  • Excel editor no longer runs a hidden render each time it opens (it added a History entry and
    its result was never used).
  • History and Templates filter and search on the server, so rows beyond the first page are found;
    live polling no longer duplicates rows.
  • Template gallery shows each template rendered with its sample data
    (GET /templates/gallery/{id}/thumbnail; regenerate with python gallery/_thumbnails.py). The gallery
    samples use the v2 request shape, so they can be sent to the API as-is, and the sales report's total comes from generateTotals instead of an unsupported {{table_sum:...}} marker.
  • Self-host: verify.sh reads APP_PORT; docs and the agent prompt use the app service, bash verify.sh and
    docker compose up -d; one Free vs Pro list across the site; SDK examples include /api.

Docker: docker pull qorstack/report:2.0.0 · .NET SDK: dotnet add package Qorstack.Report.Sdk · Node SDK: npm i qorstack-report-sdk