diff --git a/Makefile b/Makefile index f869f9a..9ce3d5b 100644 --- a/Makefile +++ b/Makefile @@ -18,6 +18,10 @@ TAR_EXCLUDE := --exclude='.git' \ --exclude='./.env' \ --exclude='./.DS_Store' +# Hook-Sync: pb_hooks nach jedem Deploy auf DS kopieren +HOOKS_SRC := pocketbase/pb_hooks +HOOKS_DST := /volume1/docker/checkflo/pocketbase/data/pb_hooks + .PHONY: help check-ssh start stop restart status logs logs-f logs-app \ shell-pb pb-admin deploy setup-db seed-demo @@ -58,6 +62,14 @@ check-ssh: deploy: check-ssh @echo "→ Sync zu DS..." @COPYFILE_DISABLE=1 tar czf - $(TAR_EXCLUDE) . | ssh $(DS_HOST) "tar xzf - -C $(DS_PATH)/" + @echo "→ .env auf DS aktualisieren..." + @if [ -f .env ]; then \ + grep -E "BREVO_KEY|TURNSTILE_SECRET" .env | ssh $(DS_HOST) "cat > $(DS_PATH)/.env"; \ + fi + @echo "→ PocketBase Hooks synchronisieren..." + @for f in $(HOOKS_SRC)/*.pb.js; do \ + cat "$$f" | ssh $(DS_HOST) "cat > $(HOOKS_DST)/$$(basename $$f)"; \ + done @echo "→ Docker rebuild + restart..." @ssh $(DS_HOST) " \ cd $(DS_PATH) && \ diff --git a/app/package-lock.json b/app/package-lock.json index 340f1ac..41cbc0a 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -8,13 +8,15 @@ "name": "app", "version": "0.0.1", "dependencies": { - "pocketbase": "^0.26.9" + "pocketbase": "^0.26.9", + "qrcode": "^1.5.4" }, "devDependencies": { "@sveltejs/adapter-auto": "^7.0.1", "@sveltejs/adapter-node": "^5.5.4", "@sveltejs/kit": "^2.57.0", "@sveltejs/vite-plugin-svelte": "^7.0.0", + "@types/qrcode": "^1.5.6", "svelte": "^5.55.2", "svelte-check": "^4.4.6", "typescript": "^6.0.2", @@ -2660,6 +2662,26 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/node": { + "version": "25.8.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.8.0.tgz", + "integrity": "sha512-TCFSk8IZh+iLX1xtksoBVtdmgL+1IX0fC9BeU4QqFSuNdN/K+HUlhqOzEmSYYpZUVsLYcPqc9KX+60iDuninSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": ">=7.24.0 <7.24.7" + } + }, + "node_modules/@types/qrcode": { + "version": "1.5.6", + "resolved": "https://registry.npmjs.org/@types/qrcode/-/qrcode-1.5.6.tgz", + "integrity": "sha512-te7NQcV2BOvdj2b1hCAHzAoMNuj65kNBMz0KBaxM6c3VGBOhU0dURQKOtH8CFNI/dsKkwlv32p26qYQTWoB5bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/resolve": { "version": "1.20.2", "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", @@ -2704,6 +2726,30 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/aria-query": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.1.tgz", @@ -2975,6 +3021,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/caniuse-lite": { "version": "1.0.30001793", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001793.tgz", @@ -3012,6 +3067,17 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, "node_modules/clsx": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", @@ -3022,6 +3088,24 @@ "node": ">=6" } }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, "node_modules/commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", @@ -3174,6 +3258,15 @@ } } }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/deepmerge": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", @@ -3237,6 +3330,12 @@ "dev": true, "license": "MIT" }, + "node_modules/dijkstrajs": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz", + "integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==", + "license": "MIT" + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -3275,6 +3374,12 @@ "dev": true, "license": "ISC" }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, "node_modules/es-abstract": { "version": "1.24.2", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.2.tgz", @@ -3565,6 +3670,19 @@ "node": ">=10" } }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/for-each": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", @@ -3690,6 +3808,15 @@ "node": ">=6.9.0" } }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", @@ -4073,6 +4200,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/is-generator-function": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", @@ -4730,6 +4866,18 @@ "dev": true, "license": "MIT" }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", @@ -4926,6 +5074,42 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/package-json-from-dist": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", @@ -4933,6 +5117,15 @@ "dev": true, "license": "BlueOak-1.0.0" }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -4997,6 +5190,15 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pngjs": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz", + "integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==", + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/pocketbase": { "version": "0.26.9", "resolved": "https://registry.npmjs.org/pocketbase/-/pocketbase-0.26.9.tgz", @@ -5065,6 +5267,23 @@ "node": ">=6" } }, + "node_modules/qrcode": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.4.tgz", + "integrity": "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==", + "license": "MIT", + "dependencies": { + "dijkstrajs": "^1.0.1", + "pngjs": "^5.0.0", + "yargs": "^15.3.1" + }, + "bin": { + "qrcode": "bin/qrcode" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/readdirp": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", @@ -5181,6 +5400,15 @@ "regjsparser": "bin/parser" } }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", @@ -5191,6 +5419,12 @@ "node": ">=0.10.0" } }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "license": "ISC" + }, "node_modules/resolve": { "version": "1.22.12", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", @@ -5387,6 +5621,12 @@ "node": ">=20.0.0" } }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "license": "ISC" + }, "node_modules/set-cookie-parser": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-3.1.0.tgz", @@ -5639,6 +5879,20 @@ "node": ">= 0.4" } }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/string.prototype.matchall": { "version": "4.0.12", "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", @@ -5741,6 +5995,18 @@ "node": ">=4" } }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-comments/-/strip-comments-2.0.1.tgz", @@ -6033,6 +6299,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/undici-types": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", + "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", + "dev": true, + "license": "MIT" + }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", @@ -6373,6 +6646,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/which-module": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", + "license": "ISC" + }, "node_modules/which-typed-array": { "version": "1.1.20", "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz", @@ -6615,6 +6894,26 @@ "workbox-core": "7.4.1" } }, + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "license": "ISC" + }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", @@ -6622,6 +6921,41 @@ "dev": true, "license": "ISC" }, + "node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "license": "MIT", + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "license": "ISC", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/zimmerframe": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/zimmerframe/-/zimmerframe-1.1.4.tgz", diff --git a/app/package.json b/app/package.json index aa45009..10855aa 100644 --- a/app/package.json +++ b/app/package.json @@ -16,6 +16,7 @@ "@sveltejs/adapter-node": "^5.5.4", "@sveltejs/kit": "^2.57.0", "@sveltejs/vite-plugin-svelte": "^7.0.0", + "@types/qrcode": "^1.5.6", "svelte": "^5.55.2", "svelte-check": "^4.4.6", "typescript": "^6.0.2", @@ -23,6 +24,7 @@ "vite-plugin-pwa": "^1.3.0" }, "dependencies": { - "pocketbase": "^0.26.9" + "pocketbase": "^0.26.9", + "qrcode": "^1.5.4" } } diff --git a/app/src/app.html b/app/src/app.html index 9f772c3..7f56a44 100644 --- a/app/src/app.html +++ b/app/src/app.html @@ -9,6 +9,7 @@ + diff --git a/app/src/lib/assets/hafner-logo.png b/app/src/lib/assets/hafner-logo.png new file mode 100644 index 0000000..4e053b8 Binary files /dev/null and b/app/src/lib/assets/hafner-logo.png differ diff --git a/app/src/lib/assets/hafner-logo.svg b/app/src/lib/assets/hafner-logo.svg new file mode 100644 index 0000000..9a125cf --- /dev/null +++ b/app/src/lib/assets/hafner-logo.svg @@ -0,0 +1,9 @@ + + + + + + + Hafner + Prüftechnik GmbH + diff --git a/app/src/lib/assets/icons/broom.svg b/app/src/lib/assets/icons/broom.svg new file mode 100644 index 0000000..0cd12e6 --- /dev/null +++ b/app/src/lib/assets/icons/broom.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/src/lib/assets/icons/dashboard.svg b/app/src/lib/assets/icons/dashboard.svg new file mode 100644 index 0000000..c9cf470 --- /dev/null +++ b/app/src/lib/assets/icons/dashboard.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/src/lib/assets/icons/logout.svg b/app/src/lib/assets/icons/logout.svg new file mode 100644 index 0000000..7bf5110 --- /dev/null +++ b/app/src/lib/assets/icons/logout.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/src/lib/assets/icons/logs.svg b/app/src/lib/assets/icons/logs.svg new file mode 100644 index 0000000..9abf1d0 --- /dev/null +++ b/app/src/lib/assets/icons/logs.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/src/lib/assets/icons/printer.svg b/app/src/lib/assets/icons/printer.svg new file mode 100644 index 0000000..7befdfd --- /dev/null +++ b/app/src/lib/assets/icons/printer.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/src/lib/assets/icons/protokoll.svg b/app/src/lib/assets/icons/protokoll.svg new file mode 100644 index 0000000..cbaca75 --- /dev/null +++ b/app/src/lib/assets/icons/protokoll.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/src/lib/assets/icons/snowflake.svg b/app/src/lib/assets/icons/snowflake.svg new file mode 100644 index 0000000..b4ac05c --- /dev/null +++ b/app/src/lib/assets/icons/snowflake.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/src/lib/assets/icons/stations.svg b/app/src/lib/assets/icons/stations.svg new file mode 100644 index 0000000..249f544 --- /dev/null +++ b/app/src/lib/assets/icons/stations.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/src/lib/assets/icons/thermometer-cold.svg b/app/src/lib/assets/icons/thermometer-cold.svg new file mode 100644 index 0000000..d2df6a0 --- /dev/null +++ b/app/src/lib/assets/icons/thermometer-cold.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/src/lib/assets/icons/thermometer-hot.svg b/app/src/lib/assets/icons/thermometer-hot.svg new file mode 100644 index 0000000..ebc0157 --- /dev/null +++ b/app/src/lib/assets/icons/thermometer-hot.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/src/lib/assets/icons/thermometer.svg b/app/src/lib/assets/icons/thermometer.svg new file mode 100644 index 0000000..57a3698 --- /dev/null +++ b/app/src/lib/assets/icons/thermometer.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/src/lib/assets/schmidt-hygiene-logo.svg b/app/src/lib/assets/schmidt-hygiene-logo.svg new file mode 100644 index 0000000..5c23b22 --- /dev/null +++ b/app/src/lib/assets/schmidt-hygiene-logo.svg @@ -0,0 +1,9 @@ + + + + + + + Schmidt + Hygiene GmbH + diff --git a/app/src/routes/+page.svelte b/app/src/routes/+page.svelte index f34d61b..2afc644 100644 --- a/app/src/routes/+page.svelte +++ b/app/src/routes/+page.svelte @@ -1,21 +1,173 @@ @@ -31,15 +183,133 @@
-

HACCP-Dokumentation für die Gastronomie

-

Prüfprotokolle.
Ohne Papier.
Ohne App-Store.

+

White-Label-Software für Prüf- und Wartungsbetriebe

+

Ihre eigene
HACCP-App —
unter Ihrem Namen.

- Kühlschranktemperaturen, Hygienekontrollen und Wartungsnachweise — - digital erfasst, automatisch protokolliert, jederzeit abrufbar. + Bieten Sie Ihren Kunden digitale Prüfprotokolle als gebrandete App an. + Kein App-Store. Keine Installation. Läuft offline. + Und Ihre Kunden sehen nur Ihr Logo.

- Kostenlose Demo anfragen - Wie es funktioniert ↓ + + So funktioniert es ↓ +
+
+
+ + +
+
+

So sieht Ihre gebrandete App aus

+

Dashboard für Sie — Checkliste für Ihre Kunden

+ +
+ + +
+
+
+ +
+
app.hafner-pruefdienste.de/admin
+
+
+
+ + + + +
+ + + {#if activeView === 'dashboard'} +
+
Dashboard
+
+ {new Date().toLocaleDateString('de-DE', { weekday: 'long', day: 'numeric', month: 'long' })} + {#if lastUpdate}· {lastUpdate}{/if} +
+
+
{liveStats.total}
Heute
+
{liveStats.ok}
OK
+
{liveStats.warn}
Abw.
+
{liveStats.crit}
Kritisch
+
+
Letzte Einträge
+ {#each liveLogs.slice(0,4) as log} +
+ {STATUS_LABEL[log.status] ?? log.status} + {log.expand?.station?.name ?? '—'} + {log.temperature ? log.temperature + ' °C' : ''} + {log.checked_by} · {formatTime(log.created)} +
+ {:else} +
Noch keine Einträge heute
+ {/each} +
+ {/if} + + + {#if activeView === 'protokoll'} +
+
Protokoll
+
{new Date().toLocaleDateString('de-DE', { month: 'long', year: 'numeric' })}
+ + + + {#each liveLogs as log} + + + + + + + {:else} + + {/each} + +
StationStatusTemp.Zeit
{log.expand?.station?.name ?? '—'}{STATUS_LABEL[log.status] ?? log.status}{log.temperature ? log.temperature + '°' : '—'}{formatTime(log.created)}
Keine Einträge
+
+ {/if} + + + {#if activeView === 'stationen'} +
+
Stationen
+
{liveStations.length} aktive Stationen
+
+ {#each liveStations as s} +
+ +
{s.name}
+ {#if s.target_temp_min || s.target_temp_max} +
{s.target_temp_min}° – {s.target_temp_max}°C
+ {/if} +
+ {:else} +
Keine Stationen
+ {/each} +
+
+ {/if} +
+
+ + +
+
+
+
+ +
+
+
+

Ihr Kunde erfasst — Sie sehen es sofort

+
+
@@ -47,22 +317,22 @@
-

In 3 Schritten zum digitalen Protokoll

+

In 3 Schritten zur eigenen HACCP-App

1
-

QR-Code aufkleben

-

Robuste NFC/QR-Tags direkt am Kühlschrank, Warmhaltebehälter oder an der Hygiene-Station.

+

Lizenz buchen & branden

+

Sie erhalten Ihre App unter Ihrem Logo, Ihren Farben und Ihrer Domain — fertig in 48 Stunden.

2
-

Scannen & ausfüllen

-

Techniker scannt den Code, trägt Temperatur ein, macht bei Abweichung ein Foto — fertig.

+

Kunden einrichten

+

Stationen anlegen, QR-Codes ausdrucken, beim Kunden aufkleben. Ihr Kunde braucht nichts installieren.

3
-

Protokoll abrufen

-

Vollständiges Monatsprotokoll als PDF — für Lebensmittelkontrolle, Franchise-Geber oder eigene Dokumentation.

+

Protokolle abrufen

+

Ihre Kunden erfassen täglich — Sie sehen alles in Echtzeit und exportieren Protokolle per Knopfdruck als PDF.

@@ -71,7 +341,7 @@
-

Warum checkflo?

+

Was checkflo für Sie leistet

{#each features as f}
@@ -84,15 +354,134 @@
+ +
+
+
+

White-Label-Lizenz für Prüf- und Wartungsbetriebe

+

+ Sie treten gegenüber Ihren Kunden mit einer eigenen gebrandeten App auf — + checkflo läuft im Hintergrund. Ihre Kunden sehen nur Ihr Logo, Ihre Farben, Ihre Domain. +

+
+ 10 Kunden × 30 €/Monat + + 300 € MRR bei 249 € Lizenz +
+
+
+
+
Basic White-Label
+
249 €/Monat
+

Für den Einstieg — bis zu 10 Kundenbetriebe

+
    +
  • ✓ Ihr Logo & CI-Farben in der App
  • +
  • ✓ Subdomain (app.ihr-betrieb.de)
  • +
  • ✓ Bis zu 500 Assets / QR-Tags gesamt
  • +
  • ✓ HACCP-Protokoll-Templates
  • +
  • ✓ PDF-Export für Ihre Kunden
  • +
  • ✓ Offline-fähige PWA
  • +
+ +
+ +
+
Enterprise
+
ab 1.500 €/Monat
+

Für Ketten, Verbände & große Facility-Manager

+
    +
  • ✓ Dedizierte Server-Infrastruktur
  • +
  • ✓ SAP / MS Dynamics Anbindung
  • +
  • ✓ On-Premise Installation möglich
  • +
  • ✓ SLA & dedizierter Ansprechpartner
  • +
  • ✓ Individuelle Entwicklung
  • +
  • ✓ Schulung & Onboarding
  • +
+ +
+
+

Keine Einrichtungsgebühr · Monatlich kündbar · Alle Pläne inkl. Updates

+
+
+
-

Bereit für papierlose HACCP-Dokumentation?

-

Für Gastronomie, Kantinen, Caterer und Lebensmittelverarbeiter.

- Jetzt Demo vereinbaren +

Bieten Sie Ihren Kunden eine eigene HACCP-App an.

+

Für Prüfbetriebe, Ingenieurbüros und Facility-Management-Firmen.

+
+ +{#if showModal} + +{/if} +