Compare commits

...

10 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
58f5af27db Fixes for all the bullshit
All checks were successful
ci/woodpecker/push/ci Pipeline was successful
ci/woodpecker/push/deploy Pipeline was successful
2026-02-07 16:38:39 -03:00
89cb014163 Trash 2026-02-07 15:56:30 -03:00
14 changed files with 168 additions and 149 deletions

View File

@@ -22,6 +22,24 @@ steps:
depends_on:
- 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
image: node:22-bookworm-slim
commands:
@@ -38,6 +56,24 @@ steps:
depends_on:
- 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
image: node:22-bookworm-slim
commands:
@@ -46,23 +82,41 @@ steps:
depends_on:
- Tests & Coverage
# build-full:
# image: node:22-bookworm-slim
# commands:
# - apt-get update
# - 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
# - rm -rf /var/lib/apt/lists/*
# - corepack enable && corepack prepare pnpm@latest --activate
# - pnpm run critical-css:install
# - pnpm run build:full
# depends_on:
# - build
- name: Send Build 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] 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:
# image: node:22-bookworm-slim
# commands:
# - corepack enable && corepack prepare pnpm@latest --activate
# - pnpm exec playwright install chromium --with-deps
# - pnpm run test:e2e
# depends_on:
# - build
- name: Send CI Pipeline Status Notification (success)
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] 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)
image: curlimages/curl
environment:
DISCORD_WEBHOOK_URL:
from_secret: discord_webhook_url
MATTERMOST_BOT_ACCESS_TOKEN:
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:
- |
BODY=$(printf '{"username":"WoodpeckerBot","content":"[%s - Build #%s] Docker images build success 🎉"}' "$CI_REPO" "$CI_PIPELINE_NUMBER")
curl -sS -X POST -H "Content-Type: application/json" -d "$BODY" "$DISCORD_WEBHOOK_URL"
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" -H "Authorization: Bearer $MATTERMOST_BOT_ACCESS_TOKEN" $MATTERMOST_POST_API_URL
depends_on:
- Docker image build (qr-api + qr-web)
when:
@@ -69,12 +73,16 @@ steps:
- name: Send Build Status Notification (failure)
image: curlimages/curl
environment:
DISCORD_WEBHOOK_URL:
from_secret: discord_webhook_url
MATTERMOST_BOT_ACCESS_TOKEN:
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:
- |
BODY=$(printf '{"username":"WoodpeckerBot","content":"[%s - Build #%s] Docker images build failure 💩"}' "$CI_REPO" "$CI_PIPELINE_NUMBER")
curl -sS -X POST -H "Content-Type: application/json" -d "$BODY" "$DISCORD_WEBHOOK_URL"
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" -H "Authorization: Bearer $MATTERMOST_BOT_ACCESS_TOKEN" $MATTERMOST_POST_API_URL
depends_on:
- Docker image build (qr-api + qr-web)
when:
@@ -103,12 +111,16 @@ steps:
- name: Send Deploy Status Notification (success)
image: curlimages/curl
environment:
DISCORD_WEBHOOK_URL:
from_secret: discord_webhook_url
MATTERMOST_BOT_ACCESS_TOKEN:
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:
- |
BODY=$(printf '{"username":"WoodpeckerBot","content":"[%s - Build #%s] Production Deploy success 🎉"}' "$CI_REPO" "$CI_PIPELINE_NUMBER")
curl -sS -X POST -H "Content-Type: application/json" -d "$BODY" "$DISCORD_WEBHOOK_URL"
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" -H "Authorization: Bearer $MATTERMOST_BOT_ACCESS_TOKEN" $MATTERMOST_POST_API_URL
depends_on:
- Trigger Portainer stack redeploy
when:
@@ -117,12 +129,16 @@ steps:
- name: Send Deploy Status Notification (failure)
image: curlimages/curl
environment:
DISCORD_WEBHOOK_URL:
from_secret: discord_webhook_url
MATTERMOST_BOT_ACCESS_TOKEN:
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:
- |
BODY=$(printf '{"username":"WoodpeckerBot","content":"[%s - Build #%s] Production Deploy failure 💩"}' "$CI_REPO" "$CI_PIPELINE_NUMBER")
curl -sS -X POST -H "Content-Type: application/json" -d "$BODY" "$DISCORD_WEBHOOK_URL"
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" -H "Authorization: Bearer $MATTERMOST_BOT_ACCESS_TOKEN" $MATTERMOST_POST_API_URL
depends_on:
- Trigger Portainer stack redeploy
when:

View File

@@ -1,7 +1,6 @@
# 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).
# Images: ${REGISTRY}/mifi-holdings/shorty-qr-api:${IMAGE_TAG}, shorty-qr-web:${IMAGE_TAG}
services:
kutt_db:
image: postgres:16-alpine
@@ -64,24 +63,27 @@ services:
start_period: 40s
environment:
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_HOST: kutt_db
DB_PORT: '5432'
DB_USER: ${DB_USER:-kutt}
DB_PASSWORD: ${DB_PASSWORD:?Set DB_PASSWORD}
DB_POOL_MIN: '2'
DB_POOL_MAX: '10'
DB_NAME: ${DB_NAME:-kutt}
DISALLOW_ANONYMOUS_LINKS: 'true'
DISALLOW_REGISTRATION: 'true'
LINK_LENGTH: 6
JWT_SECRET: ${JWT_SECRET:?Set JWT_SECRET}
MAIL_ENABLED: 'true'
MAIL_HOST: 'mail.mifi.holdings'
MAIL_PORT: '465'
MAIL_HOST: ${MAIL_HOST:?Set MAIL_HOST}
MAIL_PORT: ${MAIL_PORT:?Set MAIL_PORT}
MAIL_SECURE: 'true'
MAIL_USER: 'mailbot@mifi.ventures'
MAIL_FROM: 'mifi Holdings Shorty <noreply@mifi.holdings>'
MAIL_PASSWORD: '${SMTP_PASSWORD:?Set SMTP_PASSWORD}'
MAIL_USER: ${MAIL_USER:?Set MAIL_USER}
MAIL_FROM: ${MAIL_FROM:?Set MAIL_FROM}
MAIL_PASSWORD: '${MAIL_PASSWORD:?Set MAIL_PASSWORD}'
NODE_ENV: production
# OIDC_ENABLED: 'true'
# OIDC_ISSUER: 'https://git.mifi.dev'
@@ -90,18 +92,24 @@ services:
REDIS_ENABLED: 'true'
REDIS_HOST: kutt_redis
REDIS_PORT: '6379'
SITE_DOMAIN: link.mifi.me
SITE_NAME: 'mifi Shorty'
SERVER_CNAME_ADDRESS: ${SERVER_CNAME_ADDRESS:-${DEFAULT_DOMAIN}}
SITE_NAME: ${SITE_NAME:-Kutt}
labels:
- 'traefik.enable=true'
- '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.tls.certresolver=letsencrypt'
- 'traefik.http.routers.kutt-mifi.service=kutt-short'
- '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'
# 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:
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.
depends_on:
qr_api:
condition: service_started
condition: service_healthy
environment:
QR_API_URL: http://qr_api:8080
healthcheck:
@@ -148,7 +156,7 @@ services:
- CMD
- node
- -e
- 'require("http").get("http://127.0.0.1:3000/", (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
timeout: 10s
retries: 3

View File

@@ -76,11 +76,7 @@ services:
- 'traefik.http.routers.kutt-mifi.tls.certresolver=letsencrypt'
- 'traefik.http.routers.kutt-mifi.service=kutt-short'
- 'traefik.http.services.kutt-short.loadbalancer.server.port=3000'
- 'traefik.http.routers.kutt-link.rule=Host(`link.mifi.me`)'
- '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'
- 'traefik.http.services.kutt-short.loadbalancer.serversTransport=kutt-long-timeout@file'
qr_api:
build:
@@ -128,10 +124,10 @@ services:
QR_API_URL: http://qr_api:8080
healthcheck:
test:
[
'CMD-SHELL',
'node -e "require(''http'').get(''http://127.0.0.1:3000/'', (r) => { r.resume(); process.exit(r.statusCode >= 200 && r.statusCode < 500 ? 0 : 1); }).on(''error'', () => process.exit(1))"',
]
- CMD
- node
- -e
- '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
timeout: 10s
retries: 3

View File

@@ -1,57 +0,0 @@
# Resetting the Kutt PostgreSQL password
If you lost the stack env vars (e.g. after deleting the Portainer stack) and need to use the existing database with a new password, you have two options.
**Default DB user/db name from compose:** `kutt` / `kutt` (or whatever you set in `DB_USER` / `DB_NAME`).
**Data path on host:** `/mnt/config/docker/kutt/postgres` (from `docker-compose.portainer.yml`).
---
## Option A: Fresh start (delete all Kutt data)
Use this if you **dont need** existing links/users.
1. On the host, remove the Postgres data directory:
```bash
sudo rm -rf /mnt/config/docker/kutt/postgres
```
2. In Portainer, (re-)create the stack and set env vars, including a new `DB_PASSWORD`.
3. Deploy. Postgres will initialise a new database with the new password.
---
## Option B: Keep data, force-set a new password
Use this if you **want to keep** existing Kutt data but dont know the current password.
**Stop the stack first** (so nothing is using the Postgres data volume).
1. On the host, temporarily allow local connections without a password:
```bash
cd /mnt/config/docker/kutt/postgres
cp pg_hba.conf pg_hba.conf.bak
echo 'local all all trust' > pg_hba.conf
echo 'host all all 127.0.0.1/32 trust' >> pg_hba.conf
echo 'host all all ::1/128 trust' >> pg_hba.conf
```
2. Start a temporary Postgres container (it will use the modified `pg_hba.conf`):
```bash
docker run -d --name pg-reset \
-v /mnt/config/docker/kutt/postgres:/var/lib/postgresql/data \
postgres:16-alpine
sleep 5
```
3. Set the new password (replace `kutt` if you use a different `DB_USER`, and set `YOUR_NEW_PASSWORD`):
```bash
docker exec pg-reset psql -U postgres -c "ALTER USER kutt PASSWORD 'YOUR_NEW_PASSWORD';"
```
4. Stop the temporary container and restore `pg_hba.conf`:
```bash
docker stop pg-reset && docker rm pg-reset
cd /mnt/config/docker/kutt/postgres
mv pg_hba.conf.bak pg_hba.conf
```
5. In Portainer, create/redeploy the stack and set `DB_PASSWORD` (and other env vars) to the same `YOUR_NEW_PASSWORD`.

2
qr-api/.npmrc Normal file
View File

@@ -0,0 +1,2 @@
# Force pnpm to run build scripts for better-sqlite3
enable-pre-post-scripts=true

View File

@@ -1,25 +1,23 @@
# Build stage: TypeScript only (no native deps in this stage).
# Build stage: use npm (not pnpm) to avoid workspace issues with better-sqlite3.
FROM node:20-bookworm-slim AS builder
RUN corepack enable && corepack prepare pnpm@latest --activate
RUN apt-get update && apt-get install -y --no-install-recommends python3 make g++ \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY package.json pnpm-lock.yaml* ./
RUN pnpm install
COPY package.json ./
RUN npm install
# Verify better-sqlite3 was compiled; fail build if not.
RUN test -f node_modules/better-sqlite3/build/Release/better_sqlite3.node || \
(echo "ERROR: better-sqlite3.node not found after npm install" && exit 1)
COPY . .
RUN pnpm run build
RUN npm run build
RUN npm prune --production
# Runtime: install deps here so better-sqlite3 is compiled for this exact image/platform.
# Runtime: copy pre-built node_modules from builder.
FROM node:20-bookworm-slim
WORKDIR /app
ENV NODE_ENV=production
# Install build deps needed to compile better-sqlite3; remove after install to keep image small.
RUN apt-get update && apt-get install -y --no-install-recommends python3 make g++ \
&& rm -rf /var/lib/apt/lists/*
RUN corepack enable && corepack prepare pnpm@latest --activate
COPY package.json pnpm-lock.yaml* ./
RUN pnpm install --prod \
&& apt-get purge -y python3 make g++ \
&& apt-get autoremove -y --purge \
&& rm -rf /var/lib/apt/lists/*
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/package.json ./package.json
EXPOSE 8080
CMD ["node", "dist/index.js"]

View File

@@ -35,7 +35,10 @@ describe('shortenUrl', () => {
'Content-Type': 'application/json',
'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({
body: JSON.stringify({
target: 'https://example.com',
domain: 'mifi.me',
customurl: 'myslug',
}),
}),

View File

@@ -18,14 +18,23 @@ export async function shortenUrl(
}
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`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': env.KUTT_API_KEY,
'X-API-Key': apiKey,
},
body: JSON.stringify({
target: body.targetUrl,
domain: domain,
...(body.customSlug && { customurl: body.customSlug }),
}),
});

View File

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

View File

@@ -1020,19 +1020,7 @@ export function Editor({ id }: EditorProps) {
updateProject({ recipeJson: JSON.stringify(r) });
}}
/>
<Select
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) });
}}
/>
{/* Shape select removed - circle shape has rendering issues in qr-code-styling library */}
<Group grow>
<NumberInput
label="Margin"

View File

@@ -127,14 +127,13 @@ describe('buildQrStylingOptions', () => {
).toEqual(g);
});
it('uses imageOptions and shape from recipe', () => {
it('uses imageOptions from recipe (shape always square)', () => {
const opts = buildQrStylingOptions({
imageOptions: {
hideBackgroundDots: false,
imageSize: 0.5,
margin: 5,
},
shape: 'circle',
});
expect(
(opts.imageOptions as { hideBackgroundDots: boolean })
@@ -144,7 +143,8 @@ describe('buildQrStylingOptions', () => {
0.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 ?? ' ',
image: overrides.image,
type: 'canvas',
shape: recipe.shape ?? 'square',
shape: 'square', // circle shape has rendering issues in qr-code-styling library
margin: recipe.margin ?? 0,
qrOptions: {
type: 'canvas',

View File

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