Compare commits

..

8 Commits

Author SHA1 Message Date
e5af5cb2a3 Pipeline notification to Mattermost
All checks were successful
ci/woodpecker/push/ci Pipeline was successful
ci/woodpecker/push/deploy Pipeline was successful
2026-02-12 02:09:25 -03:00
a1cecd6de4 Remove mail server settings from docker-compose
All checks were successful
ci/woodpecker/push/ci Pipeline was successful
ci/woodpecker/push/deploy Pipeline was successful
2026-02-10 16:55:48 -03:00
053aa97983 Add the link.mifi.medomain back in...
All checks were successful
ci/woodpecker/push/ci Pipeline was successful
ci/woodpecker/push/deploy Pipeline was successful
2026-02-10 12:25:32 -03:00
7d86903565 Tweaks to ENV vars
All checks were successful
ci/woodpecker/push/ci Pipeline was successful
ci/woodpecker/push/deploy Pipeline was successful
2026-02-09 13:22:43 -03:00
32b3102070 Attempts to solve issue
All checks were successful
ci/woodpecker/push/ci Pipeline was successful
ci/woodpecker/push/deploy Pipeline was successful
2026-02-08 01:08:24 -03:00
1dc6e8b4f9 Test fixes
All checks were successful
ci/woodpecker/push/ci Pipeline was successful
ci/woodpecker/push/deploy Pipeline was successful
2026-02-07 17:31:42 -03:00
1299fc4dd3 Fixes for API logo issues
Some checks failed
ci/woodpecker/push/ci Pipeline failed
ci/woodpecker/push/deploy unknown status
2026-02-07 17:27:36 -03:00
309b0c618f Ugh... All over some QR's
All checks were successful
ci/woodpecker/push/ci Pipeline was successful
ci/woodpecker/push/deploy Pipeline was successful
2026-02-07 16:54:49 -03:00
11 changed files with 150 additions and 74 deletions

View File

@@ -22,6 +22,24 @@ steps:
depends_on: depends_on:
- Install - Install
- name: Send Prettier Status Notification (failure)
image: curlimages/curl
environment:
MATTERMOST_BOT_ACCESS_TOKEN:
from_secret: mattermost_bot_access_token
MATTERMOST_CHANNEL_ID:
from_secret: mattermost_tests_channel_id
MATTERMOST_POST_API_URL:
from_secret: mattermost_post_api_url
commands:
- |
BODY=$(printf '{"channel_id":"%s","message":"[%s - Build #%s] Prettier failure 💩"}' "$MATTERMOST_CHANNEL_ID" "$CI_REPO" "$CI_PIPELINE_NUMBER")
curl -sS -X POST -H "Content-Type: application/json" -d "$BODY" -H "Authorization: Bearer $MATTERMOST_BOT_ACCESS_TOKEN" $MATTERMOST_POST_API_URL
depends_on:
- Prettier
when:
- status: [failure]
- name: Lint - name: Lint
image: node:22-bookworm-slim image: node:22-bookworm-slim
commands: commands:
@@ -38,6 +56,24 @@ steps:
depends_on: depends_on:
- Lint - Lint
- name: Send Lint Status Notification (failure)
image: curlimages/curl
environment:
MATTERMOST_BOT_ACCESS_TOKEN:
from_secret: mattermost_bot_access_token
MATTERMOST_CHANNEL_ID:
from_secret: mattermost_tests_channel_id
MATTERMOST_POST_API_URL:
from_secret: mattermost_post_api_url
commands:
- |
BODY=$(printf '{"channel_id":"%s","message":"[%s - Build #%s] Lint failure 💩"}' "$MATTERMOST_CHANNEL_ID" "$CI_REPO" "$CI_PIPELINE_NUMBER")
curl -sS -X POST -H "Content-Type: application/json" -d "$BODY" -H "Authorization: Bearer $MATTERMOST_BOT_ACCESS_TOKEN" $MATTERMOST_POST_API_URL
depends_on:
- Lint
when:
- status: [failure]
- name: Build - name: Build
image: node:22-bookworm-slim image: node:22-bookworm-slim
commands: commands:
@@ -46,23 +82,41 @@ steps:
depends_on: depends_on:
- Tests & Coverage - Tests & Coverage
# build-full: - name: Send Build Status Notification (failure)
# image: node:22-bookworm-slim image: curlimages/curl
# commands: environment:
# - apt-get update MATTERMOST_BOT_ACCESS_TOKEN:
# - apt-get install -y --no-install-recommends ca-certificates libasound2 libatk-bridge2.0-0 libatk1.0-0 libcups2 libdrm2 libgbm1 libgtk-3-0 libnss3 libxcomposite1 libxdamage1 libxfixes3 libxkbcommon0 libxrandr2 from_secret: mattermost_bot_access_token
# - rm -rf /var/lib/apt/lists/* MATTERMOST_CHANNEL_ID:
# - corepack enable && corepack prepare pnpm@latest --activate from_secret: mattermost_tests_channel_id
# - pnpm run critical-css:install MATTERMOST_POST_API_URL:
# - pnpm run build:full from_secret: mattermost_post_api_url
# depends_on: commands:
# - build - |
BODY=$(printf '{"channel_id":"%s","message":"[%s - Build #%s] Build failure 💩"}' "$MATTERMOST_CHANNEL_ID" "$CI_REPO" "$CI_PIPELINE_NUMBER")
curl -sS -X POST -H "Content-Type: application/json" -d "$BODY" -H "Authorization: Bearer $MATTERMOST_BOT_ACCESS_TOKEN" $MATTERMOST_POST_API_URL
depends_on:
- Build
when:
- status: [failure]
# e2e: - name: Send CI Pipeline Status Notification (success)
# image: node:22-bookworm-slim image: curlimages/curl
# commands: environment:
# - corepack enable && corepack prepare pnpm@latest --activate MATTERMOST_BOT_ACCESS_TOKEN:
# - pnpm exec playwright install chromium --with-deps from_secret: mattermost_bot_access_token
# - pnpm run test:e2e MATTERMOST_CHANNEL_ID:
# depends_on: from_secret: mattermost_tests_channel_id
# - build MATTERMOST_POST_API_URL:
from_secret: mattermost_post_api_url
commands:
- |
BODY=$(printf '{"channel_id":"%s","message":"[%s - Build #%s] CI pipeline success 🎉"}' "$MATTERMOST_CHANNEL_ID" "$CI_REPO" "$CI_PIPELINE_NUMBER")
curl -sS -X POST -H "Content-Type: application/json" -d "$BODY" -H "Authorization: Bearer $MATTERMOST_BOT_ACCESS_TOKEN" $MATTERMOST_POST_API_URL
depends_on:
- Install
- Lint
- Prettier
- Build
when:
- status: [success]

View File

@@ -55,12 +55,16 @@ steps:
- name: Send Build Status Notification (success) - name: Send Build Status Notification (success)
image: curlimages/curl image: curlimages/curl
environment: environment:
DISCORD_WEBHOOK_URL: MATTERMOST_BOT_ACCESS_TOKEN:
from_secret: discord_webhook_url from_secret: mattermost_bot_access_token
MATTERMOST_CHANNEL_ID:
from_secret: mattermost_pushes_channel_id
MATTERMOST_POST_API_URL:
from_secret: mattermost_post_api_url
commands: commands:
- | - |
BODY=$(printf '{"username":"WoodpeckerBot","content":"[%s - Build #%s] Docker images build success 🎉"}' "$CI_REPO" "$CI_PIPELINE_NUMBER") BODY=$(printf '{"channel_id":"%s","message":"[%s - Build #%s] Docker images build success 🎉"}' "$MATTERMOST_CHANNEL_ID" "$CI_REPO" "$CI_PIPELINE_NUMBER")
curl -sS -X POST -H "Content-Type: application/json" -d "$BODY" "$DISCORD_WEBHOOK_URL" curl -sS -X POST -H "Content-Type: application/json" -d "$BODY" -H "Authorization: Bearer $MATTERMOST_BOT_ACCESS_TOKEN" $MATTERMOST_POST_API_URL
depends_on: depends_on:
- Docker image build (qr-api + qr-web) - Docker image build (qr-api + qr-web)
when: when:
@@ -69,12 +73,16 @@ steps:
- name: Send Build Status Notification (failure) - name: Send Build Status Notification (failure)
image: curlimages/curl image: curlimages/curl
environment: environment:
DISCORD_WEBHOOK_URL: MATTERMOST_BOT_ACCESS_TOKEN:
from_secret: discord_webhook_url from_secret: mattermost_bot_access_token
MATTERMOST_CHANNEL_ID:
from_secret: mattermost_pushes_channel_id
MATTERMOST_POST_API_URL:
from_secret: mattermost_post_api_url
commands: commands:
- | - |
BODY=$(printf '{"username":"WoodpeckerBot","content":"[%s - Build #%s] Docker images build failure 💩"}' "$CI_REPO" "$CI_PIPELINE_NUMBER") BODY=$(printf '{"channel_id":"%s","message":"[%s - Build #%s] Docker images build failure 💩"}' "$MATTERMOST_CHANNEL_ID" "$CI_REPO" "$CI_PIPELINE_NUMBER")
curl -sS -X POST -H "Content-Type: application/json" -d "$BODY" "$DISCORD_WEBHOOK_URL" curl -sS -X POST -H "Content-Type: application/json" -d "$BODY" -H "Authorization: Bearer $MATTERMOST_BOT_ACCESS_TOKEN" $MATTERMOST_POST_API_URL
depends_on: depends_on:
- Docker image build (qr-api + qr-web) - Docker image build (qr-api + qr-web)
when: when:
@@ -103,12 +111,16 @@ steps:
- name: Send Deploy Status Notification (success) - name: Send Deploy Status Notification (success)
image: curlimages/curl image: curlimages/curl
environment: environment:
DISCORD_WEBHOOK_URL: MATTERMOST_BOT_ACCESS_TOKEN:
from_secret: discord_webhook_url from_secret: mattermost_bot_access_token
MATTERMOST_CHANNEL_ID:
from_secret: mattermost_pushes_channel_id
MATTERMOST_POST_API_URL:
from_secret: mattermost_post_api_url
commands: commands:
- | - |
BODY=$(printf '{"username":"WoodpeckerBot","content":"[%s - Build #%s] Production Deploy success 🎉"}' "$CI_REPO" "$CI_PIPELINE_NUMBER") BODY=$(printf '{"channel_id":"%s","message":"[%s - Build #%s] Production Deploy success 🎉"}' "$MATTERMOST_CHANNEL_ID" "$CI_REPO" "$CI_PIPELINE_NUMBER")
curl -sS -X POST -H "Content-Type: application/json" -d "$BODY" "$DISCORD_WEBHOOK_URL" curl -sS -X POST -H "Content-Type: application/json" -d "$BODY" -H "Authorization: Bearer $MATTERMOST_BOT_ACCESS_TOKEN" $MATTERMOST_POST_API_URL
depends_on: depends_on:
- Trigger Portainer stack redeploy - Trigger Portainer stack redeploy
when: when:
@@ -117,12 +129,16 @@ steps:
- name: Send Deploy Status Notification (failure) - name: Send Deploy Status Notification (failure)
image: curlimages/curl image: curlimages/curl
environment: environment:
DISCORD_WEBHOOK_URL: MATTERMOST_BOT_ACCESS_TOKEN:
from_secret: discord_webhook_url from_secret: mattermost_bot_access_token
MATTERMOST_CHANNEL_ID:
from_secret: mattermost_pushes_channel_id
MATTERMOST_POST_API_URL:
from_secret: mattermost_post_api_url
commands: commands:
- | - |
BODY=$(printf '{"username":"WoodpeckerBot","content":"[%s - Build #%s] Production Deploy failure 💩"}' "$CI_REPO" "$CI_PIPELINE_NUMBER") BODY=$(printf '{"channel_id":"%s","message":"[%s - Build #%s] Production Deploy failure 💩"}' "$MATTERMOST_CHANNEL_ID" "$CI_REPO" "$CI_PIPELINE_NUMBER")
curl -sS -X POST -H "Content-Type: application/json" -d "$BODY" "$DISCORD_WEBHOOK_URL" curl -sS -X POST -H "Content-Type: application/json" -d "$BODY" -H "Authorization: Bearer $MATTERMOST_BOT_ACCESS_TOKEN" $MATTERMOST_POST_API_URL
depends_on: depends_on:
- Trigger Portainer stack redeploy - Trigger Portainer stack redeploy
when: when:

View File

@@ -1,7 +1,6 @@
# Portainer stack: registry-based images (no build). Use with CI/CD webhook redeploy. # Portainer stack: registry-based images (no build). Use with CI/CD webhook redeploy.
# Set in Portainer stack env (or .env): REGISTRY, IMAGE_TAG (defaults below). # Set in Portainer stack env (or .env): REGISTRY, IMAGE_TAG (defaults below).
# Images: ${REGISTRY}/mifi-holdings/shorty-qr-api:${IMAGE_TAG}, shorty-qr-web:${IMAGE_TAG} # Images: ${REGISTRY}/mifi-holdings/shorty-qr-api:${IMAGE_TAG}, shorty-qr-web:${IMAGE_TAG}
services: services:
kutt_db: kutt_db:
image: postgres:16-alpine image: postgres:16-alpine
@@ -64,24 +63,27 @@ services:
start_period: 40s start_period: 40s
environment: environment:
ADMIN_EMAILS: ${ADMIN_EMAILS:?Set ADMIN_EMAILS} ADMIN_EMAILS: ${ADMIN_EMAILS:?Set ADMIN_EMAILS}
DEFAULT_DOMAIN: mifi.me CUSTOM_DOMAIN_USE_HTTPS: 'true'
DEFAULT_DOMAIN: ${DEFAULT_DOMAIN:?Set DEFAULT_DOMAIN}
DB_CLIENT: pg DB_CLIENT: pg
DB_HOST: kutt_db DB_HOST: kutt_db
DB_PORT: '5432' DB_PORT: '5432'
DB_USER: ${DB_USER:-kutt} DB_USER: ${DB_USER:-kutt}
DB_PASSWORD: ${DB_PASSWORD:?Set DB_PASSWORD} DB_PASSWORD: ${DB_PASSWORD:?Set DB_PASSWORD}
DB_POOL_MIN: '2'
DB_POOL_MAX: '10'
DB_NAME: ${DB_NAME:-kutt} DB_NAME: ${DB_NAME:-kutt}
DISALLOW_ANONYMOUS_LINKS: 'true' DISALLOW_ANONYMOUS_LINKS: 'true'
DISALLOW_REGISTRATION: 'true' DISALLOW_REGISTRATION: 'true'
LINK_LENGTH: 6 LINK_LENGTH: 6
JWT_SECRET: ${JWT_SECRET:?Set JWT_SECRET} JWT_SECRET: ${JWT_SECRET:?Set JWT_SECRET}
MAIL_ENABLED: 'true' MAIL_ENABLED: 'true'
MAIL_HOST: 'mail.mifi.holdings' MAIL_HOST: ${MAIL_HOST:?Set MAIL_HOST}
MAIL_PORT: '465' MAIL_PORT: ${MAIL_PORT:?Set MAIL_PORT}
MAIL_SECURE: 'true' MAIL_SECURE: 'true'
MAIL_USER: 'mailbot@mifi.ventures' MAIL_USER: ${MAIL_USER:?Set MAIL_USER}
MAIL_FROM: 'mifi Holdings Shorty <noreply@mifi.holdings>' MAIL_FROM: ${MAIL_FROM:?Set MAIL_FROM}
MAIL_PASSWORD: '${SMTP_PASSWORD:?Set SMTP_PASSWORD}' MAIL_PASSWORD: '${MAIL_PASSWORD:?Set MAIL_PASSWORD}'
NODE_ENV: production NODE_ENV: production
# OIDC_ENABLED: 'true' # OIDC_ENABLED: 'true'
# OIDC_ISSUER: 'https://git.mifi.dev' # OIDC_ISSUER: 'https://git.mifi.dev'
@@ -90,18 +92,24 @@ services:
REDIS_ENABLED: 'true' REDIS_ENABLED: 'true'
REDIS_HOST: kutt_redis REDIS_HOST: kutt_redis
REDIS_PORT: '6379' REDIS_PORT: '6379'
SITE_DOMAIN: link.mifi.me SERVER_CNAME_ADDRESS: ${SERVER_CNAME_ADDRESS:-${DEFAULT_DOMAIN}}
SITE_NAME: 'mifi Shorty' SITE_NAME: ${SITE_NAME:-Kutt}
labels: labels:
- 'traefik.enable=true' - 'traefik.enable=true'
- 'docker.network=marina-net' - 'docker.network=marina-net'
- 'traefik.http.routers.kutt-mifi.rule=Host(`mifi.me`)' - 'traefik.http.routers.kutt-mifi.rule=Host(`${DEFAULT_DOMAIN}`)'
- 'traefik.http.routers.kutt-mifi.entrypoints=websecure' - 'traefik.http.routers.kutt-mifi.entrypoints=websecure'
- 'traefik.http.routers.kutt-mifi.tls.certresolver=letsencrypt' - 'traefik.http.routers.kutt-mifi.tls.certresolver=letsencrypt'
- 'traefik.http.routers.kutt-mifi.service=kutt-short' - 'traefik.http.routers.kutt-mifi.service=kutt-short'
- 'traefik.http.services.kutt-short.loadbalancer.server.port=3000' - 'traefik.http.services.kutt-short.loadbalancer.server.port=3000'
# Backend timeout: use transport from file provider (see traefik-kutt-timeout.example.yml).
- 'traefik.http.services.kutt-short.loadbalancer.serversTransport=kutt-long-timeout@file' - 'traefik.http.services.kutt-short.loadbalancer.serversTransport=kutt-long-timeout@file'
# UI
- 'traefik.http.routers.kutt-mifi-ui.rule=Host(`${SERVER_CNAME_ADDRESS}`)'
- 'traefik.http.routers.kutt-mifi-ui.entrypoints=websecure'
- 'traefik.http.routers.kutt-mifi-ui.tls.certresolver=letsencrypt'
- 'traefik.http.routers.kutt-mifi-ui.service=kutt-short-ui'
- 'traefik.http.services.kutt-short-ui.loadbalancer.server.port=3000'
- 'traefik.http.services.kutt-short-ui.loadbalancer.serversTransport=kutt-long-timeout@file'
qr_api: qr_api:
image: ${REGISTRY:-git.mifi.dev}/mifi-holdings/shorty-qr-api:${IMAGE_TAG:-latest} image: ${REGISTRY:-git.mifi.dev}/mifi-holdings/shorty-qr-api:${IMAGE_TAG:-latest}
@@ -140,7 +148,7 @@ services:
# Use service_started so the stack can deploy even if qr_api is still broken; switch to service_healthy once qr_api image is fixed. # Use service_started so the stack can deploy even if qr_api is still broken; switch to service_healthy once qr_api image is fixed.
depends_on: depends_on:
qr_api: qr_api:
condition: service_started condition: service_healthy
environment: environment:
QR_API_URL: http://qr_api:8080 QR_API_URL: http://qr_api:8080
healthcheck: healthcheck:
@@ -148,7 +156,7 @@ services:
- CMD - CMD
- node - node
- -e - -e
- 'require("http").get("http://0.0.0.0:3000/", {timeout: 5000}, (r) => { r.resume(); process.exit(r.statusCode >= 200 && r.statusCode < 500 ? 0 : 1); }).on("error", () => process.exit(1))' - 'require("http").get("http://localhost:3000/", {timeout: 5000}, (r) => { r.resume(); process.exit(r.statusCode >= 200 && r.statusCode < 500 ? 0 : 1); }).on("error", () => process.exit(1))'
interval: 30s interval: 30s
timeout: 10s timeout: 10s
retries: 3 retries: 3

View File

@@ -76,11 +76,7 @@ services:
- 'traefik.http.routers.kutt-mifi.tls.certresolver=letsencrypt' - 'traefik.http.routers.kutt-mifi.tls.certresolver=letsencrypt'
- 'traefik.http.routers.kutt-mifi.service=kutt-short' - 'traefik.http.routers.kutt-mifi.service=kutt-short'
- 'traefik.http.services.kutt-short.loadbalancer.server.port=3000' - 'traefik.http.services.kutt-short.loadbalancer.server.port=3000'
- 'traefik.http.routers.kutt-link.rule=Host(`link.mifi.me`)' - 'traefik.http.services.kutt-short.loadbalancer.serversTransport=kutt-long-timeout@file'
- 'traefik.http.routers.kutt-link.entrypoints=websecure'
- 'traefik.http.routers.kutt-link.tls.certresolver=letsencrypt'
- 'traefik.http.routers.kutt-link.service=kutt'
- 'traefik.http.services.kutt.loadbalancer.server.port=3000'
qr_api: qr_api:
build: build:
@@ -131,7 +127,7 @@ services:
- CMD - CMD
- node - node
- -e - -e
- 'require("http").get("http://0.0.0.0:3000/", {timeout: 5000}, (r) => { r.resume(); process.exit(r.statusCode >= 200 && r.statusCode < 500 ? 0 : 1); }).on("error", () => process.exit(1))' - 'require("http").get("http://localhost:3000/", {timeout: 5000}, (r) => { r.resume(); process.exit(r.statusCode >= 200 && r.statusCode < 500 ? 0 : 1); }).on("error", () => process.exit(1))'
interval: 30s interval: 30s
timeout: 10s timeout: 10s
retries: 3 retries: 3

View File

@@ -35,7 +35,10 @@ describe('shortenUrl', () => {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'X-API-Key': 'test-key', 'X-API-Key': 'test-key',
}, },
body: JSON.stringify({ target: 'https://example.com' }), body: JSON.stringify({
target: 'https://example.com',
domain: 'mifi.me',
}),
}), }),
); );
}); });
@@ -54,6 +57,7 @@ describe('shortenUrl', () => {
expect.objectContaining({ expect.objectContaining({
body: JSON.stringify({ body: JSON.stringify({
target: 'https://example.com', target: 'https://example.com',
domain: 'mifi.me',
customurl: 'myslug', customurl: 'myslug',
}), }),
}), }),

View File

@@ -18,14 +18,23 @@ export async function shortenUrl(
} }
const base = env.KUTT_BASE_URL.replace(/\/$/, ''); const base = env.KUTT_BASE_URL.replace(/\/$/, '');
const apiKey = env.KUTT_API_KEY.trim();
// Extract domain from SHORT_DOMAIN (e.g., "https://mifi.me" -> "mifi.me")
const domain = env.SHORT_DOMAIN.replace(/^https?:\/\//, '').replace(
/\/$/,
'',
);
const res = await fetch(`${base}/api/v2/links`, { const res = await fetch(`${base}/api/v2/links`, {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'X-API-Key': env.KUTT_API_KEY, 'X-API-Key': apiKey,
}, },
body: JSON.stringify({ body: JSON.stringify({
target: body.targetUrl, target: body.targetUrl,
domain: domain,
...(body.customSlug && { customurl: body.customSlug }), ...(body.customSlug && { customurl: body.customSlug }),
}), }),
}); });

View File

@@ -13,6 +13,7 @@ RUN corepack enable && corepack prepare pnpm@latest --activate
WORKDIR /app WORKDIR /app
ENV NODE_ENV=production ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1 ENV NEXT_TELEMETRY_DISABLED=1
ENV HOSTNAME=0.0.0.0
COPY --from=builder /app/package.json ./ COPY --from=builder /app/package.json ./
COPY --from=builder /app/pnpm-lock.yaml* ./ COPY --from=builder /app/pnpm-lock.yaml* ./
RUN pnpm install --prod RUN pnpm install --prod

View File

@@ -1020,19 +1020,7 @@ export function Editor({ id }: EditorProps) {
updateProject({ recipeJson: JSON.stringify(r) }); updateProject({ recipeJson: JSON.stringify(r) });
}} }}
/> />
<Select {/* Shape select removed - circle shape has rendering issues in qr-code-styling library */}
label="Shape"
data={[
{ value: 'square', label: 'Square' },
{ value: 'circle', label: 'Circle' },
]}
value={recipe.shape ?? 'square'}
onChange={(v) => {
const r = { ...recipe };
r.shape = (v as 'square' | 'circle') ?? 'square';
updateProject({ recipeJson: JSON.stringify(r) });
}}
/>
<Group grow> <Group grow>
<NumberInput <NumberInput
label="Margin" label="Margin"

View File

@@ -127,14 +127,13 @@ describe('buildQrStylingOptions', () => {
).toEqual(g); ).toEqual(g);
}); });
it('uses imageOptions and shape from recipe', () => { it('uses imageOptions from recipe (shape always square)', () => {
const opts = buildQrStylingOptions({ const opts = buildQrStylingOptions({
imageOptions: { imageOptions: {
hideBackgroundDots: false, hideBackgroundDots: false,
imageSize: 0.5, imageSize: 0.5,
margin: 5, margin: 5,
}, },
shape: 'circle',
}); });
expect( expect(
(opts.imageOptions as { hideBackgroundDots: boolean }) (opts.imageOptions as { hideBackgroundDots: boolean })
@@ -144,7 +143,8 @@ describe('buildQrStylingOptions', () => {
0.5, 0.5,
); );
expect((opts.imageOptions as { margin: number }).margin).toBe(5); expect((opts.imageOptions as { margin: number }).margin).toBe(5);
expect(opts.shape).toBe('circle'); // shape is always 'square' (circle has rendering issues in qr-code-styling)
expect(opts.shape).toBe('square');
}); });
}); });

View File

@@ -28,7 +28,7 @@ export function buildQrStylingOptions(
data: overrides.data ?? recipe.data ?? ' ', data: overrides.data ?? recipe.data ?? ' ',
image: overrides.image, image: overrides.image,
type: 'canvas', type: 'canvas',
shape: recipe.shape ?? 'square', shape: 'square', // circle shape has rendering issues in qr-code-styling library
margin: recipe.margin ?? 0, margin: recipe.margin ?? 0,
qrOptions: { qrOptions: {
type: 'canvas', type: 'canvas',

View File

@@ -53,7 +53,7 @@ export interface RecipeOptions {
type?: string; type?: string;
gradient?: QrGradient; gradient?: QrGradient;
}; };
shape?: 'square' | 'circle'; // shape removed - circle shape has rendering issues in qr-code-styling library
margin?: number; margin?: number;
} }