Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f56c0f83c2 | ||
|
|
35a849913e | ||
|
|
e01b9b5445 | ||
|
|
e3484718a0 |
2
.env
2
.env
@@ -4,8 +4,6 @@
|
||||
# Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB.
|
||||
# See the documentation for all the connection string options: https://pris.ly/d/connection-strings
|
||||
|
||||
SECRET="jg57cya4mrNkGlnb2R85X1gI8LdY7iNZ9MF0Jbn0K5zQBshOxv"
|
||||
|
||||
POSTGRES_DB=main
|
||||
POSTGRES_HOST=localhost
|
||||
POSTGRES_PORT=5432
|
||||
|
||||
39
.github/workflows/prod-pipeline.yml
vendored
39
.github/workflows/prod-pipeline.yml
vendored
@@ -5,33 +5,8 @@ on:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
check-migrations:
|
||||
name: Check for new Prisma migrations
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
has_new_migrations: ${{ steps.diff.outputs.has_new_migrations }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0 # get full history so git diff works properly
|
||||
|
||||
- name: Detect new migration files
|
||||
id: diff
|
||||
run: |
|
||||
# Compare the last two commits on main
|
||||
if git diff --quiet HEAD~1 -- prisma/migrations/; then
|
||||
echo "✅ No new Prisma migrations detected."
|
||||
echo "has_new_migrations=false" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "⚠️ New Prisma migrations detected! Blocking deployment."
|
||||
echo "has_new_migrations=true" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
deploy:
|
||||
name: Deploy to production
|
||||
runs-on: ubuntu-latest
|
||||
needs: check-migrations
|
||||
if: needs.check-migrations.outputs.has_new_migrations == 'false'
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install Bun
|
||||
@@ -53,16 +28,4 @@ jobs:
|
||||
git clean -f -d
|
||||
git pull origin main
|
||||
git status
|
||||
make prod
|
||||
|
||||
block-deploy:
|
||||
name: Block deployment (new migrations detected)
|
||||
runs-on: ubuntu-latest
|
||||
needs: check-migrations
|
||||
if: needs.check-migrations.outputs.has_new_migrations == 'true'
|
||||
steps:
|
||||
- name: Stop deploy
|
||||
run: |
|
||||
echo "🚫 Deployment blocked because new Prisma migrations were detected."
|
||||
echo "Please apply migrations on staging and verify before deploying to production."
|
||||
exit 1
|
||||
make prod
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -15,8 +15,6 @@ npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
error.log*
|
||||
combined.log*
|
||||
|
||||
# lockfile
|
||||
pnpm-lock.yaml
|
||||
|
||||
3
Makefile
3
Makefile
@@ -62,8 +62,7 @@ all:
|
||||
bun run dev 2>&1 | tee ~/logs/`date '+%d-%m-%Y_%H:%M:%S'`.log
|
||||
|
||||
update-dwd-klimafaktoren-cron:
|
||||
- pm2 delete update-dwd-klimafaktoren-cron
|
||||
pm2 start --no-autorestart bun --name "update-dwd-klimafaktoren-cron" --cron "0 12 28 * *" -- src/cronjobs/update-dwd-klimafaktoren.ts
|
||||
pm2 start bun --name "update-dwd-klimafaktoren-cron" --cron "0 12 28 * *" -- src/cronjobs/update-dwd-klimafaktoren.ts
|
||||
|
||||
prod: prod-no-backup backup-database-cronjob
|
||||
|
||||
|
||||
@@ -5,11 +5,8 @@ import tailwind from "@astrojs/tailwind";
|
||||
import node from "@astrojs/node";
|
||||
import mdx from "@astrojs/mdx";
|
||||
import astroTypesafeAPI from "astro-typesafe-api"
|
||||
import { logger } from "./src/lib/logger";
|
||||
|
||||
logger.info("Astro config loaded");
|
||||
|
||||
// https://astro.build/config
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
integrations: [svelte(), tailwind(), mdx(), astroTypesafeAPI()],
|
||||
outDir: "./dist",
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [[ -z "$prev_restart_delay" && -n "$cron_restart" ]]; then
|
||||
echo "skipping initial launch...."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
FILE_NAME=data-dump_`date +%Y-%m-%d"_"%H_%M_%S`.sql.br
|
||||
FILE_NAME_COMPLETE=full-dump_`date +%Y-%m-%d"_"%H_%M_%S`.sql.br
|
||||
DATABASE_NAME=database
|
||||
|
||||
29
bun.lock
29
bun.lock
@@ -29,6 +29,7 @@
|
||||
"fontkit": "^2.0.4",
|
||||
"handlebars": "^4.7.8",
|
||||
"heic2any": "^0.0.4",
|
||||
"highlight.run": "^9.14.0",
|
||||
"is-base64": "^1.1.0",
|
||||
"js-cookie": "^3.0.5",
|
||||
"js-interpolate": "^1.3.2",
|
||||
@@ -55,7 +56,7 @@
|
||||
"tailwindcss": "^3.4.17",
|
||||
"trpc-openapi": "^1.2.0",
|
||||
"uuid": "^9.0.1",
|
||||
"winston": "^3.18.3",
|
||||
"winston": "^3.17.0",
|
||||
"zod": "^3.24.1",
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -269,7 +270,7 @@
|
||||
|
||||
"@cypress/xvfb": ["@cypress/xvfb@1.2.4", "", { "dependencies": { "debug": "^3.1.0", "lodash.once": "^4.1.1" } }, "sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q=="],
|
||||
|
||||
"@dabh/diagnostics": ["@dabh/diagnostics@2.0.8", "", { "dependencies": { "@so-ric/colorspace": "^1.1.6", "enabled": "2.0.x", "kuler": "^2.0.0" } }, "sha512-R4MSXTVnuMzGD7bzHdW2ZhhdPC/igELENcq5IjEverBvq5hn1SXCWcsi6eSsdWP0/Ur+SItRRjAktmdoX/8R/Q=="],
|
||||
"@dabh/diagnostics": ["@dabh/diagnostics@2.0.3", "", { "dependencies": { "colorspace": "1.1.x", "enabled": "2.0.x", "kuler": "^2.0.0" } }, "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA=="],
|
||||
|
||||
"@daybrush/utils": ["@daybrush/utils@1.13.0", "", {}, "sha512-ALK12C6SQNNHw1enXK+UO8bdyQ+jaWNQ1Af7Z3FNxeAwjYhQT7do+TRE4RASAJ3ObaS2+TJ7TXR3oz2Gzbw0PQ=="],
|
||||
|
||||
@@ -713,8 +714,6 @@
|
||||
|
||||
"@smithy/util-waiter": ["@smithy/util-waiter@4.0.2", "", { "dependencies": { "@smithy/abort-controller": "^4.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-piUTHyp2Axx3p/kc2CIJkYSv0BAaheBQmbACZgQSSfWUumWNW+R1lL+H9PDBxKJkvOeEX+hKYEFiwO8xagL8AQ=="],
|
||||
|
||||
"@so-ric/colorspace": ["@so-ric/colorspace@1.1.6", "", { "dependencies": { "color": "^5.0.2", "text-hex": "1.0.x" } }, "sha512-/KiKkpHNOBgkFJwu9sh48LkHSMYGyuTcSFK/qMBdnOAlrRJzRSXAOFB5qwzaVQuDl8wAvHVMkaASQDReTahxuw=="],
|
||||
|
||||
"@svelte-plugins/datepicker": ["@svelte-plugins/datepicker@1.0.11", "", {}, "sha512-Tqc07QLyRkCpc3Glg6oRLTUApLtCrOh52d6vJ7L32QI17HrwvcDDjaH3LF3X1SBm3CWdMrnqfJp3xjUZmB4wzw=="],
|
||||
|
||||
"@sveltejs/vite-plugin-svelte": ["@sveltejs/vite-plugin-svelte@2.5.3", "", { "dependencies": { "@sveltejs/vite-plugin-svelte-inspector": "^1.0.4", "debug": "^4.3.4", "deepmerge": "^4.3.1", "kleur": "^4.1.5", "magic-string": "^0.30.3", "svelte-hmr": "^0.15.3", "vitefu": "^0.2.4" }, "peerDependencies": { "svelte": "^3.54.0 || ^4.0.0 || ^5.0.0-next.0", "vite": "^4.0.0" } }, "sha512-erhNtXxE5/6xGZz/M9eXsmI7Pxa6MS7jyTy06zN3Ck++ldrppOnOlJwHHTsMC7DHDQdgUp4NAc4cDNQ9eGdB/w=="],
|
||||
@@ -1107,6 +1106,8 @@
|
||||
|
||||
"colorette": ["colorette@2.0.19", "", {}, "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ=="],
|
||||
|
||||
"colorspace": ["colorspace@1.1.4", "", { "dependencies": { "color": "^3.1.3", "text-hex": "1.0.x" } }, "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w=="],
|
||||
|
||||
"combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="],
|
||||
|
||||
"comma-separated-tokens": ["comma-separated-tokens@2.0.3", "", {}, "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg=="],
|
||||
@@ -1559,6 +1560,8 @@
|
||||
|
||||
"hexoid": ["hexoid@2.0.0", "", {}, "sha512-qlspKUK7IlSQv2o+5I7yhUd7TxlOG2Vr5LTa3ve2XSNVKAL/n/u/7KLvKmFNimomDIKvZFXWHv0T12mv7rT8Aw=="],
|
||||
|
||||
"highlight.run": ["highlight.run@9.14.0", "", {}, "sha512-ZR+ZLHlVU8lXqsuto0ZEMAOuvptaTBBf1jradnKDIn9OfAXupcYFbkASDlbsZtyBh2SYJSK50xwrucXujhksRg=="],
|
||||
|
||||
"hosted-git-info": ["hosted-git-info@2.8.9", "", {}, "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw=="],
|
||||
|
||||
"hotkeys-js": ["hotkeys-js@3.13.9", "", {}, "sha512-3TRCj9u9KUH6cKo25w4KIdBfdBfNRjfUwrljCLDC2XhmPDG0SjAZFcFZekpUZFmXzfYoGhFDcdx2gX/vUVtztQ=="],
|
||||
@@ -2765,7 +2768,7 @@
|
||||
|
||||
"widest-line": ["widest-line@5.0.0", "", { "dependencies": { "string-width": "^7.0.0" } }, "sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA=="],
|
||||
|
||||
"winston": ["winston@3.18.3", "", { "dependencies": { "@colors/colors": "^1.6.0", "@dabh/diagnostics": "^2.0.8", "async": "^3.2.3", "is-stream": "^2.0.0", "logform": "^2.7.0", "one-time": "^1.0.0", "readable-stream": "^3.4.0", "safe-stable-stringify": "^2.3.1", "stack-trace": "0.0.x", "triple-beam": "^1.3.0", "winston-transport": "^4.9.0" } }, "sha512-NoBZauFNNWENgsnC9YpgyYwOVrl2m58PpQ8lNHjV3kosGs7KJ7Npk9pCUE+WJlawVSe8mykWDKWFSVfs3QO9ww=="],
|
||||
"winston": ["winston@3.17.0", "", { "dependencies": { "@colors/colors": "^1.6.0", "@dabh/diagnostics": "^2.0.2", "async": "^3.2.3", "is-stream": "^2.0.0", "logform": "^2.7.0", "one-time": "^1.0.0", "readable-stream": "^3.4.0", "safe-stable-stringify": "^2.3.1", "stack-trace": "0.0.x", "triple-beam": "^1.3.0", "winston-transport": "^4.9.0" } }, "sha512-DLiFIXYC5fMPxaRg832S6F5mJYvePtmO5G9v9IgUFPhXm9/GkXarH/TUrBAVzhTCzAj9anE/+GjrgXp/54nOgw=="],
|
||||
|
||||
"winston-transport": ["winston-transport@4.9.0", "", { "dependencies": { "logform": "^2.7.0", "readable-stream": "^3.6.2", "triple-beam": "^1.3.0" } }, "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A=="],
|
||||
|
||||
@@ -2885,8 +2888,6 @@
|
||||
|
||||
"@rollup/pluginutils/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
|
||||
|
||||
"@so-ric/colorspace/color": ["color@5.0.2", "", { "dependencies": { "color-convert": "^3.0.1", "color-string": "^2.0.0" } }, "sha512-e2hz5BzbUPcYlIRHo8ieAhYgoajrJr+hWoceg6E345TPsATMUKqDgzt8fSXZJJbxfpiPzkWyphz8yn8At7q3fA=="],
|
||||
|
||||
"@sveltejs/vite-plugin-svelte/vitefu": ["vitefu@0.2.5", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0" }, "optionalPeers": ["vite"] }, "sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q=="],
|
||||
|
||||
"@types/ssh2/@types/node": ["@types/node@18.19.86", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-fifKayi175wLyKyc5qUfyENhQ1dCNI1UNjp653d8kuYcPQN5JhX3dGuP/XmvPTg/xRBn1VTLpbmi+H/Mr7tLfQ=="],
|
||||
@@ -2933,6 +2934,8 @@
|
||||
|
||||
"co-body/qs": ["qs@6.14.0", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w=="],
|
||||
|
||||
"colorspace/color": ["color@3.2.1", "", { "dependencies": { "color-convert": "^1.9.3", "color-string": "^1.6.0" } }, "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA=="],
|
||||
|
||||
"csvtojson/strip-bom": ["strip-bom@2.0.0", "", { "dependencies": { "is-utf8": "^0.2.0" } }, "sha512-kwrX1y7czp1E69n2ajbG65mIo9dqvJ+8aBQXOGVxqwvNbsXdFM6Lq37dLAY3mknUwru8CfcCbfOLL/gMo+fi3g=="],
|
||||
|
||||
"cypress/buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="],
|
||||
@@ -3177,10 +3180,6 @@
|
||||
|
||||
"@prisma/internals/globby/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="],
|
||||
|
||||
"@so-ric/colorspace/color/color-convert": ["color-convert@3.1.2", "", { "dependencies": { "color-name": "^2.0.0" } }, "sha512-UNqkvCDXstVck3kdowtOTWROIJQwafjOfXSmddoDrXo4cewMKmusCeF22Q24zvjR8nwWib/3S/dfyzPItPEiJg=="],
|
||||
|
||||
"@so-ric/colorspace/color/color-string": ["color-string@2.1.2", "", { "dependencies": { "color-name": "^2.0.0" } }, "sha512-RxmjYxbWemV9gKu4zPgiZagUxbH3RQpEIO77XoSSX0ivgABDZ+h8Zuash/EMFLTI4N9QgFPOJ6JQpPZKFxa+dA=="],
|
||||
|
||||
"@types/ssh2/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="],
|
||||
|
||||
"@typescript-eslint/typescript-estree/globby/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="],
|
||||
@@ -3201,6 +3200,8 @@
|
||||
|
||||
"boxen/wrap-ansi/strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="],
|
||||
|
||||
"colorspace/color/color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="],
|
||||
|
||||
"express/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
|
||||
|
||||
"express/send/encodeurl": ["encodeurl@1.0.2", "", {}, "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w=="],
|
||||
@@ -3347,14 +3348,12 @@
|
||||
|
||||
"@prisma/internals/@prisma/debug/debug/ms": ["ms@2.1.2", "", {}, "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="],
|
||||
|
||||
"@so-ric/colorspace/color/color-convert/color-name": ["color-name@2.0.2", "", {}, "sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A=="],
|
||||
|
||||
"@so-ric/colorspace/color/color-string/color-name": ["color-name@2.0.2", "", {}, "sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A=="],
|
||||
|
||||
"boxen/string-width/strip-ansi/ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="],
|
||||
|
||||
"boxen/wrap-ansi/strip-ansi/ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="],
|
||||
|
||||
"colorspace/color/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="],
|
||||
|
||||
"form-render/color/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="],
|
||||
|
||||
"npm-packlist/glob/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="],
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
"fontkit": "^2.0.4",
|
||||
"handlebars": "^4.7.8",
|
||||
"heic2any": "^0.0.4",
|
||||
"highlight.run": "^9.14.0",
|
||||
"is-base64": "^1.1.0",
|
||||
"js-cookie": "^3.0.5",
|
||||
"js-interpolate": "^1.3.2",
|
||||
@@ -69,7 +70,7 @@
|
||||
"tailwindcss": "^3.4.17",
|
||||
"trpc-openapi": "^1.2.0",
|
||||
"uuid": "^9.0.1",
|
||||
"winston": "^3.18.3",
|
||||
"winston": "^3.17.0",
|
||||
"zod": "^3.24.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- You are about to drop the column `anrede` on the `benutzer` table. All the data in the column will be lost.
|
||||
|
||||
*/
|
||||
-- AlterTable
|
||||
ALTER TABLE "benutzer" DROP COLUMN "anrede",
|
||||
ADD COLUMN "empfaenger" VARCHAR(100);
|
||||
@@ -17,7 +17,7 @@ model Benutzer {
|
||||
ort String? @db.VarChar(50)
|
||||
adresse String? @db.VarChar(150)
|
||||
telefon String? @db.VarChar(50)
|
||||
empfaenger String? @db.VarChar(100)
|
||||
anrede String? @db.VarChar(50)
|
||||
rolle BenutzerRolle @default(USER)
|
||||
firma String?
|
||||
lex_office_id String?
|
||||
|
||||
@@ -5,6 +5,12 @@ export const createCaller = createCallerFactory({
|
||||
"klimafaktoren": await import("../src/pages/api/klimafaktoren.ts"),
|
||||
"postleitzahlen": await import("../src/pages/api/postleitzahlen.ts"),
|
||||
"unterlage": await import("../src/pages/api/unterlage.ts"),
|
||||
"ausweise": await import("../src/pages/api/ausweise/index.ts"),
|
||||
"bedarfsausweis-gewerbe/[id]": await import("../src/pages/api/bedarfsausweis-gewerbe/[id].ts"),
|
||||
"bedarfsausweis-gewerbe": await import("../src/pages/api/bedarfsausweis-gewerbe/index.ts"),
|
||||
"aufnahme": await import("../src/pages/api/aufnahme/index.ts"),
|
||||
"bedarfsausweis-wohnen/[id]": await import("../src/pages/api/bedarfsausweis-wohnen/[id].ts"),
|
||||
"bedarfsausweis-wohnen": await import("../src/pages/api/bedarfsausweis-wohnen/index.ts"),
|
||||
"admin/ausstellen": await import("../src/pages/api/admin/ausstellen.ts"),
|
||||
"admin/bedarfsausweis-ausstellen": await import("../src/pages/api/admin/bedarfsausweis-ausstellen.ts"),
|
||||
"admin/bestellbestaetigung": await import("../src/pages/api/admin/bestellbestaetigung.ts"),
|
||||
@@ -12,25 +18,16 @@ export const createCaller = createCallerFactory({
|
||||
"admin/nicht-ausstellen": await import("../src/pages/api/admin/nicht-ausstellen.ts"),
|
||||
"admin/registriernummer": await import("../src/pages/api/admin/registriernummer.ts"),
|
||||
"admin/stornieren": await import("../src/pages/api/admin/stornieren.ts"),
|
||||
"ausweise": await import("../src/pages/api/ausweise/index.ts"),
|
||||
"aufnahme": await import("../src/pages/api/aufnahme/index.ts"),
|
||||
"auth/access-token": await import("../src/pages/api/auth/access-token.ts"),
|
||||
"auth/passwort-vergessen": await import("../src/pages/api/auth/passwort-vergessen.ts"),
|
||||
"auth/refresh-token": await import("../src/pages/api/auth/refresh-token.ts"),
|
||||
"bedarfsausweis-gewerbe/[id]": await import("../src/pages/api/bedarfsausweis-gewerbe/[id].ts"),
|
||||
"bedarfsausweis-gewerbe": await import("../src/pages/api/bedarfsausweis-gewerbe/index.ts"),
|
||||
"geg-nachweis-gewerbe/[id]": await import("../src/pages/api/geg-nachweis-gewerbe/[id].ts"),
|
||||
"geg-nachweis-gewerbe": await import("../src/pages/api/geg-nachweis-gewerbe/index.ts"),
|
||||
"bilder/[id]": await import("../src/pages/api/bilder/[id].ts"),
|
||||
"geg-nachweis-wohnen/[id]": await import("../src/pages/api/geg-nachweis-wohnen/[id].ts"),
|
||||
"geg-nachweis-wohnen": await import("../src/pages/api/geg-nachweis-wohnen/index.ts"),
|
||||
"bedarfsausweis-wohnen/[id]": await import("../src/pages/api/bedarfsausweis-wohnen/[id].ts"),
|
||||
"bedarfsausweis-wohnen": await import("../src/pages/api/bedarfsausweis-wohnen/index.ts"),
|
||||
"objekt": await import("../src/pages/api/objekt/index.ts"),
|
||||
"auth/access-token": await import("../src/pages/api/auth/access-token.ts"),
|
||||
"auth/passwort-vergessen": await import("../src/pages/api/auth/passwort-vergessen.ts"),
|
||||
"auth/refresh-token": await import("../src/pages/api/auth/refresh-token.ts"),
|
||||
"rechnung/[id]": await import("../src/pages/api/rechnung/[id].ts"),
|
||||
"rechnung/anfordern": await import("../src/pages/api/rechnung/anfordern.ts"),
|
||||
"rechnung": await import("../src/pages/api/rechnung/index.ts"),
|
||||
"user/autocreate": await import("../src/pages/api/user/autocreate.ts"),
|
||||
"user": await import("../src/pages/api/user/index.ts"),
|
||||
"user/self": await import("../src/pages/api/user/self.ts"),
|
||||
"ticket": await import("../src/pages/api/ticket/index.ts"),
|
||||
@@ -38,6 +35,9 @@ export const createCaller = createCallerFactory({
|
||||
"verbrauchsausweis-wohnen": await import("../src/pages/api/verbrauchsausweis-wohnen/index.ts"),
|
||||
"verbrauchsausweis-gewerbe/[id]": await import("../src/pages/api/verbrauchsausweis-gewerbe/[id].ts"),
|
||||
"verbrauchsausweis-gewerbe": await import("../src/pages/api/verbrauchsausweis-gewerbe/index.ts"),
|
||||
"ticket": await import("../src/pages/api/ticket/index.ts"),
|
||||
"verbrauchsausweis-wohnen/[id]": await import("../src/pages/api/verbrauchsausweis-wohnen/[id].ts"),
|
||||
"verbrauchsausweis-wohnen": await import("../src/pages/api/verbrauchsausweis-wohnen/index.ts"),
|
||||
"webhooks/mollie": await import("../src/pages/api/webhooks/mollie.ts"),
|
||||
"aufnahme/[id]/bilder": await import("../src/pages/api/aufnahme/[id]/bilder.ts"),
|
||||
"aufnahme/[id]": await import("../src/pages/api/aufnahme/[id]/index.ts"),
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
import { api } from "astro-typesafe-api/client";
|
||||
import Cookies from "js-cookie";
|
||||
import { API_ACCESS_TOKEN_COOKIE_NAME } from "#lib/constants.js";
|
||||
import {
|
||||
AufnahmeClient,
|
||||
BedarfsausweisWohnenClient,
|
||||
BildClient,
|
||||
ObjektClient,
|
||||
VerbrauchsausweisGewerbeClient,
|
||||
VerbrauchsausweisWohnenClient,
|
||||
} from "#components/Ausweis/types.js";
|
||||
import {
|
||||
Aufnahme,
|
||||
BedarfsausweisWohnen,
|
||||
|
||||
@@ -170,6 +170,7 @@ export async function benutzerSpeichern(benutzer: Partial<Benutzer>): Promise<st
|
||||
email: benutzer.email,
|
||||
passwort: "",
|
||||
adresse: benutzer.adresse ?? null,
|
||||
anrede: benutzer.anrede ?? null,
|
||||
firma: benutzer.firma ?? null,
|
||||
vorname: benutzer.vorname ?? null,
|
||||
ort: benutzer.ort ?? null,
|
||||
|
||||
@@ -75,7 +75,7 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
|
||||
>
|
||||
<option disabled selected value={null}>Bitte auswählen</option>
|
||||
|
||||
{#if ausweisart==Enums.Ausweisart.VerbrauchsausweisWohnen || ausweisart === Enums.Ausweisart.GEGNachweisWohnen || ausweisart === Enums.Ausweisart.BedarfsausweisWohnen}
|
||||
{#if ausweisart==Enums.Ausweisart.VerbrauchsausweisWohnen || ausweisart === Enums.Ausweisart.GEGNachweisWohnen || ausweisart === Enums.Ausweisart.BedarfsausweisWohnen || ausweisart === Enums.Ausweisart.BedarfsausweisGewerbe}
|
||||
<option value="Einfamilienhaus">Einfamilienhaus</option>
|
||||
<option value="Freistehendes Einfamilienhaus">Freistehendes Einfamilienhaus</option>
|
||||
<option value="Freistehendes Zweifamilienhaus">Freistehendes Zweifamilienhaus</option>
|
||||
@@ -87,7 +87,7 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
|
||||
<option value="Atrium-Bungalow">Atrium-Bungalow</option>
|
||||
<option value="Winkelbungalow">Winkelbungalow</option>
|
||||
|
||||
{:else if ausweisart==Enums.Ausweisart.VerbrauchsausweisGewerbe || ausweisart=== Enums.Ausweisart.GEGNachweisGewerbe || ausweisart === Enums.Ausweisart.BedarfsausweisGewerbe}
|
||||
{:else if ausweisart==Enums.Ausweisart.VerbrauchsausweisGewerbe || ausweisart=== Enums.Ausweisart.GEGNachweisGewerbe}
|
||||
<option value="Verwaltungsgebäude (allgemein)">Verwaltungsgebäude (allgemein)</option>
|
||||
<option value="Parlaments- und Gerichtsgebäude">Parlaments- und Gerichtsgebäude</option>
|
||||
<option value="Ministerien u. Ämter u. Behörden">Ministerien u. Ämter u. Behörden</option>
|
||||
@@ -186,7 +186,6 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
|
||||
onlyUnique={true}
|
||||
minlength={4}
|
||||
maxlength={4}
|
||||
required={true}
|
||||
onFocusIn={() => {
|
||||
addNotification({
|
||||
message: "Info",
|
||||
@@ -250,7 +249,6 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
|
||||
minlength={4}
|
||||
maxlength={4}
|
||||
onlyUnique={true}
|
||||
required={true}
|
||||
onFocusIn={() => {
|
||||
addNotification({
|
||||
message: "Info",
|
||||
@@ -289,7 +287,6 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
|
||||
onlyUnique={true}
|
||||
minlength={4}
|
||||
maxlength={4}
|
||||
required={true}
|
||||
onFocusIn={() => {
|
||||
addNotification({
|
||||
message: "Info",
|
||||
|
||||
@@ -44,17 +44,11 @@
|
||||
// Wir holen uns die Daten aus dem Formular
|
||||
const data = new FormData(form);
|
||||
// Und gleichen diese mit allen Feldern ab die "required" sind, damit stellen wir sicher, dass alles richtig ausgefüllt wurde.
|
||||
(form.querySelectorAll("select[name][required], input[name][required]") as NodeListOf<HTMLInputElement | HTMLSelectElement>).forEach((element) => {
|
||||
(form.querySelectorAll("select[name][required],input[name][required]") as NodeListOf<HTMLInputElement | HTMLSelectElement>).forEach((element) => {
|
||||
const value = data.get(element.getAttribute("name") as string)
|
||||
|
||||
if (!value && element.required) {
|
||||
element.setCustomValidity("Eine Auswahl ist verpflichtend.")
|
||||
element.dataset["isinvalid"] = "true"
|
||||
|
||||
element.addEventListener("change", () => {
|
||||
element.setCustomValidity("")
|
||||
element.dataset["isinvalid"] = "false"
|
||||
})
|
||||
} else {
|
||||
element.setCustomValidity("")
|
||||
}
|
||||
@@ -184,15 +178,7 @@ sm:grid-cols-[1fr_min-content_min-content_min-content] sm:justify-self-end">
|
||||
|
||||
<Overlay bind:hidden={loginOverlayHidden}>
|
||||
<div class="bg-white w-full max-w-screen-sm py-8">
|
||||
<EmbeddedAuthFlowModule onLogin={loginAction} email={""} route="signup" title={
|
||||
{
|
||||
login: "Ausweisdaten speichern",
|
||||
signup: "Ausweisdaten speichern"
|
||||
}
|
||||
} buttonText={{
|
||||
login: "Speichern",
|
||||
signup: "Speichern"
|
||||
}}></EmbeddedAuthFlowModule>
|
||||
<EmbeddedAuthFlowModule onLogin={loginAction} email={""}></EmbeddedAuthFlowModule>
|
||||
</div>
|
||||
</Overlay>
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
<script lang="ts">
|
||||
import HelpLabel from "#components/labels/HelpLabel.svelte";
|
||||
import Inputlabel from "#components/labels/InputLabel.svelte";
|
||||
import PlzSuche from "#components/PlzSuche.svelte";
|
||||
|
||||
|
||||
import ZipSearch from "#components/PlzSuche.svelte";
|
||||
import { Enums } from "#lib/client/prisma.js";
|
||||
import { AufnahmeClient, ObjektClient } from "./types.js";
|
||||
|
||||
@@ -26,8 +27,6 @@
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
let ortInput: HTMLInputElement;
|
||||
</script>
|
||||
|
||||
<div
|
||||
@@ -70,12 +69,9 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
|
||||
<div class="input-noHelp">
|
||||
<Inputlabel title="PLZ *"></Inputlabel>
|
||||
|
||||
<PlzSuche
|
||||
<ZipSearch
|
||||
bind:zip={objekt.plz}
|
||||
bind:city={objekt.ort}
|
||||
onchange={(e) => {
|
||||
ortInput.dispatchEvent(new Event('change'));
|
||||
}}
|
||||
name="plz"
|
||||
/>
|
||||
</div>
|
||||
@@ -89,7 +85,6 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
|
||||
readonly={false}
|
||||
required
|
||||
bind:value={objekt.ort}
|
||||
bind:this={ortInput}
|
||||
type="text"
|
||||
/>
|
||||
|
||||
|
||||
@@ -186,7 +186,7 @@ xl:grid-cols-2 xl:gap-x-8 xl:gap-y-8
|
||||
>
|
||||
<option>Bitte auswählen</option>
|
||||
{#each arrayRange(2.1, 4.5, 0.1) as step}
|
||||
<option value={step} selected={ausweis.geschosshoehe === step}>{step.toFixed(2)} m</option>
|
||||
<option value={step}>{step.toFixed(2)} m</option>
|
||||
{/each}
|
||||
</select>
|
||||
|
||||
|
||||
@@ -746,29 +746,24 @@
|
||||
<div class="card-body">
|
||||
<div class="flex flex-col flex-wrap items-left gap-2">
|
||||
|
||||
{#if aufnahme.bilder.length > 0}
|
||||
<h3 class="font-semibold text-lg">Bilder</h3>
|
||||
<div class="grid grid-cols-[1fr] md:grid-cols-[1fr,1fr,1fr] lg:grid-cols-[1fr,1fr,1fr] justify-start items-center gap-2">
|
||||
{#each aufnahme.bilder as bild, i (i)}
|
||||
<img src="/bilder/{bild.id}.jpg" alt={bild.kategorie} loading="lazy" class="max-h-[10vh] h-full w-full object-contain">
|
||||
<h3 class="font-semibold text-lg">Bilder</h3>
|
||||
<div class="grid grid-cols-[1fr] md:grid-cols-[1fr,1fr,1fr] lg:grid-cols-[1fr,1fr,1fr] justify-start items-center gap-2">
|
||||
{#each aufnahme.bilder as bild, i (i)}
|
||||
<img src="/bilder/{bild.id}.jpg" alt={bild.kategorie} loading="lazy" class="max-h-[10vh] h-full w-full object-contain">
|
||||
{/each}
|
||||
</div>
|
||||
<hr>
|
||||
<h3 class="font-semibold text-lg">Unterlagen</h3>
|
||||
<div class="text-sm">
|
||||
{#if aufnahme.unterlagen.length > 0}
|
||||
{#each aufnahme.unterlagen as unterlage}
|
||||
<a href="/unterlagen/{unterlage.id}.{mime.getExtension(unterlage.mime)}" target="_blank" class="text-black flex flex-row items-center gap-2 bg-base-200 p-2 rounded-lg"><FileText size={32}></FileText> {unterlage.name}</a>
|
||||
{/each}
|
||||
</div>
|
||||
<hr>
|
||||
{/if}
|
||||
{#if aufnahme.unterlagen.length > 0}
|
||||
<h3 class="font-semibold text-lg">Unterlagen</h3>
|
||||
<div class="text-sm">
|
||||
{#if aufnahme.unterlagen.length > 0}
|
||||
{#each aufnahme.unterlagen as unterlage}
|
||||
<a href="/unterlagen/{unterlage.id}.{mime.getExtension(unterlage.mime)}" target="_blank" class="text-black flex flex-row items-center gap-2 bg-base-200 p-2 rounded-lg"><FileText size={32}></FileText> {unterlage.name}</a>
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Benachrichtigungen -->
|
||||
<!-- <div class="dropdown dropdown-top items-end absolute bottom-4 right-4 z-50">
|
||||
<div class="dropdown dropdown-top items-end absolute bottom-4 right-4 z-50">
|
||||
<div class="indicator">
|
||||
{#if Object.keys($notifications).length > 0}
|
||||
<span class="indicator-item badge badge-accent text-xs"
|
||||
@@ -787,7 +782,7 @@
|
||||
>
|
||||
<NotificationProvider component={DashboardNotification} />
|
||||
</ul>
|
||||
</div> -->
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<script lang="ts">
|
||||
import { BedarfsausweisWohnenClient, ObjektClient, VerbrauchsausweisGewerbeClient, VerbrauchsausweisWohnenClient } from "./Ausweis/types.js";
|
||||
import { BedarfsausweisWohnenClient, GEGNachweisWohnenClient, ObjektClient, UnterlageClient, VerbrauchsausweisGewerbeClient, VerbrauchsausweisWohnenClient } from "./Ausweis/types.js";
|
||||
import { Trash, Upload } from "radix-svelte-icons";
|
||||
import HelpLabel from "#components/labels/HelpLabel.svelte";
|
||||
|
||||
export let kategorie: string = "";
|
||||
export let files: (Unterlage & { preview?: boolean })[] = [];
|
||||
export let files: Unterlage[] = [];
|
||||
export let max: number = Infinity;
|
||||
export let min: number = 1;
|
||||
export let name: string = "";
|
||||
@@ -26,8 +26,6 @@
|
||||
|
||||
for (let i = 0; i < fileArray.length; i++) {
|
||||
const file = fileArray[i];
|
||||
files.push({ preview: true, kategorie, name: file.name, mime: file.type, aufnahme_id: null, id: "" });
|
||||
files = files;
|
||||
|
||||
if (i == max) {
|
||||
break;
|
||||
@@ -63,14 +61,8 @@
|
||||
mime: mimeType,
|
||||
name: file.name
|
||||
})
|
||||
|
||||
const placeholder = files.find((f) => f.name === fileArray[i].name && f.preview === true && f.kategorie === kategorie);
|
||||
if (!placeholder) {
|
||||
return;
|
||||
}
|
||||
placeholder!.preview = false;
|
||||
placeholder!.id = id;
|
||||
placeholder!.aufnahme_id = ausweis.id;
|
||||
|
||||
files.push({ id, kategorie, name: file.name, mime: mimeType, aufnahme_id: null });
|
||||
files = files;
|
||||
|
||||
|
||||
@@ -118,37 +110,23 @@
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
{#each files as file, i}
|
||||
{#if file.kategorie == kategorie}
|
||||
{#if file.preview === true}
|
||||
<!-- Show loading spinner -->
|
||||
<div class="relative group">
|
||||
<div
|
||||
class="h-full max-h-96 w-full rounded-lg border-2 group-hover:contrast-50 object-cover transition-all text-center items-center justify-center flex p-4 text-opacity-75 text-black"
|
||||
>
|
||||
<div class="flex flex-row gap-4">
|
||||
<span>{file.name}</span>
|
||||
<span class="loader"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="relative group">
|
||||
<div
|
||||
class="h-full max-h-96 w-full rounded-lg border-2 group-hover:contrast-50 object-cover transition-all text-center items-center flex p-4 text-opacity-75 text-black"
|
||||
>{file.name}</div>
|
||||
<div class="invisible group-hover:visible absolute left-[50%] top-[50%] translate-x-[-50%] translate-y-[-50%] flex flex-row gap-2">
|
||||
<button
|
||||
type="button"
|
||||
class="rounded-full w-[30px] h-[30px] flex items-center justify-center p-0 bg-[rgba(0,0,0,0.4)]"
|
||||
on:click={() => {
|
||||
delete files[i];
|
||||
files = files.filter((x) => x);
|
||||
}}
|
||||
>
|
||||
<Trash size={20} color="#fff"></Trash>
|
||||
</button>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="relative group">
|
||||
<div
|
||||
class="h-full max-h-96 w-full rounded-lg border-2 group-hover:contrast-50 object-cover transition-all text-center items-center flex p-4 text-opacity-75 text-black"
|
||||
>{file.name}</div>
|
||||
<div class="invisible group-hover:visible absolute left-[50%] top-[50%] translate-x-[-50%] translate-y-[-50%] flex flex-row gap-2">
|
||||
<button
|
||||
type="button"
|
||||
class="rounded-full w-[30px] h-[30px] flex items-center justify-center p-0 bg-[rgba(0,0,0,0.4)]"
|
||||
on:click={() => {
|
||||
delete files[i];
|
||||
files = files.filter((x) => x);
|
||||
}}
|
||||
>
|
||||
<Trash size={20} color="#fff"></Trash>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
import Cookies from "js-cookie";
|
||||
import { API_ACCESS_TOKEN_COOKIE_NAME } from "#lib/constants.js";
|
||||
|
||||
export let images: (BildClient & { preview?: boolean })[] = [];
|
||||
export let images: BildClient[] = [];
|
||||
export let max: number = Infinity;
|
||||
export let min: number = 1;
|
||||
export let name: string = "";
|
||||
@@ -35,43 +35,40 @@
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
{#each images as image, i}
|
||||
{#if image.kategorie == kategorie}
|
||||
{#if image.preview === true}
|
||||
<!-- Show loading spinner -->
|
||||
<div class="relative group">
|
||||
<div
|
||||
class="h-full max-h-96 w-full rounded-lg border-2 group-hover:contrast-50 object-cover transition-all text-center items-center justify-center flex p-4 text-opacity-75 text-black"
|
||||
>
|
||||
<div class="flex flex-row gap-4">
|
||||
<span class="loader"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="relative group">
|
||||
<img
|
||||
src="/bilder/{image.id}.jpg"
|
||||
alt={kategorie}
|
||||
class="h-full max-h-96 w-full rounded-lg border-2 group-hover:contrast-50 object-cover transition-all"
|
||||
/>
|
||||
<div class="invisible group-hover:visible absolute left-[50%] top-[50%] translate-x-[-50%] translate-y-[-50%] flex flex-row gap-2">
|
||||
<button
|
||||
type="button"
|
||||
class="rounded-full w-[30px] h-[30px] flex items-center justify-center p-0 bg-[rgba(0,0,0,0.4)]"
|
||||
on:click={() => {
|
||||
deleteImage(images[i])
|
||||
}}
|
||||
>
|
||||
<Trash size={20} color="#fff"></Trash>
|
||||
</button>
|
||||
</div>
|
||||
<div class="relative group">
|
||||
<img
|
||||
src="/bilder/{image.id}.jpg"
|
||||
alt={kategorie}
|
||||
class="h-full max-h-96 w-full rounded-lg border-2 group-hover:contrast-50 object-cover transition-all"
|
||||
/>
|
||||
<div class="invisible group-hover:visible absolute left-[50%] top-[50%] translate-x-[-50%] translate-y-[-50%] flex flex-row gap-2">
|
||||
<button
|
||||
type="button"
|
||||
class="rounded-full w-[30px] h-[30px] flex items-center justify-center p-0 bg-[rgba(0,0,0,0.4)]"
|
||||
on:click={() => {
|
||||
deleteImage(images[i])
|
||||
}}
|
||||
>
|
||||
<Trash size={20} color="#fff"></Trash>
|
||||
</button>
|
||||
<!-- <button
|
||||
type="button"
|
||||
class="rounded-full w-[30px] h-[30px] flex items-center justify-center p-0 bg-[rgba(0,0,0,0.4)]"
|
||||
on:click={async () => {
|
||||
let image = await rotateImage(images[i]);
|
||||
images[i] = image;
|
||||
images = images
|
||||
}}
|
||||
>
|
||||
<RotateCounterClockwise size={20} color="#fff"></RotateCounterClockwise>
|
||||
</button> -->
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
|
||||
<!-- Wir zeigen Platzhalter an, damit der Nutzer sieht wie viele Bilder er hochladen soll -->
|
||||
{#each { length: Math.max(0, Math.min(max, 4) - images.filter(image => image.kategorie === kategorie).length) } as _, i}
|
||||
|
||||
<div class="relative group">
|
||||
<img
|
||||
src="/placeholder.png"
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
export let readonly: boolean = false;
|
||||
export let city: string | null | undefined;
|
||||
export let zip: string | null = "";
|
||||
export let onchange: (event: Event) => void = () => {};
|
||||
|
||||
|
||||
let hideZipDropdown: boolean = true;
|
||||
let zipCodes: inferOutput<API["postleitzahlen"]["GET"]> = [];
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
export let onFocusIn: () => any = () => {};
|
||||
export let onFocusOut: () => any = () => {};
|
||||
export let className: string = "";
|
||||
export let required: boolean = false;
|
||||
|
||||
function addTag(tag: string) {
|
||||
if ((onlyUnique && tags.indexOf(tag) > -1) || maxTags == tags.length) {
|
||||
@@ -49,8 +48,6 @@
|
||||
tag = ""
|
||||
}
|
||||
}
|
||||
|
||||
$: required = tags.length > 0 ? false : required;
|
||||
</script>
|
||||
|
||||
<div
|
||||
@@ -86,7 +83,6 @@
|
||||
class="input input-bordered h-10 px-2 py-1.5 {className}"
|
||||
{minlength}
|
||||
{maxlength}
|
||||
{required}
|
||||
disabled={disable}
|
||||
readonly={readonly}
|
||||
autocomplete="off"
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
import { addNotification } from "./Notifications/shared.js";
|
||||
import { API_ACCESS_TOKEN_COOKIE_NAME } from "#lib/constants.js";
|
||||
|
||||
export let images: (BildClient & { preview?: boolean })[] = [];
|
||||
export let images: BildClient[] = [];
|
||||
export let ausweis:
|
||||
| VerbrauchsausweisWohnenClient
|
||||
| VerbrauchsausweisGewerbeClient
|
||||
@@ -39,6 +39,8 @@
|
||||
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
const file = files[i];
|
||||
console.log(file);
|
||||
|
||||
|
||||
if (file.type !== "image/jpeg" && file.type !== "image/png" && file.type !== "image/webp" && file.type !== "image/heif" && file.type !== "image/heic") {
|
||||
continue;
|
||||
@@ -48,9 +50,6 @@
|
||||
break;
|
||||
}
|
||||
|
||||
images.push({ preview: true, benutzer_id: null, kategorie, created_at: new Date(), updated_at: new Date(), id: "" });
|
||||
images = images;
|
||||
|
||||
const reader = new FileReader();
|
||||
|
||||
reader.onload = async () => {
|
||||
@@ -120,19 +119,8 @@
|
||||
dismissable: true
|
||||
})
|
||||
} else {
|
||||
const index = images.findIndex((img) => img.preview === true);
|
||||
|
||||
if (index !== -1) {
|
||||
delete images[index];
|
||||
images = images.filter((img) => img);
|
||||
}
|
||||
images = [...images, {
|
||||
benutzer_id: null,
|
||||
created_at: new Date(),
|
||||
updated_at: new Date(),
|
||||
id: result.id,
|
||||
kategorie
|
||||
}];
|
||||
images.push({ id: result.id, kategorie });
|
||||
images = images;
|
||||
|
||||
if (i == Math.min(files.length, max) - 1) {
|
||||
this.value = "";
|
||||
|
||||
@@ -2,11 +2,6 @@ import { prisma } from "#lib/server/prisma.js";
|
||||
import moment from "moment";
|
||||
import csv from "csvtojson"
|
||||
|
||||
if (!process.env.prev_restart_delay && process.env.cron_restart) {
|
||||
console.log('skipping initial launch....');
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// Als erstes schauen wir, welches das letzte Jahr ist, für das wir einen Verbrauchsausweis haben.
|
||||
// Das machen wir, indem wir die Ausweise nach Jahr und Monat sortieren und dann den letzten Eintrag nehmen.
|
||||
const newestDate = await prisma.klimafaktoren.findFirst({
|
||||
|
||||
@@ -13,7 +13,7 @@ export const BenutzerSchema = z.object({
|
||||
ort: z.string().nullish(),
|
||||
adresse: z.string().nullish(),
|
||||
telefon: z.string().nullish(),
|
||||
empfaenger: z.string().nullish(),
|
||||
anrede: z.string().nullish(),
|
||||
rolle: z.nativeEnum(BenutzerRolle),
|
||||
firma: z.string().nullish(),
|
||||
lex_office_id: z.string().nullish(),
|
||||
|
||||
@@ -18,6 +18,20 @@ const { title } = Astro.props;
|
||||
---
|
||||
|
||||
<script>
|
||||
// import { H } from "highlight.run";
|
||||
|
||||
// if (import.meta.env.PROD) {
|
||||
// H.init("1jdkoe52", {
|
||||
// serviceName: "online-energieausweis",
|
||||
// backendUrl: "https://highlight-backend.online-energieausweis.org/public",
|
||||
// tracingOrigins: true,
|
||||
// networkRecording: {
|
||||
// enabled: true,
|
||||
// recordHeadersAndBody: true
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
|
||||
window.addEventListener("scroll", () => {
|
||||
const skala = document.getElementById("skala");
|
||||
|
||||
|
||||
@@ -17,6 +17,22 @@ export interface Props {
|
||||
const { title } = Astro.props;
|
||||
---
|
||||
|
||||
<script>
|
||||
// import { H } from "highlight.run";
|
||||
|
||||
// if (import.meta.env.PROD) {
|
||||
// H.init("1jdkoe52", {
|
||||
// serviceName: "online-energieausweis",
|
||||
// backendUrl: "https://highlight-backend.online-energieausweis.org/public",
|
||||
// tracingOrigins: true,
|
||||
// networkRecording: {
|
||||
// enabled: true,
|
||||
// recordHeadersAndBody: true
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
</script>
|
||||
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
|
||||
@@ -34,6 +34,22 @@ const schema = JSON.stringify({
|
||||
});
|
||||
---
|
||||
|
||||
<script>
|
||||
// import { H } from "highlight.run";
|
||||
|
||||
// if (import.meta.env.PROD) {
|
||||
// H.init("1jdkoe52", {
|
||||
// serviceName: "online-energieausweis",
|
||||
// backendUrl: "https://highlight-backend.online-energieausweis.org/public",
|
||||
// tracingOrigins: true,
|
||||
// networkRecording: {
|
||||
// enabled: true,
|
||||
// recordHeadersAndBody: true
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
</script>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
|
||||
@@ -45,6 +45,31 @@ const schema = JSON.stringify({
|
||||
let lightTheme = Astro.cookies.get("theme")?.value === "light";
|
||||
---
|
||||
|
||||
<script >
|
||||
// import { H } from "highlight.run";
|
||||
|
||||
// const user = JSON.parse(document.body.dataset.user);
|
||||
|
||||
// if (import.meta.env.PROD) {
|
||||
// H.init("1jdkoe52", {
|
||||
// serviceName: "online-energieausweis",
|
||||
// backendUrl:
|
||||
// "https://highlight-backend.online-energieausweis.org/public",
|
||||
// tracingOrigins: true,
|
||||
// networkRecording: {
|
||||
// enabled: true,
|
||||
// recordHeadersAndBody: true,
|
||||
// }
|
||||
// });
|
||||
|
||||
// if (user) {
|
||||
// H.identify(user.email, {
|
||||
// id: user.id
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
</script>
|
||||
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
|
||||
@@ -19,6 +19,19 @@ const { title } = Astro.props;
|
||||
---
|
||||
|
||||
<script>
|
||||
// import { H } from "highlight.run";
|
||||
|
||||
// if (import.meta.env.PROD) {
|
||||
// H.init("1jdkoe52", {
|
||||
// serviceName: "online-energieausweis",
|
||||
// backendUrl: "https://highlight-backend.online-energieausweis.org/public",
|
||||
// tracingOrigins: true,
|
||||
// networkRecording: {
|
||||
// enabled: true,
|
||||
// recordHeadersAndBody: true
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
|
||||
/*
|
||||
window.addEventListener("scroll", (event) => {
|
||||
|
||||
@@ -3,7 +3,7 @@ import { memoize } from "./Memoization.js";
|
||||
import { api } from "astro-typesafe-api/client"
|
||||
|
||||
export const getKlimafaktoren = memoize(async (date: Date, plz: string) => {
|
||||
if (!plz || plz.length < 5 || !date) {
|
||||
if (!plz || !date) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
import winston, { format} from "winston";
|
||||
|
||||
const { combine, timestamp } = format;
|
||||
|
||||
const loggingFormat = format.printf(({ level, message, timestamp }) => {
|
||||
return `${timestamp} [${level}]: ${message}`;
|
||||
});
|
||||
|
||||
export const logger = winston.createLogger({
|
||||
level: "info",
|
||||
format: combine(
|
||||
timestamp(),
|
||||
loggingFormat
|
||||
),
|
||||
transports: [
|
||||
new winston.transports.File({ filename: "error.log", level: "error" }),
|
||||
new winston.transports.File({ filename: "combined.log" }),
|
||||
],
|
||||
});
|
||||
|
||||
if (process.env.NODE_ENV !== "production") {
|
||||
logger.add(new winston.transports.Console({
|
||||
format: combine(timestamp(), loggingFormat),
|
||||
}));
|
||||
}
|
||||
@@ -2,7 +2,6 @@ import os from "os"
|
||||
import fs from "fs"
|
||||
|
||||
export const PERSISTENT_DIR = `${os.homedir()}/persistent/online-energieausweis`
|
||||
export const SECRET = process.env.SECRET as string
|
||||
|
||||
if (!fs.existsSync(PERSISTENT_DIR)) {
|
||||
fs.mkdirSync(PERSISTENT_DIR, {recursive: true})
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
import { transport } from "#lib/mail.js";
|
||||
import {
|
||||
Benutzer,
|
||||
} from "#lib/client/prisma.js";
|
||||
|
||||
export async function sendAutoRegisterMail(
|
||||
user: Benutzer,
|
||||
password: string
|
||||
) {
|
||||
await transport.sendMail({
|
||||
from: `"IBCornelsen" <info@online-energieausweis.org>`,
|
||||
to: user.email,
|
||||
subject: `Ihre Registrierung bei IBCornelsen`,
|
||||
bcc: "info@online-energieausweis.org",
|
||||
html: `<p>Sehr geehrte/r Kund/in,</p>
|
||||
<p>vielen Dank für Ihre Registrierung bei IBCornelsen. Ihr Benutzerkonto wurde erfolgreich erstellt.<br><br>
|
||||
|
||||
Nachfolgend finden Sie Ihre Zugangsdaten:<br><br>
|
||||
E-Mail: ${user.email}<br>
|
||||
Passwort: ${password}<br><br>
|
||||
|
||||
Sollten Sie diese Registrierung nicht vorgenommen haben, können Sie diese E-Mail einfach ignorieren. Ihr Benutzerkonto wird in diesem Fall nicht aktiviert.<br><br>
|
||||
|
||||
Falls Sie Fragen haben oder Unterstützung benötigen, stehen wir Ihnen gerne zur Verfügung. Kontaktieren Sie uns einfach unter <a href="mailto:support@online-energieausweis.org">support@online-energieausweis.org</a>.
|
||||
<p>
|
||||
Mit freundlichen Grüßen,
|
||||
<br>
|
||||
Dipl.-Ing. Jens Cornelsen
|
||||
<br>
|
||||
<br>
|
||||
<strong>IB Cornelsen</strong>
|
||||
<br>
|
||||
Katendeich 5A
|
||||
<br>
|
||||
21035 Hamburg
|
||||
<br>
|
||||
www.online-energieausweis.org
|
||||
<br>
|
||||
<br>
|
||||
fon 040 · 209339850
|
||||
<br>
|
||||
fax 040 · 209339859
|
||||
</p>`
|
||||
});
|
||||
}
|
||||
@@ -75,6 +75,6 @@ export async function sendInvoiceMail(
|
||||
name: rechnung.empfaenger || "",
|
||||
},
|
||||
bcc: "info@online-energieausweis.org",
|
||||
html: `<p>Sehr geehrte/r ${user.vorname} ${user.name},</p>` + getPaymentInvoiceBody(ausweis, rechnung, ausweisart),
|
||||
html: `<p>Sehr geehrte*r ${user.vorname} ${user.name},</p>` + getPaymentInvoiceBody(ausweis, rechnung, ausweisart),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -80,6 +80,6 @@ export async function sendPaymentSuccessMail(
|
||||
name: rechnung.empfaenger || "",
|
||||
},
|
||||
bcc: "info@online-energieausweis.org",
|
||||
html: `<p>Sehr geehrte/r ${user.vorname} ${user.name},</p>` + getPaymentSuccessBody(ausweis, rechnung, ausweisart),
|
||||
html: `<p>Sehr geehrte*r ${user.vorname} ${user.name},</p>` + getPaymentSuccessBody(ausweis, rechnung, ausweisart),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,30 +1,31 @@
|
||||
import { BASE_URI } from "#lib/constants.js";
|
||||
import { transport } from "#lib/mail.js";
|
||||
import {
|
||||
Benutzer,
|
||||
} from "#lib/client/prisma.js";
|
||||
import { encodeToken } from "#lib/auth/token.js";
|
||||
import { TokenType } from "#lib/auth/types.js";
|
||||
|
||||
export async function sendRegisterMail(
|
||||
user: Benutzer,
|
||||
passwort: string
|
||||
user: Benutzer
|
||||
) {
|
||||
const verificationJwt = encodeToken({
|
||||
typ: TokenType.Verify,
|
||||
exp: Date.now() + (15 * 60 * 1000),
|
||||
id: user.id
|
||||
})
|
||||
|
||||
await transport.sendMail({
|
||||
from: `"IBCornelsen" <info@online-energieausweis.org>`,
|
||||
to: user.email,
|
||||
subject: `Ihre Registrierung bei IBCornelsen`,
|
||||
bcc: "info@online-energieausweis.org",
|
||||
html: `<p>Sehr geehrte/r ${user.vorname} ${user.name},</p>
|
||||
html: `<p>Sehr geehrte*r ${user.vorname} ${user.name},</p>
|
||||
<p>vielen Dank für Ihre Registrierung bei IBCornelsen. Ihr Benutzerkonto wurde erfolgreich erstellt.<br><br>
|
||||
|
||||
Nachfolgend finden Sie Ihre Zugangsdaten:<br><br>
|
||||
E-Mail: ${user.email}<br>
|
||||
Passwort: ${passwort}<br><br>
|
||||
|
||||
Aus Sicherheitsgründen empfehlen wir Ihnen, Ihr Passwort nach dem ersten Login zu ändern.<br><br>
|
||||
Um sich anzumelden, besuchen Sie bitte unsere Website unter <a href="https://online-energieausweis.org/auth/login">online-energieausweis.org/auth/login</a>.<br><br>
|
||||
Um Ihre Registrierung abzuschließen, klicken Sie bitte auf den folgenden Link, um Ihre E-Mail-Adresse zu bestätigen:<br><br>
|
||||
|
||||
Sollten Sie diese Registrierung nicht vorgenommen haben, können Sie diese E-Mail einfach ignorieren. Ihr Benutzerkonto wird in diesem Fall nicht aktiviert.<br><br>
|
||||
|
||||
Falls Sie Fragen haben oder Unterstützung benötigen, stehen wir Ihnen gerne zur Verfügung. Kontaktieren Sie uns einfach unter <a href="mailto:support@online-energieausweis.org">support@online-energieausweis.org</a>.
|
||||
<a href="${BASE_URI}/auth/verify?t=${verificationJwt}">E-Mail-Adresse bestätigen</a><br></p>
|
||||
<p>
|
||||
Mit freundlichen Grüßen,
|
||||
<br>
|
||||
|
||||
@@ -7,7 +7,7 @@ export async function sendAusweisGespeichertMail(user: Benutzer, ausweis_id: str
|
||||
from: `"IBCornelsen" <info@online-energieausweis.org>`,
|
||||
to: user.email,
|
||||
subject: `Ihr Ausweis wurde gespeichert - IBCornelsen - (ID: ${ausweis_id})`,
|
||||
html: `<p>Sehr geehrte/r ${user.vorname} ${user.name},</p>
|
||||
html: `<p>Sehr geehrte*r ${user.vorname} ${user.name},</p>
|
||||
<p>Ihr Energieausweis wurde erfolgreich in Ihrem Konto gespeichert. Sie können ihn jederzeit in Ihrem Kundenbereich abrufen.<br><br>
|
||||
|
||||
Ihre Vorgänge und Ausweise können Sie in Ihrem Kundenkonto einsehen und bearbeiten:<br><br>
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
try {
|
||||
sent = true
|
||||
const response = await api.auth["passwort-vergessen"].GET.fetch({
|
||||
redirect,
|
||||
email
|
||||
})
|
||||
|
||||
@@ -58,7 +57,7 @@
|
||||
{#if showEmailSuccess}
|
||||
<div class="flex-row justify-between" style="margin-top: 10px">
|
||||
<a class="link link-hover"
|
||||
href="/auth/login{redirect ? `?r=${redirect}` : ""}">Einloggen</a
|
||||
href="/auth/login{redirect ? `?redirect=${redirect}` : ""}">Einloggen</a
|
||||
>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
let passwortWiederholen: string;
|
||||
|
||||
export let token: string;
|
||||
export let redirect: string | null = null;
|
||||
|
||||
let disabled = false;
|
||||
|
||||
@@ -29,7 +28,7 @@
|
||||
})
|
||||
|
||||
setTimeout(() => {
|
||||
window.location.href = "/auth/login?r=" + encodeURIComponent(redirect ?? "/dashboard")
|
||||
window.location.href = "/auth/login"
|
||||
}, 5000)
|
||||
} catch (e) {
|
||||
disabled = false
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
<script lang="ts">
|
||||
import {
|
||||
Reader,
|
||||
EnvelopeClosed,
|
||||
Cube,
|
||||
Person,
|
||||
} from "radix-svelte-icons";
|
||||
import { Tabs, Tab, TabList, TabPanel } from "../../components/Tabs/index.js";
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<script lang="ts">
|
||||
import { addNotification } from "#components/Notifications/shared.js";
|
||||
import { loginClient } from "#lib/login.js";
|
||||
import EmbeddedLoginModule from "./EmbeddedLoginModule.svelte"
|
||||
import EmbeddedRegisterModule from "./EmbeddedRegisterModule.svelte"
|
||||
@@ -8,31 +7,22 @@
|
||||
export let email: string = "";
|
||||
export let password: string = "";
|
||||
export let route: "login" | "signup" = "login"
|
||||
export let title: Record<typeof route, string> = {
|
||||
login: "Einloggen",
|
||||
signup: "Registrieren"
|
||||
}
|
||||
export let buttonText: Record<typeof route, string> = {
|
||||
login: "Einloggen",
|
||||
signup: "Registrieren"
|
||||
}
|
||||
|
||||
const navigate = (target: string) => {
|
||||
route = target as typeof route;
|
||||
}
|
||||
|
||||
const loginData = {
|
||||
email,
|
||||
passwort: "",
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if route == "login"}
|
||||
<EmbeddedLoginModule onLogin={onLogin} bind:email bind:password {navigate} title={title.login} buttonText={buttonText.login} />
|
||||
{:else if route == "signup"}
|
||||
<EmbeddedRegisterModule bind:email onRegister={(response) => {
|
||||
addNotification({
|
||||
message: "Registrierung erfolgreich",
|
||||
subtext: "Ein Passwort wurde an ihre Email Adresse gesendet, sie werden nun automatisch weitergeleitet..",
|
||||
type: "success",
|
||||
timeout: 6000,
|
||||
dismissable: true
|
||||
})
|
||||
onLogin(response)
|
||||
}} {navigate} title={title.signup} buttonText={buttonText.signup} />
|
||||
<EmbeddedLoginModule onLogin={onLogin} bind:email bind:password {navigate} />
|
||||
{:else}
|
||||
<EmbeddedRegisterModule bind:email bind:password onRegister={(response) => {
|
||||
email = response.email
|
||||
navigate("login")
|
||||
}} {navigate} />
|
||||
{/if}
|
||||
@@ -1,11 +1,10 @@
|
||||
<script lang="ts">
|
||||
import { addNotification } from "@ibcornelsen/ui";
|
||||
import { loginClient } from "#lib/login.js";
|
||||
|
||||
export let navigate: (target: string) => void;
|
||||
export let email: string;
|
||||
export let password: string;
|
||||
export let title: string = "Einloggen";
|
||||
export let buttonText: string = "Einloggen";
|
||||
|
||||
export let onLogin: (response: Awaited<ReturnType<typeof loginClient>>) => any;
|
||||
|
||||
@@ -14,19 +13,21 @@
|
||||
const response = await loginClient(email, password)
|
||||
|
||||
if (response === null) {
|
||||
error = true;
|
||||
errorMessage = "Das hat leider nicht geklappt, haben sie ihr Passwort und ihre Email Adresse richtig eingegeben?"
|
||||
addNotification({
|
||||
message: "Ups...",
|
||||
subtext: "Das hat leider nicht geklappt, haben sie ihr Passwort und ihre Email Adresse richtig eingegeben?",
|
||||
type: "error",
|
||||
timeout: 6000,
|
||||
dismissable: true
|
||||
})
|
||||
} else {
|
||||
onLogin(response);
|
||||
}
|
||||
}
|
||||
|
||||
let error = false;
|
||||
let errorMessage = "";
|
||||
</script>
|
||||
|
||||
<form class="max-w-md mx-auto" on:submit={login} name="login">
|
||||
<h1 class="text-3xl mb-4 p-0">{title}</h1>
|
||||
<form style="width:50%;margin: 0 auto" on:submit={login} name="login">
|
||||
<h1 class="text-2xl font-semibold mb-6">Einloggen</h1>
|
||||
<div class="flex flex-col gap-4">
|
||||
<div>
|
||||
<h4>Email</h4>
|
||||
@@ -36,7 +37,6 @@
|
||||
placeholder="Email"
|
||||
name="email"
|
||||
bind:value={email}
|
||||
on:focus={() => (error = false)}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
@@ -48,19 +48,13 @@
|
||||
placeholder="********"
|
||||
name="passwort"
|
||||
bind:value={password}
|
||||
on:focus={() => (error = false)}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
{#if error}
|
||||
<div class="bg-red-200 p-4 rounded-lg w-full">
|
||||
<p class="text-red-800">{errorMessage}</p>
|
||||
</div>
|
||||
{/if}
|
||||
<button class="button" type="submit">{buttonText}</button>
|
||||
<button class="button" type="submit">Einloggen</button>
|
||||
<div class="flex flex-row justify-between" style="margin-top: 10px">
|
||||
<a on:click={() => navigate("signup")} class="cursor-pointer" data-cy="registrieren">Registrieren</a>
|
||||
<a href="/auth/passwort-vergessen?r={window.location.href}">Passwort Vergessen?</a>
|
||||
<a href="/user/passwort_vergessen">Passwort Vergessen?</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -1,51 +1,74 @@
|
||||
<script lang="ts">
|
||||
import { loginClient } from "#lib/login.js";
|
||||
import { addNotification } from "@ibcornelsen/ui";
|
||||
import { api } from "astro-typesafe-api/client";
|
||||
|
||||
export let navigate: (target: string) => void;
|
||||
export let onRegister: (response: Awaited<ReturnType<typeof loginClient>>) => void;
|
||||
export let onRegister: (response: { email: string, name: string, vorname: string }) => void;
|
||||
export let password: string;
|
||||
export let email: string;
|
||||
export let title: string = "Registrieren";
|
||||
export let buttonText: string = "Registrieren";
|
||||
let repeatEmail: string;
|
||||
let vorname: string;
|
||||
let name: string;
|
||||
|
||||
async function signup(e: SubmitEvent) {
|
||||
async function signUp(e: SubmitEvent) {
|
||||
e.preventDefault()
|
||||
|
||||
if (email !== repeatEmail) {
|
||||
error = true;
|
||||
errorMessage = "Die eingegebenen Email Adressen stimmen nicht überein.";
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const { id, passwort } = await api.user.autocreate.PUT.fetch({
|
||||
const response = await api.user.PUT.fetch({
|
||||
email,
|
||||
passwort: password,
|
||||
vorname,
|
||||
name,
|
||||
});
|
||||
|
||||
const response = await loginClient(email, passwort)
|
||||
onRegister(response);
|
||||
onRegister({
|
||||
email,
|
||||
name,
|
||||
vorname
|
||||
})
|
||||
} catch (e) {
|
||||
error = true;
|
||||
errorMessage = "Sie besitzen bereits ein Konto bei IBC. Bitte loggen Sie sich mit Ihrem Passwort ein oder vergeben sich über “Passwort vergessen” ein neues."
|
||||
navigate("login");
|
||||
addNotification({
|
||||
message: "Ups...",
|
||||
subtext:
|
||||
"Da ist wohl etwas schiefgelaufen. Diese Email Adresse ist bereits in Benutzung, haben sie vielleicht bereits ein Konto bei uns?",
|
||||
type: "error",
|
||||
timeout: 0,
|
||||
dismissable: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let error: boolean = false;
|
||||
let errorMessage: string = "";
|
||||
</script>
|
||||
|
||||
<form class="max-w-md mx-auto" name="signup" on:submit={signup}>
|
||||
<h1 class="text-3xl mb-4 p-0">{title}</h1>
|
||||
<p class="p-0 text-base">Ihre Ausweisdaten werden bei uns gespeichert und Ihnen wird ein vorläufiges Passwort erstellt. Sollte Ihre E-Mail bereits bei uns registriert sein, dann loggen Sie sich bitte mit Ihrem vorhandenen Passwort ein oder vergeben sich ein neues über “Passwort vergessen”.</p>
|
||||
<hr>
|
||||
<form style="width:50%;margin: 0 auto" name="signup" on:submit={signUp}>
|
||||
<h1>Registrieren</h1>
|
||||
<div class="flex flex-col gap-4">
|
||||
<div class="flex flex-row gap-4 w-full">
|
||||
<div class="w-1/2">
|
||||
<h4>Vorname</h4>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Vorname"
|
||||
name="vorname"
|
||||
class="px-2.5 py-1.5 rounded-lg border bg-gray-50"
|
||||
bind:value={vorname}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="w-1/2">
|
||||
<h4>Nachname</h4>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Nachname"
|
||||
name="nachname"
|
||||
class="px-2.5 py-1.5 rounded-lg border bg-gray-50"
|
||||
bind:value={name}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h4>Email</h4>
|
||||
<input
|
||||
type="email"
|
||||
placeholder="max.mustermann@email.de"
|
||||
placeholder="Email"
|
||||
name="email"
|
||||
class="px-2.5 py-1.5 rounded-lg border bg-gray-50"
|
||||
bind:value={email}
|
||||
@@ -53,27 +76,21 @@
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="flex flex-col gap-2">
|
||||
<h4>Email erneut eingeben</h4>
|
||||
<div>
|
||||
<h4>Passwort</h4>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="max.mustermann@email.de"
|
||||
name="email"
|
||||
class="input input-bordered text-base text-base-content font-medium"
|
||||
bind:value={repeatEmail}
|
||||
on:keyup={() => (repeatEmail = repeatEmail.toLowerCase())}
|
||||
type="password"
|
||||
placeholder="********"
|
||||
name="passwort"
|
||||
class="px-2.5 py-1.5 rounded-lg border bg-gray-50"
|
||||
bind:value={password}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
{#if error}
|
||||
<div class="bg-red-200 p-4 rounded-lg w-full">
|
||||
<p class="text-red-800">{errorMessage}</p>
|
||||
</div>
|
||||
{/if}
|
||||
<button class="button" type="submit">{buttonText}</button>
|
||||
<div class="flex flex-row justify-between" style="margin-top: 10px">
|
||||
<button class="button" type="submit">Registrieren</button>
|
||||
<div class="flex-row justify-between" style="margin-top: 10px">
|
||||
<button on:click={() => navigate("login")}>Einloggen</button>
|
||||
<a href="/auth/passwort-vergessen?r={window.location.href}">Passwort Vergessen?</a>
|
||||
<a href="/user/passwort_vergessen">Passwort Vergessen?</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import PerformanceScore from "#components/Ausweis/PerformanceScore.svelte";
|
||||
import Progressbar from "#components/Ausweis/Progressbar.svelte";
|
||||
import Bereich from "#components/labels/Bereich.svelte";
|
||||
import type { BedarfsausweisGewerbe, BedarfsausweisWohnen, Benutzer, Bezahlmethoden, GEGNachweisGewerbe, GEGNachweisWohnen, Unterlage, VerbrauchsausweisGewerbe, VerbrauchsausweisWohnen } from "#lib/client/prisma.js";
|
||||
import type { BedarfsausweisGewerbe, BedarfsausweisWohnen, Bezahlmethoden, GEGNachweisGewerbe, GEGNachweisWohnen, Unterlage, VerbrauchsausweisGewerbe, VerbrauchsausweisWohnen } from "#lib/client/prisma.js";
|
||||
import { Enums } from "#lib/client/prisma.js";
|
||||
import {
|
||||
API_ACCESS_TOKEN_COOKIE_NAME,
|
||||
@@ -34,6 +34,7 @@
|
||||
import { endEnergieVerbrauchVerbrauchsausweisGewerbe_2016_Client } from "#lib/Berechnungen/VerbrauchsausweisGewerbe/VerbrauchsausweisGewerbe_2016_Client.js";
|
||||
import { benutzerSpeichern } from "#client/lib/speichern.js";
|
||||
import { benutzerLesen } from "#client/lib/lesen.js";
|
||||
import { exclude } from "#lib/exclude.js";
|
||||
|
||||
export let user: Partial<BenutzerClient>;
|
||||
export let impersonatedUser: Partial<BenutzerClient> | null = null;
|
||||
@@ -55,7 +56,7 @@
|
||||
email = rechnung?.email || localStorage.getItem("kundendaten.email") || user.email || "";
|
||||
vorname = localStorage.getItem("kundendaten.vorname") || user.vorname || "";
|
||||
name = localStorage.getItem("kundendaten.name") || user.name || "";
|
||||
empfaenger = rechnung?.empfaenger || localStorage.getItem("kundendaten.empfaenger") || user.empfaenger
|
||||
empfaenger = rechnung?.empfaenger || localStorage.getItem("kundendaten.empfaenger") || (user.vorname && user.name ? `${user.vorname} ${user.name}` : "")
|
||||
strasse = rechnung?.strasse || localStorage.getItem("kundendaten.strasse") || user.adresse || "";
|
||||
plz = rechnung?.plz || localStorage.getItem("kundendaten.plz") || user.plz || "";
|
||||
ort = rechnung?.ort || localStorage.getItem("kundendaten.ort") || user.ort || "";
|
||||
@@ -170,7 +171,6 @@
|
||||
|
||||
async function anfordern() {
|
||||
if (!form.checkValidity()) {
|
||||
displayFormValidity()
|
||||
addNotification({
|
||||
dismissable: true,
|
||||
message: "Fehlende Daten.",
|
||||
@@ -345,30 +345,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
function displayFormValidity() {
|
||||
(form.querySelectorAll("select[name][required], input[name][required]") as NodeListOf<HTMLInputElement | HTMLSelectElement>).forEach((element) => {
|
||||
if (element.willValidate && !element.checkValidity()) {
|
||||
element.dataset["isinvalid"] = "true"
|
||||
|
||||
const onChange = () => {
|
||||
console.log(element, element.value, element.checkValidity());
|
||||
|
||||
if (!element.checkValidity()) {
|
||||
return;
|
||||
}
|
||||
element.dataset["isinvalid"] = "false"
|
||||
element.removeEventListener("change", onChange)
|
||||
}
|
||||
|
||||
element.addEventListener("change", onChange)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async function bestellen(authuser: Benutzer | null = null) {
|
||||
async function bestellen(authuser = null) {
|
||||
if (!form.checkValidity()) {
|
||||
displayFormValidity()
|
||||
|
||||
addNotification({
|
||||
dismissable: true,
|
||||
message: "Fehlende Daten.",
|
||||
@@ -439,7 +417,6 @@
|
||||
const merged_versand_plz = versand_plz || plz;
|
||||
const merged_versand_ort = versand_ort || ort;
|
||||
const merged_versand_zusatzzeile = versand_zusatzzeile || zusatzzeile;
|
||||
|
||||
|
||||
if (rechnung) {
|
||||
const result = await api.rechnung._id.PATCH.fetch({
|
||||
@@ -527,14 +504,6 @@
|
||||
let loginOverlayHidden = true;
|
||||
let loginAction = () => {};
|
||||
let form: HTMLFormElement;
|
||||
let ortInput: HTMLInputElement;
|
||||
|
||||
$: {
|
||||
if (ort && ortInput) {
|
||||
ortInput.value = ort
|
||||
ortInput.dispatchEvent(new Event("change"))
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if !nurRechnungsadresseUpdate}
|
||||
@@ -693,9 +662,6 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
|
||||
name="rechnung_plz"
|
||||
bind:zip={plz}
|
||||
bind:city={ort}
|
||||
onchange={(e) => {
|
||||
ortInput.dispatchEvent(new Event('change'));
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -707,7 +673,6 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
|
||||
type="text"
|
||||
required
|
||||
bind:value={ort}
|
||||
bind:this={ortInput}
|
||||
/>
|
||||
|
||||
<div class="help-label">
|
||||
@@ -1248,23 +1213,13 @@ sm:grid-cols-[min-content_min-content_min-content] sm:justify-self-end sm:mt-8"
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</form>
|
||||
|
||||
<Overlay bind:hidden={loginOverlayHidden}>
|
||||
<Overlay bind:hidden={loginOverlayHidden}>
|
||||
<div class="bg-white w-full max-w-screen-sm py-8">
|
||||
<EmbeddedAuthFlowModule onLogin={loginAction} email={email} route="signup" title={
|
||||
{
|
||||
login: "Ausweis bestellen",
|
||||
signup: "Ausweis bestellen"
|
||||
}
|
||||
} buttonText={{
|
||||
login: "Bestellen",
|
||||
signup: "Bestellen"
|
||||
}}></EmbeddedAuthFlowModule>
|
||||
<EmbeddedAuthFlowModule onLogin={loginAction} email={email} route="signup"></EmbeddedAuthFlowModule>
|
||||
</div>
|
||||
</Overlay>
|
||||
|
||||
</form>
|
||||
|
||||
<NotificationWrapper></NotificationWrapper>
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<script lang="ts">
|
||||
import { loginClient } from "#lib/login.js";
|
||||
|
||||
import CrossCircled from "radix-svelte-icons/src/lib/icons/CrossCircled.svelte";
|
||||
import { fade } from "svelte/transition";
|
||||
|
||||
let email: string;
|
||||
let passwort: string;
|
||||
|
||||
@@ -11,8 +13,7 @@
|
||||
const response = await loginClient(email, passwort);
|
||||
|
||||
if (response === null) {
|
||||
error = true
|
||||
errorMessage = "Das hat leider nicht geklappt, haben sie ihr Passwort und ihre Email Adresse richtig eingegeben?"
|
||||
errorHidden = false;
|
||||
} else {
|
||||
if (redirect) {
|
||||
window.location.href = redirect;
|
||||
@@ -23,8 +24,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
let error = false;
|
||||
let errorMessage = "";
|
||||
let errorHidden = true;
|
||||
</script>
|
||||
|
||||
<div class="mx-auto w-1/3 bg-base-200 p-8 border border-base-300 rounded-lg">
|
||||
@@ -38,7 +38,7 @@
|
||||
placeholder="nutzer@email.com"
|
||||
name="email"
|
||||
bind:value={email}
|
||||
on:focus={() => (error = false)}
|
||||
on:focus={() => (errorHidden = true)}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
@@ -51,19 +51,20 @@
|
||||
placeholder="********"
|
||||
name="passwort"
|
||||
bind:value={passwort}
|
||||
on:focus={() => (error = false)}
|
||||
on:focus={() => (errorHidden = true)}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
{#if error}
|
||||
<div class="bg-red-200 p-4 rounded-lg w-full">
|
||||
<p class="text-red-800">{errorMessage}</p>
|
||||
</div>
|
||||
{#if !errorHidden}
|
||||
<div role="alert" class="alert alert-error" in:fade out:fade={{delay: 400}}>
|
||||
<CrossCircled size={24} />
|
||||
<span class="font-semibold">Das hat leider nicht geklappt, haben sie ihr Passwort und ihre Email Adresse richtig eingegeben?</span>
|
||||
</div>
|
||||
{/if}
|
||||
<button class="button" type="submit">Einloggen</button>
|
||||
<div class="flex flex-row justify-between" style="margin-top: 10px">
|
||||
<a class="link link-hover" href="/auth/signup{redirect ? `?r=${redirect}` : ""}">Registrieren</a>
|
||||
<a class="link link-hover" href="/auth/passwort-vergessen{redirect ? `?r=${redirect}` : ""}"
|
||||
<div class="flex-row justify-between" style="margin-top: 10px">
|
||||
<a class="link link-hover" href="/auth/signup{redirect ? `?redirect=${redirect}` : ""}">Registrieren</a>
|
||||
<a class="link link-hover" href="/auth/passwort-vergessen{redirect ? `?redirect=${redirect}` : ""}"
|
||||
>Passwort Vergessen?</a
|
||||
>
|
||||
</div>
|
||||
|
||||
@@ -1,58 +1,55 @@
|
||||
<script lang="ts">
|
||||
import { addNotification } from "#components/Notifications/shared.js";
|
||||
import { CrossCircled } from "radix-svelte-icons";
|
||||
import { fade } from "svelte/transition";
|
||||
import { api } from "astro-typesafe-api/client";
|
||||
import NotificationWrapper from "#components/Notifications/NotificationWrapper.svelte";
|
||||
import { loginClient } from "#lib/login.js";
|
||||
import PlzSuche from "#components/PlzSuche.svelte";
|
||||
|
||||
let passwort: string;
|
||||
let email: string;
|
||||
let vorname: string;
|
||||
let name: string;
|
||||
let repeatEmail: string;
|
||||
let adresse: string;
|
||||
let plz: string;
|
||||
let ort: string;
|
||||
let empfaenger: string;
|
||||
|
||||
export let redirect: string | null = null;
|
||||
|
||||
async function login(e: SubmitEvent) {
|
||||
e.preventDefault()
|
||||
|
||||
if (email !== repeatEmail) {
|
||||
error = true;
|
||||
errorMessage = "Die eingegebenen Email Adressen stimmen nicht überein.";
|
||||
if (passwort.length < 8) {
|
||||
addNotification({
|
||||
message: "Passwort muss mindestens 6 Zeichen enthalten.",
|
||||
dismissable: true,
|
||||
timeout: 3000,
|
||||
type: "error"
|
||||
})
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const { id, passwort } = await api.user.PUT.fetch({
|
||||
const { id } = await api.user.PUT.fetch({
|
||||
email,
|
||||
passwort,
|
||||
vorname,
|
||||
name, email,
|
||||
adresse, plz, ort, empfaenger
|
||||
name
|
||||
})
|
||||
|
||||
await loginClient(email, passwort)
|
||||
|
||||
if (redirect) {
|
||||
window.location.href = redirect
|
||||
return
|
||||
}
|
||||
|
||||
window.location.href = "/dashboard";
|
||||
window.location.href = "/auth/login";
|
||||
} catch (e) {
|
||||
error = true
|
||||
errorMessage = "Sie besitzen bereits ein Konto bei IBC. Bitte loggen Sie sich mit Ihrem Passwort ein oder vergeben sich über “Passwort vergessen” ein neues."
|
||||
errorHidden = false;
|
||||
}
|
||||
}
|
||||
|
||||
let error = false;
|
||||
let errorMessage = "";
|
||||
|
||||
let errorHidden = true;
|
||||
</script>
|
||||
|
||||
<div class="mx-auto w-1/3 bg-base-200 p-8 border border-base-300 rounded-lg">
|
||||
<h1 class="text-3xl mb-4">Registrieren</h1>
|
||||
<p>Bitte geben sie Email, Ansprechpartner und Adressdaten ein, um einen Account beim IBC zu erstellen, ein Passwort wird ihnen per Email zugesendet, dieses können sie im Nachhinein jederzeit ändern.</p>
|
||||
<form class="flex flex-col gap-4 mt-8" on:submit={login}>
|
||||
<form class="flex flex-col gap-4" on:submit={login}>
|
||||
<div class="flex flex-row gap-4 w-full">
|
||||
<div class="w-1/2 flex flex-col gap-2">
|
||||
<h4>Vorname</h4>
|
||||
@@ -77,51 +74,11 @@
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-row gap-4 w-full">
|
||||
<div class="w-1/2 flex flex-col gap-2">
|
||||
<h4>Straße</h4>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Straße"
|
||||
name="strasse"
|
||||
class="input input-bordered text-base text-base-content font-medium"
|
||||
bind:value={adresse}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="w-1/2 flex flex-col gap-2">
|
||||
<h4>PLZ</h4>
|
||||
<PlzSuche bind:zip={plz} bind:city={ort} name="plz" readonly={false} />
|
||||
</div>
|
||||
<div class="w-1/2 flex flex-col gap-2">
|
||||
<h4>Ort</h4>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Ort"
|
||||
name="ort"
|
||||
class="input input-bordered text-base text-base-content font-medium"
|
||||
bind:value={ort}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="flex flex-col gap-2">
|
||||
<h4>Empfänger</h4>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Max Mustermann"
|
||||
name="empfaenger"
|
||||
class="input input-bordered text-base text-base-content font-medium"
|
||||
bind:value={empfaenger}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="flex flex-col gap-2">
|
||||
<h4>Email</h4>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="max.mustermann@email.de"
|
||||
placeholder="Email"
|
||||
name="email"
|
||||
class="input input-bordered text-base text-base-content font-medium"
|
||||
bind:value={email}
|
||||
@@ -130,29 +87,31 @@
|
||||
/>
|
||||
</div>
|
||||
<div class="flex flex-col gap-2">
|
||||
<h4>Email erneut eingeben</h4>
|
||||
<h4>Passwort</h4>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="max.mustermann@email.de"
|
||||
name="email"
|
||||
type="password"
|
||||
placeholder="********"
|
||||
minlength="8"
|
||||
name="passwort"
|
||||
class="input input-bordered text-base text-base-content font-medium"
|
||||
bind:value={repeatEmail}
|
||||
on:keyup={() => (repeatEmail = repeatEmail.toLowerCase())}
|
||||
bind:value={passwort}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
{#if error}
|
||||
<div class="bg-red-200 p-4 rounded-lg w-full">
|
||||
<p class="text-red-800">{errorMessage}</p>
|
||||
</div>
|
||||
{#if !errorHidden}
|
||||
<div role="alert" class="alert alert-error" in:fade out:fade={{delay: 400}}>
|
||||
<CrossCircled size={24} />
|
||||
<span class="font-semibold">Da ist wohl etwas schiefgelaufen. Diese Email Adresse ist bereits in Benutzung, haben sie vielleicht bereits ein Konto bei uns?</span>
|
||||
</div>
|
||||
{/if}
|
||||
<button type="submit" class="button"
|
||||
>Registrieren</button>
|
||||
<div class="flex flex-row justify-between" style="margin-top: 10px">
|
||||
<a class="link link-hover"
|
||||
href="/auth/login{redirect ? `?r=${redirect}` : ""}">Einloggen</a
|
||||
<button type="submit" class="button"
|
||||
>Registrieren</button
|
||||
>
|
||||
<a class="link link-hover" href="/auth/passwort-vergessen{redirect ? `?r=${redirect}` : ""}">Passwort Vergessen?</a>
|
||||
<div class="flex-row justify-between" style="margin-top: 10px">
|
||||
<a class="link link-hover"
|
||||
href="/auth/login{redirect ? `?redirect=${redirect}` : ""}">Einloggen</a
|
||||
>
|
||||
<a class="link link-hover" href="/auth/passwort-vergessen{redirect ? `?redirect=${redirect}` : ""}">Passwort Vergessen?</a>
|
||||
</div>
|
||||
</form>
|
||||
<NotificationWrapper></NotificationWrapper>
|
||||
|
||||
@@ -20,7 +20,7 @@ const user = await getCurrentUser(Astro)
|
||||
if (id) {
|
||||
if (!user) {
|
||||
return Astro.redirect(
|
||||
`/auth/login?r=${Astro.url.toString()}`
|
||||
`/auth/login?redirect=${Astro.url.toString()}`
|
||||
);
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ if (id) {
|
||||
} else if (aufnahme_id) {
|
||||
if (!user) {
|
||||
return Astro.redirect(
|
||||
`/auth/login?r=${Astro.url.toString()}`
|
||||
`/auth/login?redirect=${Astro.url.toString()}`
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ const user = await getCurrentUser(Astro)
|
||||
if (id) {
|
||||
if (!user) {
|
||||
return Astro.redirect(
|
||||
`/auth/login?r=${Astro.url.toString()}`
|
||||
`/auth/login?redirect=${Astro.url.toString()}`
|
||||
);
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ if (id) {
|
||||
} else if (aufnahme_id) {
|
||||
if (!user) {
|
||||
return Astro.redirect(
|
||||
`/auth/login?r=${Astro.url.toString()}`
|
||||
`/auth/login?redirect=${Astro.url.toString()}`
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ const user = await getCurrentUser(Astro)
|
||||
if (id) {
|
||||
if (!user) {
|
||||
return Astro.redirect(
|
||||
`/auth/login?r=${Astro.url.toString()}`
|
||||
`/auth/login?redirect=${Astro.url.toString()}`
|
||||
);
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ if (id) {
|
||||
} else if (aufnahme_id) {
|
||||
if (!user) {
|
||||
return Astro.redirect(
|
||||
`/auth/login?r=${Astro.url.toString()}`
|
||||
`/auth/login?redirect=${Astro.url.toString()}`
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ let loadFromDatabase = false;
|
||||
|
||||
if (typ === AusstellungsTyp.Neuausstellung) {
|
||||
if (!user) {
|
||||
return Astro.redirect(`/auth/login?r=${Astro.url.toString()}`);
|
||||
return Astro.redirect(`/auth/login?redirect=${Astro.url.toString()}`);
|
||||
}
|
||||
|
||||
if (!ausweis_id) {
|
||||
@@ -115,7 +115,7 @@ if (typ === AusstellungsTyp.Neuausstellung) {
|
||||
loadFromDatabase = true;
|
||||
} else if (typ === AusstellungsTyp.Speichern) {
|
||||
if (!user) {
|
||||
return Astro.redirect(`/auth/login?r=${Astro.url.toString()}`);
|
||||
return Astro.redirect(`/auth/login?redirect=${Astro.url.toString()}`);
|
||||
}
|
||||
|
||||
if (!ausweis_id) {
|
||||
@@ -151,7 +151,7 @@ if (typ === AusstellungsTyp.Neuausstellung) {
|
||||
loadFromDatabase = true;
|
||||
} else if (typ === AusstellungsTyp.Alternativdokument) {
|
||||
if (!user) {
|
||||
return Astro.redirect(`/auth/login?r=${Astro.url.toString()}`);
|
||||
return Astro.redirect(`/auth/login?redirect=${Astro.url.toString()}`);
|
||||
}
|
||||
|
||||
if (!ausweis_id) {
|
||||
|
||||
@@ -20,7 +20,7 @@ const user = await getCurrentUser(Astro)
|
||||
if (id) {
|
||||
if (!user) {
|
||||
return Astro.redirect(
|
||||
`/auth/login?r=${Astro.url.toString()}`
|
||||
`/auth/login?redirect=${Astro.url.toString()}`
|
||||
);
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ if (id) {
|
||||
} else if (aufnahme_id) {
|
||||
if (!user) {
|
||||
return Astro.redirect(
|
||||
`/auth/login?r=${Astro.url.toString()}`
|
||||
`/auth/login?redirect=${Astro.url.toString()}`
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ const user = await getCurrentUser(Astro)
|
||||
if (id) {
|
||||
if (!user) {
|
||||
return Astro.redirect(
|
||||
`/auth/login?r=${Astro.url.toString()}`
|
||||
`/auth/login?redirect=${Astro.url.toString()}`
|
||||
);
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ if (id) {
|
||||
} else if (aufnahme_id) {
|
||||
if (!user) {
|
||||
return Astro.redirect(
|
||||
`/auth/login?r=${Astro.url.toString()}`
|
||||
`/auth/login?redirect=${Astro.url.toString()}`
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ const user = await getCurrentUser(Astro)
|
||||
if (id) {
|
||||
if (!user) {
|
||||
return Astro.redirect(
|
||||
`/auth/login?r=${Astro.url.toString()}`
|
||||
`/auth/login?redirect=${Astro.url.toString()}`
|
||||
);
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ if (id) {
|
||||
} else if (aufnahme_id) {
|
||||
if (!user) {
|
||||
return Astro.redirect(
|
||||
`/auth/login?r=${Astro.url.toString()}`
|
||||
`/auth/login?redirect=${Astro.url.toString()}`
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -294,7 +294,7 @@ export const GET = defineApiRoute({
|
||||
|
||||
if (rechnung.status === Enums.Rechnungsstatus.paid) {
|
||||
html = `
|
||||
<p>Sehr geehrte/r ${rechnung.empfaenger},</p>
|
||||
<p>Sehr geehrte*r ${rechnung.empfaenger},</p>
|
||||
|
||||
<p>im Anhang finden Sie Ihren geprüften Energieusweis inkl. Rechnung als PDF-Datei. ${
|
||||
post ? "Zusätzlich haben wir Ihren Ausweis per Post verschickt" : ""
|
||||
@@ -323,7 +323,7 @@ export const GET = defineApiRoute({
|
||||
</p>`;
|
||||
} else {
|
||||
html = `
|
||||
<p>Sehr geehrte/r ${rechnung.empfaenger},</p>
|
||||
<p>Sehr geehrte*r ${rechnung.empfaenger},</p>
|
||||
|
||||
<p>im Anhang finden Sie Ihren geprüften Energieusweis inkl. Rechnung als PDF-Datei. ${
|
||||
post ? "Zusätzlich haben wir Ihren Ausweis per Post verschickt" : ""
|
||||
|
||||
@@ -303,7 +303,7 @@ export const POST = defineApiRoute({
|
||||
|
||||
if (rechnung.status === Enums.Rechnungsstatus.paid) {
|
||||
html = `
|
||||
<p>Sehr geehrte/r ${rechnung.empfaenger},</p>
|
||||
<p>Sehr geehrte*r ${rechnung.empfaenger},</p>
|
||||
|
||||
<p>im Anhang finden Sie Ihren geprüften Energieusweis inkl. Rechnung als PDF-Datei. ${
|
||||
post ? "Zusätzlich haben wir Ihren Ausweis per Post verschickt" : ""
|
||||
@@ -332,7 +332,7 @@ export const POST = defineApiRoute({
|
||||
</p>`;
|
||||
} else {
|
||||
html = `
|
||||
<p>Sehr geehrte/r ${rechnung.empfaenger},</p>
|
||||
<p>Sehr geehrte*r ${rechnung.empfaenger},</p>
|
||||
|
||||
<p>im Anhang finden Sie Ihren geprüften Energieusweis inkl. Rechnung als PDF-Datei. ${
|
||||
post ? "Zusätzlich haben wir Ihren Ausweis per Post verschickt" : ""
|
||||
|
||||
@@ -120,7 +120,7 @@ export const PUT = defineApiRoute({
|
||||
to: rechnung.email || user.email,
|
||||
bcc: "info@online-energieausweis.org",
|
||||
subject: `Stornierung des Energieausweises vom Ingenieurbüro Cornelsen (ID: ${ausweis.id})`,
|
||||
html: `<p>Sehr geehrte/r ${user.vorname} ${user.name},</p>
|
||||
html: `<p>Sehr geehrte*r ${user.vorname} ${user.name},</p>
|
||||
|
||||
<p>Ihr Energieausweis wurde soeben storniert.
|
||||
<br>
|
||||
|
||||
@@ -9,8 +9,7 @@ import { transport } from "#lib/mail.js";
|
||||
|
||||
export const GET = defineApiRoute({
|
||||
input: z.object({
|
||||
email: z.string().email(),
|
||||
redirect: z.string().optional()
|
||||
email: z.string().email()
|
||||
}),
|
||||
output: z.void(),
|
||||
async fetch(input, context, transfer) {
|
||||
@@ -43,7 +42,7 @@ export const GET = defineApiRoute({
|
||||
|
||||
sie haben eine Anfrage zum Zurücksetzen ihres Passworts gestellt. Klicken sie auf den folgenden Link, um ein neues Passwort festzulegen:
|
||||
|
||||
https://online-energieausweis.org/auth/passwort-zuruecksetzen?t=${resetToken}${input.redirect ? `&r=${input.redirect}` : ""}
|
||||
https://online-energieausweis.org/auth/passwort-zuruecksetzen?t=${resetToken}
|
||||
|
||||
Dieser Link ist für die nächsten 15 Minuten gültig. Falls du diese Anfrage nicht gestellt hast, kannst du diese E-Mail ignorieren - dein Passwort bleibt unverändert.
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ import { PutObjectCommand } from "@aws-sdk/client-s3";
|
||||
import { s3Client } from "#lib/s3.js";
|
||||
import { generateIDWithPrefix } from "#lib/db.js";
|
||||
import { VALID_UUID_PREFIXES } from "#lib/constants.js";
|
||||
import { logger } from "#lib/logger.js";
|
||||
|
||||
export const PUT = defineApiRoute({
|
||||
input: BildSchema.pick({
|
||||
@@ -70,7 +69,6 @@ export const PUT = defineApiRoute({
|
||||
id,
|
||||
},
|
||||
});
|
||||
logger.error("Fehler beim Speichern des Bildes in S3: " + e);
|
||||
// Und geben einen Fehler zurück
|
||||
throw new APIError({
|
||||
code: "INTERNAL_SERVER_ERROR",
|
||||
@@ -111,7 +109,8 @@ export const DELETE = defineApiRoute({
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
logger.error("Fehler beim Löschen des Bildes: " + e);
|
||||
console.log(e);
|
||||
|
||||
throw new APIError({
|
||||
code: "INTERNAL_SERVER_ERROR",
|
||||
message: "Bild konnte nicht gelöscht werden.",
|
||||
|
||||
@@ -96,41 +96,6 @@ export const PATCH = defineApiRoute({
|
||||
}
|
||||
})
|
||||
|
||||
if (user.rolle !== Enums.BenutzerRolle.ADMIN) {
|
||||
// Wir aktualisieren auch die Rechnungsdaten des Benutzers
|
||||
// Das sollte allerdings nur passieren, falls diese noch nicht gesetzt sind.
|
||||
const data = {
|
||||
ort: input.ort,
|
||||
plz: input.plz,
|
||||
adresse: input.strasse,
|
||||
telefon: input.telefon,
|
||||
empfaenger: input.empfaenger
|
||||
}
|
||||
|
||||
if (user.ort) {
|
||||
delete data.ort;
|
||||
}
|
||||
if (user.plz) {
|
||||
delete data.plz;
|
||||
}
|
||||
if (user.adresse) {
|
||||
delete data.adresse;
|
||||
}
|
||||
if (user.telefon) {
|
||||
delete data.telefon;
|
||||
}
|
||||
if (user.empfaenger) {
|
||||
delete data.empfaenger;
|
||||
}
|
||||
|
||||
await prisma.benutzer.update({
|
||||
data,
|
||||
where: {
|
||||
id: user.id
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (input.bezahlmethode === Enums.Bezahlmethoden.rechnung) {
|
||||
return { id: rechnung.id }
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ export const PUT = defineApiRoute({
|
||||
if (!adapter) {
|
||||
throw new APIError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "Ungültige Ausweis ID"
|
||||
message: "Ungültige Ausweis UID"
|
||||
})
|
||||
}
|
||||
|
||||
@@ -214,41 +214,6 @@ export const PUT = defineApiRoute({
|
||||
}
|
||||
})
|
||||
|
||||
if (user.rolle !== Enums.BenutzerRolle.ADMIN) {
|
||||
// Wir aktualisieren auch die Rechnungsdaten des Benutzers
|
||||
// Das sollte allerdings nur passieren, falls diese noch nicht gesetzt sind.
|
||||
const data = {
|
||||
ort: input.ort,
|
||||
plz: input.plz,
|
||||
adresse: input.strasse,
|
||||
telefon: input.telefon,
|
||||
empfaenger: input.empfaenger
|
||||
}
|
||||
|
||||
if (user.ort) {
|
||||
delete data.ort;
|
||||
}
|
||||
if (user.plz) {
|
||||
delete data.plz;
|
||||
}
|
||||
if (user.adresse) {
|
||||
delete data.adresse;
|
||||
}
|
||||
if (user.telefon) {
|
||||
delete data.telefon;
|
||||
}
|
||||
if (user.empfaenger) {
|
||||
delete data.empfaenger;
|
||||
}
|
||||
|
||||
await prisma.benutzer.update({
|
||||
data,
|
||||
where: {
|
||||
id: user.id
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (bezahlmethode === Enums.Bezahlmethoden.rechnung) {
|
||||
return { id }
|
||||
}
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
import { IDWithPrefix } from "#components/Ausweis/types.js";
|
||||
import { VALID_UUID_PREFIXES } from "#lib/constants.js";
|
||||
import { generateIDWithPrefix } from "#lib/db.js";
|
||||
import { hashPassword } from "#lib/password.js";
|
||||
import { createLexOfficeCustomer } from "#lib/server/lexoffice.js";
|
||||
import { sendAutoRegisterMail } from "#lib/server/mail/auto-registrierung.js";
|
||||
import { sendRegisterMail } from "#lib/server/mail/registrierung.js";
|
||||
import { prisma } from "#lib/server/prisma.js";
|
||||
import { defineApiRoute, APIError } from "astro-typesafe-api/server";
|
||||
import { z } from "astro:content";
|
||||
|
||||
export const PUT = defineApiRoute({
|
||||
input: z.object({
|
||||
email: z.string().email(),
|
||||
}),
|
||||
output: z.object({
|
||||
email: z.string().email(),
|
||||
id: IDWithPrefix,
|
||||
passwort: z.string().min(8).max(100)
|
||||
}),
|
||||
async fetch(input) {
|
||||
let { email } = input;
|
||||
email = email.toLowerCase();
|
||||
|
||||
const existingUser = await prisma.benutzer.findUnique({
|
||||
where: {
|
||||
email
|
||||
}
|
||||
})
|
||||
|
||||
if (existingUser) {
|
||||
throw new APIError({
|
||||
code: "CONFLICT",
|
||||
message: "Email Adresse ist bereits vergeben."
|
||||
})
|
||||
}
|
||||
|
||||
const id = generateIDWithPrefix(9, VALID_UUID_PREFIXES.User);
|
||||
const passwort = crypto.randomUUID().slice(0, 8);
|
||||
|
||||
const user = await prisma.benutzer.create({
|
||||
data: {
|
||||
email,
|
||||
passwort: hashPassword(passwort),
|
||||
id
|
||||
}
|
||||
})
|
||||
|
||||
const lex_office_id = await createLexOfficeCustomer(user);
|
||||
|
||||
await prisma.benutzer.update({
|
||||
where: {
|
||||
id: user.id
|
||||
},
|
||||
data: {
|
||||
lex_office_id
|
||||
}
|
||||
})
|
||||
|
||||
await sendRegisterMail(user, passwort)
|
||||
|
||||
return { id, email: user.email, passwort }
|
||||
},
|
||||
})
|
||||
@@ -1,11 +1,11 @@
|
||||
import { IDWithPrefix } from "#components/Ausweis/types.js";
|
||||
import { VALID_UUID_PREFIXES } from "#lib/constants.js";
|
||||
import { generateIDWithPrefix } from "#lib/db.js";
|
||||
import { authorizationMiddleware } from "#lib/middleware/authorization.js";
|
||||
import { adminMiddleware, authorizationMiddleware } from "#lib/middleware/authorization.js";
|
||||
import { hashPassword } from "#lib/password.js";
|
||||
import { createLexOfficeCustomer } from "#lib/server/lexoffice.js";
|
||||
import { sendRegisterMail } from "#lib/server/mail/registrierung.js";
|
||||
import { prisma } from "#lib/server/prisma.js";
|
||||
import { Benutzer, prisma } from "#lib/server/prisma.js";
|
||||
import { APIError, defineApiRoute } from "astro-typesafe-api/server";
|
||||
import { BenutzerSchema } from "src/generated/zod/benutzer.js";
|
||||
import { z } from "zod";
|
||||
@@ -26,6 +26,7 @@ export const POST = defineApiRoute({
|
||||
const updateData: any = {};
|
||||
updateData.id = user.id;
|
||||
if (input.adresse) updateData.adresse = input.adresse;
|
||||
if (input.anrede) updateData.anrede = input.anrede;
|
||||
if (input.email) updateData.email = input.email;
|
||||
if (input.firma) updateData.firma = input.firma;
|
||||
if (input.name) updateData.name = input.name;
|
||||
@@ -105,19 +106,15 @@ export const GET = defineApiRoute({
|
||||
export const PUT = defineApiRoute({
|
||||
input: z.object({
|
||||
email: z.string().email(),
|
||||
passwort: z.string().min(8),
|
||||
vorname: z.string(),
|
||||
name: z.string(),
|
||||
adresse: z.string(),
|
||||
plz: z.string(),
|
||||
ort: z.string(),
|
||||
empfaenger: z.string()
|
||||
name: z.string()
|
||||
}),
|
||||
output: z.object({
|
||||
id: IDWithPrefix,
|
||||
passwort: z.string().min(8).max(100)
|
||||
id: IDWithPrefix
|
||||
}),
|
||||
async fetch(input) {
|
||||
let { email, vorname, name, adresse, plz, ort, empfaenger } = input;
|
||||
let { email, passwort, vorname, name } = input;
|
||||
email = email.toLowerCase();
|
||||
|
||||
const existingUser = await prisma.benutzer.findUnique({
|
||||
@@ -134,18 +131,13 @@ export const PUT = defineApiRoute({
|
||||
}
|
||||
|
||||
const id = generateIDWithPrefix(9, VALID_UUID_PREFIXES.User);
|
||||
const passwort = crypto.randomUUID().slice(0, 8);
|
||||
|
||||
const user = await prisma.benutzer.create({
|
||||
data: {
|
||||
email,
|
||||
vorname,
|
||||
passwort: hashPassword(passwort),
|
||||
vorname,
|
||||
name,
|
||||
adresse,
|
||||
plz,
|
||||
ort,
|
||||
empfaenger,
|
||||
id
|
||||
}
|
||||
})
|
||||
@@ -161,8 +153,8 @@ export const PUT = defineApiRoute({
|
||||
}
|
||||
})
|
||||
|
||||
await sendRegisterMail(user, passwort)
|
||||
await sendRegisterMail(user)
|
||||
|
||||
return { id, passwort }
|
||||
return { id }
|
||||
},
|
||||
})
|
||||
9
src/pages/auth/embedded-login.astro
Normal file
9
src/pages/auth/embedded-login.astro
Normal file
@@ -0,0 +1,9 @@
|
||||
---
|
||||
import BlankLayout from "#layouts/BlankLayout.astro";
|
||||
import EmbeddedLoginModule from "#modules/EmbeddedLoginModule.svelte";
|
||||
|
||||
---
|
||||
|
||||
<BlankLayout title="Login - IBCornelsen">
|
||||
<EmbeddedLoginModule client:only></EmbeddedLoginModule>
|
||||
</BlankLayout>
|
||||
10
src/pages/auth/embedded-signup.astro
Normal file
10
src/pages/auth/embedded-signup.astro
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
import BlankLayout from "#layouts/BlankLayout.astro";
|
||||
import EmbeddedRegisterModule from "#modules/EmbeddedRegisterModule.svelte";
|
||||
|
||||
const redirect = Astro.url.searchParams.get("redirect");
|
||||
---
|
||||
|
||||
<BlankLayout title="Registrieren - IBCornelsen">
|
||||
<EmbeddedRegisterModule client:only></EmbeddedRegisterModule>
|
||||
</BlankLayout>
|
||||
@@ -9,7 +9,7 @@ if (valid) {
|
||||
return Astro.redirect("/dashboard")
|
||||
}
|
||||
|
||||
const redirect = Astro.url.searchParams.get("r")
|
||||
const redirect = Astro.url.searchParams.get("redirect")
|
||||
---
|
||||
|
||||
<MinimalLayout title="Login">
|
||||
|
||||
@@ -5,13 +5,11 @@ import MinimalLayout from "#layouts/MinimalLayout.astro";
|
||||
|
||||
const valid = await validateAccessTokenServer(Astro)
|
||||
|
||||
// Es kann sein, dass ein Nutzer von dem Ausweisformular kommt und sein Passwort vergessen hat.
|
||||
// In dem Fall sollte er auch auf das Formular zurückgeleitet werden wenn er sein Passwort zurückgesetzt hat.
|
||||
const redirect = Astro.url.searchParams.get("r")
|
||||
|
||||
if (valid) {
|
||||
return Astro.redirect("/dashboard")
|
||||
}
|
||||
|
||||
const redirect = Astro.url.searchParams.get("redirect")
|
||||
---
|
||||
|
||||
<MinimalLayout title="Passwort Vergessen">
|
||||
|
||||
@@ -12,7 +12,6 @@ if (valid) {
|
||||
}
|
||||
|
||||
const token = Astro.url.searchParams.get("t")
|
||||
const redirect = Astro.url.searchParams.get("r")
|
||||
|
||||
if (!token) {
|
||||
return Astro.redirect("/")
|
||||
@@ -26,5 +25,5 @@ if (!decoded.exp || decoded.exp < Date.now() || decoded.typ !== TokenType.Reset)
|
||||
---
|
||||
|
||||
<MinimalLayout title="Passwort Vergessen">
|
||||
<PasswortZuruecksetzenModule token={token} redirect={redirect} client:load></PasswortZuruecksetzenModule>
|
||||
<PasswortZuruecksetzenModule token={token} client:load></PasswortZuruecksetzenModule>
|
||||
</MinimalLayout>
|
||||
|
||||
@@ -9,7 +9,7 @@ if (valid) {
|
||||
return Astro.redirect("/dashboard")
|
||||
}
|
||||
|
||||
const redirect = Astro.url.searchParams.get("r");
|
||||
const redirect = Astro.url.searchParams.get("redirect");
|
||||
|
||||
|
||||
---
|
||||
|
||||
32
src/pages/auth/verify.astro
Normal file
32
src/pages/auth/verify.astro
Normal file
@@ -0,0 +1,32 @@
|
||||
---
|
||||
import Layout from "#layouts/Layout.astro";
|
||||
import { decodeToken } from "#lib/auth/token";
|
||||
import { TokenType } from "#lib/auth/types";
|
||||
import { prisma } from "#lib/server/prisma";
|
||||
|
||||
const token = Astro.url.searchParams.get("t");
|
||||
|
||||
if (!token) {
|
||||
return Astro.redirect("/")
|
||||
}
|
||||
|
||||
const payload = decodeToken(token)
|
||||
|
||||
if (payload.typ !== TokenType.Verify || !payload.uid || !payload.exp || payload.exp < Date.now()) {
|
||||
return Astro.redirect("/")
|
||||
}
|
||||
|
||||
await prisma.benutzer.update({
|
||||
where: {
|
||||
uid: payload.uid
|
||||
},
|
||||
data: {
|
||||
verified: true
|
||||
}
|
||||
})
|
||||
---
|
||||
|
||||
<Layout title="Bestätigung">
|
||||
<h1>Vielen Dank</h1>
|
||||
<p>Ihre Email Adresse wurde bestätigt, sie können diese Seite nun schließen.</p>
|
||||
</Layout>
|
||||
@@ -53,7 +53,7 @@ import Layout from "#layouts/Layout.astro";
|
||||
<div>
|
||||
<h3 class="text-xl font-semibold text-gray-800 mb-2">Kontakt & Gebäudedaten</h3>
|
||||
<ul class="list-disc list-inside text-gray-700">
|
||||
<li>Name & E-Mail des Ansprechpartners</li>
|
||||
<li>Name, Anrede & E-Mail des Ansprechpartners</li>
|
||||
<li>Rechnungs- & Versandadresse</li>
|
||||
<li>Gebäudeangaben: Wohnfläche, Baujahr, Anzahl Geschosse</li>
|
||||
</ul>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import UserLayout from "#layouts/DashboardLayout.astro";
|
||||
import { getCurrentUser } from "#lib/server/user";
|
||||
import DashboardEinstellungenModule from "#modules/Dashboard/DashboardEinstellungenModule.svelte";
|
||||
import { validateAccessTokenServer } from "#server/lib/validateAccessToken";
|
||||
|
||||
const user = await getCurrentUser(Astro)
|
||||
|
||||
|
||||
@@ -53,21 +53,5 @@ if (user.rolle !== Enums.BenutzerRolle.ADMIN) {
|
||||
|
||||
if (result.length > 0) {
|
||||
return Astro.redirect(`/dashboard/objekte/${result[0].id}?p=${page}`);
|
||||
} else {
|
||||
return Astro.redirect("/dashboard/objekte/leer");
|
||||
}
|
||||
|
||||
|
||||
---
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Document</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
---
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
Enums,
|
||||
Objekt,
|
||||
prisma,
|
||||
Unterlage,
|
||||
VerbrauchsausweisGewerbe,
|
||||
VerbrauchsausweisWohnen,
|
||||
} from "#lib/server/prisma";
|
||||
@@ -17,7 +16,6 @@ import {
|
||||
getBedarfsausweisWohnen,
|
||||
getBilder,
|
||||
getObjekt,
|
||||
getUnterlagen,
|
||||
getVerbrauchsausweisGewerbe,
|
||||
getVerbrauchsausweisWohnen,
|
||||
} from "#lib/server/db";
|
||||
@@ -54,18 +52,14 @@ let ausweis:
|
||||
let aufnahme: Aufnahme | null = {} as Aufnahme;
|
||||
let objekt: Objekt | null = {} as Objekt;
|
||||
let bilder: Bild[] = [];
|
||||
let unterlagen: Unterlage[] = [];
|
||||
let loadFromDatabase = false;
|
||||
|
||||
if (typ === AusstellungsTyp.Neuausstellung) {
|
||||
if (!user) {
|
||||
// Der Nutzer muss eingeloggt sein um eine Neuausstellung anzufordern,
|
||||
// sonst können wir nicht sicher sein, dass der Nutzer berechtigt ist auf den Ausweis zuzugreifen.
|
||||
return Astro.redirect(`/auth/login?r=${Astro.url.toString()}`);
|
||||
return Astro.redirect(`/auth/login?redirect=${Astro.url.toString()}`);
|
||||
}
|
||||
|
||||
if (!ausweis_id) {
|
||||
// Falls es keine Ausweis ID gibt können wir nicht fortfahren.
|
||||
return Astro.redirect("/400");
|
||||
}
|
||||
|
||||
@@ -100,10 +94,9 @@ if (typ === AusstellungsTyp.Neuausstellung) {
|
||||
return Astro.redirect("/405");
|
||||
}
|
||||
|
||||
// Wir setzen alle Daten vom Ausweis zurück, sonst könnte es passieren, dass der Ausweis als der alte Ausweis gespeichert wird.
|
||||
ausweis.id = "";
|
||||
aufnahme.id = "";
|
||||
aufnahme.erstellungsdatum = null;
|
||||
ausweis.id = null;
|
||||
aufnahme.id = null;
|
||||
delete aufnahme.erstellungsdatum;
|
||||
ausweis.created_at = new Date()
|
||||
ausweis.updated_at = new Date();
|
||||
ausweis.alte_ausweis_id = null;
|
||||
@@ -121,7 +114,7 @@ if (typ === AusstellungsTyp.Neuausstellung) {
|
||||
loadFromDatabase = true;
|
||||
} else if (typ === AusstellungsTyp.Speichern) {
|
||||
if (!user) {
|
||||
return Astro.redirect(`/auth/login?r=${Astro.url.toString()}`);
|
||||
return Astro.redirect(`/auth/login?redirect=${Astro.url.toString()}`);
|
||||
}
|
||||
|
||||
if (!ausweis_id) {
|
||||
@@ -136,11 +129,6 @@ if (typ === AusstellungsTyp.Neuausstellung) {
|
||||
ausweis = await getBedarfsausweisWohnen(ausweis_id);
|
||||
}
|
||||
|
||||
if (!ausweis) {
|
||||
// Falls der Ausweis nicht gefunden wurde, können wir nicht fortfahren.
|
||||
return Astro.redirect("/404");
|
||||
}
|
||||
|
||||
ausweistyp = ausweis.ausweistyp;
|
||||
|
||||
aufnahme = (await getAufnahme(ausweis.aufnahme_id)) as Aufnahme;
|
||||
@@ -159,11 +147,10 @@ if (typ === AusstellungsTyp.Neuausstellung) {
|
||||
}
|
||||
|
||||
bilder = await getBilder(aufnahme.id);
|
||||
unterlagen = await getUnterlagen(aufnahme.id);
|
||||
loadFromDatabase = true;
|
||||
} else if (typ === AusstellungsTyp.Alternativdokument) {
|
||||
if (!user) {
|
||||
return Astro.redirect(`/auth/login?r=${Astro.url.toString()}`);
|
||||
return Astro.redirect(`/auth/login?redirect=${Astro.url.toString()}`);
|
||||
}
|
||||
|
||||
if (!ausweis_id) {
|
||||
@@ -201,8 +188,8 @@ if (typ === AusstellungsTyp.Neuausstellung) {
|
||||
return Astro.redirect("/405");
|
||||
}
|
||||
|
||||
ausweis.id = "";
|
||||
aufnahme.erstellungsdatum = null;
|
||||
ausweis.id = null;
|
||||
delete aufnahme.erstellungsdatum;
|
||||
ausweis.created_at = new Date()
|
||||
ausweis.updated_at = new Date();
|
||||
ausweis.alte_ausweis_id = null;
|
||||
@@ -262,7 +249,6 @@ if (typ === AusstellungsTyp.Neuausstellung) {
|
||||
{aufnahme}
|
||||
{bilder}
|
||||
{ausweistyp}
|
||||
{unterlagen}
|
||||
{ausweis_id}
|
||||
{user}
|
||||
{loadFromDatabase}
|
||||
|
||||
@@ -20,7 +20,7 @@ const caller = createCaller(Astro);
|
||||
if (uid) {
|
||||
if (!valid) {
|
||||
return Astro.redirect(
|
||||
`/auth/login?r=${Astro.url.toString()}`
|
||||
`/auth/login?redirect=${Astro.url.toString()}`
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ const caller = createCaller(Astro);
|
||||
if (uid) {
|
||||
if (!valid) {
|
||||
return Astro.redirect(
|
||||
`/auth/login?r=${Astro.url.toString()}`
|
||||
`/auth/login?redirect=${Astro.url.toString()}`
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ const caller = createCaller(Astro);
|
||||
if (uid) {
|
||||
if (!valid) {
|
||||
return Astro.redirect(
|
||||
`/auth/login?r=${Astro.url.toString()}`
|
||||
`/auth/login?redirect=${Astro.url.toString()}`
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ const caller = createCaller(Astro);
|
||||
if (uid) {
|
||||
if (!valid) {
|
||||
return Astro.redirect(
|
||||
`/auth/login?r=${Astro.url.toString()}`
|
||||
`/auth/login?redirect=${Astro.url.toString()}`
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ const caller = createCaller(Astro);
|
||||
if (uid) {
|
||||
if (!valid) {
|
||||
return Astro.redirect(
|
||||
`/auth/login?r=${Astro.url.toString()}`
|
||||
`/auth/login?redirect=${Astro.url.toString()}`
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ const caller = createCaller(Astro);
|
||||
if (uid) {
|
||||
if (!valid) {
|
||||
return Astro.redirect(
|
||||
`/auth/login?r=${Astro.url.toString()}`
|
||||
`/auth/login?redirect=${Astro.url.toString()}`
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -363,7 +363,7 @@ import Layout from "#layouts/Layout.astro";
|
||||
eingegeben und an uns übermittelt und gespeichert. Eine Weitergabe der Daten an Dritte findet
|
||||
nicht statt. Folgende Daten werden im Rahmen des Registrierungsprozesses erhoben:</p>
|
||||
<ol>
|
||||
<li>Vorname, Name und E-Mail des Ansprechpartners</li>
|
||||
<li>Anrede, Vorname, Name und E-Mail des Ansprechpartners</li>
|
||||
<li>Empfänger, E-Mail, Straße, <span class="caps">PLZ</span> und Ort der Rechnungsanschrift
|
||||
</li>
|
||||
<li>Optional die Versandanschrift</li>
|
||||
@@ -417,7 +417,7 @@ import Layout from "#layouts/Layout.astro";
|
||||
Kontaktaufnahme genutzt werden kann. Nimmt ein Nutzer diese Möglichkeit wahr, so werden die in
|
||||
der Eingabemaske eingegeben Daten an uns übermittelt und gespeichert. Diese Daten sind:</p>
|
||||
<ol>
|
||||
<li>Vorname, Name und E-Mail des Ansprechpartners</li>
|
||||
<li>Anrede, Vorname, Name und E-Mail des Ansprechpartners</li>
|
||||
</ol> Im Zeitpunkt der Absendung der Nachricht werden zudem folgende Daten gespeichert:<ol>
|
||||
<li>Die IP-Adresse des Nutzers</li>
|
||||
<li>Datum und Uhrzeit der Registrierung</li>
|
||||
|
||||
@@ -55,7 +55,7 @@ import Layout from "#layouts/Layout.astro";
|
||||
<div>
|
||||
<h3 class="text-xl font-semibold text-gray-800 mb-2">Kontakt und Gebäudedaten</h3>
|
||||
<ul class="list-disc list-inside text-gray-700">
|
||||
<li>Vorname, Name und E-Mail des Ansprechpartners</li>
|
||||
<li>Anrede, Vorname, Name und E-Mail des Ansprechpartners</li>
|
||||
<li>Empfänger, E-Mail, Straße, PLZ und Ort der Rechnungsanschrift</li>
|
||||
<li>Optionale Versandanschrift</li>
|
||||
<li>Details zur Wohnfläche, Heizung, Baujahr usw.</li>
|
||||
|
||||
@@ -279,31 +279,3 @@ article {
|
||||
/*SIDEBAR-RIGHT*/
|
||||
|
||||
/*FOOTER*/
|
||||
|
||||
|
||||
/* LOADERS */
|
||||
|
||||
.loader {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border: 3px solid #444f94;
|
||||
border-bottom-color: transparent;
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
box-sizing: border-box;
|
||||
animation: rotation 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes rotation {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
/* FORM VALIDATION MESSAGE */
|
||||
[data-isinvalid="true"] {
|
||||
border: 2px solid red !important;
|
||||
}
|
||||
@@ -45,6 +45,7 @@ Papa.parse(file, {
|
||||
email: user.email,
|
||||
passwort: user.password,
|
||||
adresse: user.adresse,
|
||||
anrede: user.anrede,
|
||||
name: user.name,
|
||||
vorname: user.vorname,
|
||||
ort: user.ort,
|
||||
|
||||
@@ -338,6 +338,7 @@ export function fakeBenutzer() {
|
||||
ort: undefined,
|
||||
adresse: undefined,
|
||||
telefon: undefined,
|
||||
anrede: undefined,
|
||||
firma: undefined,
|
||||
lex_office_id: undefined,
|
||||
};
|
||||
@@ -355,6 +356,7 @@ export function fakeBenutzerComplete() {
|
||||
ort: undefined,
|
||||
adresse: undefined,
|
||||
telefon: undefined,
|
||||
anrede: undefined,
|
||||
rolle: BenutzerRolle.USER,
|
||||
firma: undefined,
|
||||
lex_office_id: undefined,
|
||||
|
||||
@@ -45,14 +45,7 @@ fi
|
||||
|
||||
echo "🧨 Alle Daten aus allen Tabellen werden gelöscht..."
|
||||
|
||||
# Erst müssen wir alle Verbindungen zur Datenbank trennen
|
||||
docker exec -i "$CONTAINER_NAME" psql -U "$DB_USER" "postgres" <<'EOSQL'
|
||||
SELECT pg_terminate_backend(pid)
|
||||
FROM pg_stat_activity
|
||||
WHERE datname = 'main' AND pid <> pg_backend_pid();
|
||||
EOSQL
|
||||
|
||||
# Dann löschen wir die Datenbank und erstellen sie neu
|
||||
# Generate and run TRUNCATE statements for all tables in the public schema
|
||||
docker exec -i "$CONTAINER_NAME" psql -U "$DB_USER" "postgres" <<'EOSQL'
|
||||
DROP DATABASE IF EXISTS main;
|
||||
CREATE DATABASE main WITH OWNER main ENCODING 'UTF8';
|
||||
|
||||
Reference in New Issue
Block a user