4 Commits
dev ... main

Author SHA1 Message Date
Jens Cornelsen
f56c0f83c2 Merge pull request #576 from IBCornelsen/staging
Fernwärme Pforzheim hinzugefuegt
2025-10-14 17:37:07 +02:00
Jens Cornelsen
35a849913e Fernwärme Pforzheim hinzugefuegt 2025-10-14 17:34:59 +02:00
Jens Cornelsen
e01b9b5445 Merge pull request #575 from IBCornelsen/staging
Update main
2025-10-01 16:24:41 +02:00
Jens Cornelsen
e3484718a0 Merge pull request #574 from IBCornelsen/dev
Update staging
2025-10-01 16:21:18 +02:00
86 changed files with 487 additions and 836 deletions

2
.env
View File

@@ -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

View File

@@ -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
@@ -54,15 +29,3 @@ jobs:
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

2
.gitignore vendored
View File

@@ -15,8 +15,6 @@ npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
error.log*
combined.log*
# lockfile
pnpm-lock.yaml

View File

@@ -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

View File

@@ -5,9 +5,6 @@ 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
export default defineConfig({

View File

@@ -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

View File

@@ -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=="],

View File

@@ -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": {

View File

@@ -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);

View File

@@ -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?

View File

@@ -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"),

View File

@@ -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,

View File

@@ -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,

View File

@@ -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",

View File

@@ -49,12 +49,6 @@
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>

View File

@@ -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"
/>

View File

@@ -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>

View File

@@ -746,7 +746,6 @@
<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)}
@@ -754,8 +753,6 @@
{/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}
@@ -764,11 +761,9 @@
{/each}
{/if}
</div>
{/if}
</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>

View File

@@ -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;
@@ -64,13 +62,7 @@
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,19 +110,6 @@
<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>
{: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"
@@ -149,7 +128,6 @@
</div>
</div>
{/if}
{/if}
{/each}
<!-- Wir zeigen Platzhalter an, damit der Nutzer sieht wie viele Bilder er hochladen soll -->

View File

@@ -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,18 +35,6 @@
<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"
@@ -63,15 +51,24 @@
>
<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>
</div>
{/if}
{/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"

View File

@@ -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"]> = [];

View File

@@ -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"

View File

@@ -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 = "";

View File

@@ -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({

View File

@@ -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(),

View File

@@ -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");

View File

@@ -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">

View File

@@ -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>

View File

@@ -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" />

View File

@@ -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) => {

View File

@@ -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;
}

View File

@@ -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),
}));
}

View File

@@ -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})

View File

@@ -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>`
});
}

View File

@@ -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),
});
}

View File

@@ -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),
});
}

View File

@@ -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>
Um Ihre Registrierung abzuschließen, klicken Sie bitte auf den folgenden Link, um Ihre E-Mail-Adresse zu bestätigen:<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>
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>

View File

@@ -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>

View File

@@ -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}

View File

@@ -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

View File

@@ -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";

View File

@@ -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}

View File

@@ -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>

View File

@@ -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>

View File

@@ -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.",
@@ -440,7 +418,6 @@
const merged_versand_ort = versand_ort || ort;
const merged_versand_zusatzzeile = versand_zusatzzeile || zusatzzeile;
if (rechnung) {
const result = await api.rechnung._id.PATCH.fetch({
bezahlmethode: aktiveBezahlmethode,
@@ -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,24 +1213,14 @@ sm:grid-cols-[min-content_min-content_min-content] sm:justify-self-end sm:mt-8"
</div>
</div>
</form>
<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>

View File

@@ -1,5 +1,7 @@
<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>
{#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>

View File

@@ -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>
{#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
>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>

View File

@@ -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()}`
);
}

View File

@@ -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()}`
);
}

View File

@@ -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()}`
);
}

View File

@@ -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) {

View File

@@ -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()}`
);
}

View File

@@ -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()}`
);
}

View File

@@ -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()}`
);
}

View File

@@ -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" : ""

View File

@@ -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" : ""

View File

@@ -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>

View File

@@ -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.

View File

@@ -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.",

View File

@@ -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 }
}

View File

@@ -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 }
}

View File

@@ -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 }
},
})

View File

@@ -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 }
},
})

View 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>

View 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>

View File

@@ -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">

View File

@@ -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">

View File

@@ -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>

View File

@@ -9,7 +9,7 @@ if (valid) {
return Astro.redirect("/dashboard")
}
const redirect = Astro.url.searchParams.get("r");
const redirect = Astro.url.searchParams.get("redirect");
---

View 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>

View File

@@ -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>

View File

@@ -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)

View File

@@ -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>

View File

@@ -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}

View File

@@ -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()}`
);
}

View File

@@ -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()}`
);
}

View File

@@ -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()}`
);
}

View File

@@ -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()}`
);
}

View File

@@ -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()}`
);
}

View File

@@ -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()}`
);
}

View File

@@ -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>

View File

@@ -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>

View File

@@ -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;
}

View File

@@ -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,

View File

@@ -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,

View File

@@ -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';