Compare commits
1 Commits
UMBE
...
registrier
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dc0509cac2 |
2
Makefile
2
Makefile
@@ -59,7 +59,7 @@ all:
|
||||
bun run dev 2>&1 | tee ~/logs/`date '+%d-%m-%Y_%H:%M:%S'`.log
|
||||
|
||||
update-dwd-klimafaktoren-cron:
|
||||
pm2 start bun --name "update-dwd-klimafaktoren-cron" --cron "0 12 28 * *" -- src/cronjobs/update-dwd-klimafaktoren.ts
|
||||
pm2 start bun --name "update-dwd-klimafaktoren-cron" --no-autorestart --cron "0 12 28 * *" -- src/cronjobs/update-dwd-klimafaktoren.ts
|
||||
|
||||
prod: install-dependencies prisma-studio backup-database-cronjob update-dwd-klimafaktoren-cron
|
||||
bun run build
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
version: '3'
|
||||
name: database
|
||||
services:
|
||||
database:
|
||||
build: ./
|
||||
|
||||
@@ -3,12 +3,17 @@
|
||||
# === Configuration ===
|
||||
BUCKET_NAME="ibc-db-backup"
|
||||
ENDPOINT_URL="https://s3.eu-central-3.ionoscloud.com"
|
||||
LOCAL_DOWNLOAD_DIR="./" # Where to save the file
|
||||
LOCAL_DOWNLOAD_DIR="./"
|
||||
|
||||
# === Use filename from argument if provided ===
|
||||
if [ -n "$1" ]; then
|
||||
LATEST_FILE="$1"
|
||||
else
|
||||
echo "📡 No filename provided, fetching latest..."
|
||||
# === Get latest file from IONOS S3 bucket ===
|
||||
LATEST_FILE=$(aws s3api list-objects-v2 \
|
||||
LATEST_FILE=$(aws --profile ionos s3api list-objects-v2 \
|
||||
--bucket "$BUCKET_NAME" \
|
||||
--prefix "data-dump" \
|
||||
--prefix "full-dump" \
|
||||
--endpoint-url "$ENDPOINT_URL" \
|
||||
--query 'Contents | sort_by(@, &LastModified) | [-1].Key' \
|
||||
--output text)
|
||||
@@ -18,12 +23,13 @@ if [ "$LATEST_FILE" == "None" ] || [ -z "$LATEST_FILE" ]; then
|
||||
echo "❌ No matching .sql.br file found."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
FILENAME=$(basename "$LATEST_FILE")
|
||||
SQL_FILE="${FILENAME%.br}" # Remove .br suffix
|
||||
|
||||
echo "📥 Downloading $LATEST_FILE"
|
||||
aws s3 cp "s3://$BUCKET_NAME/$LATEST_FILE" "$LOCAL_DOWNLOAD_DIR" \
|
||||
aws --profile ionos s3 cp "s3://$BUCKET_NAME/$LATEST_FILE" "$LOCAL_DOWNLOAD_DIR" \
|
||||
--endpoint-url "$ENDPOINT_URL"
|
||||
|
||||
# === Decompress with Brotli ===
|
||||
@@ -31,8 +37,8 @@ echo "🗜️ Decompressing $FILENAME -> $SQL_FILE"
|
||||
brotli -d "$FILENAME"
|
||||
|
||||
# === Import into Postgres inside Docker ===
|
||||
echo "🐘 Importing into PostgreSQL (online-energieausweis-database-1:main)"
|
||||
docker exec -i "online-energieausweis-database-1" env PGPASSWORD="hHMP8cd^N3SnzGRR" \
|
||||
echo "🐘 Importing into PostgreSQL (database:main)"
|
||||
docker exec -i "database" env PGPASSWORD="hHMP8cd^N3SnzGRR" \
|
||||
psql -U "main" -d "main" < "$SQL_FILE"
|
||||
|
||||
echo "✅ Import complete."
|
||||
|
||||
@@ -17,8 +17,11 @@ export const createCaller = createCallerFactory({
|
||||
"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"),
|
||||
"auth/verification-code": await import("../src/pages/api/auth/verification-code.ts"),
|
||||
"aufnahme": await import("../src/pages/api/aufnahme/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"),
|
||||
"ausweise": await import("../src/pages/api/ausweise/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"),
|
||||
"bilder/[id]": await import("../src/pages/api/bilder/[id].ts"),
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { BedarfsausweisWohnen, Enums, Rechnung, VerbrauchsausweisGewerbe, VerbrauchsausweisWohnen } from "#lib/server/prisma.js";
|
||||
import moment from "moment";
|
||||
import { format } from "sharp";
|
||||
|
||||
export let bestellungen: (Rechnung & {
|
||||
verbrauchsausweis_wohnen: VerbrauchsausweisWohnen | null,
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { fade } from "svelte/transition";
|
||||
import WidgetCardTemplate from "#components/widgets/immowelt/WidgetCardTemplate.svelte";
|
||||
import { PRICES } from "#lib/constants.js";
|
||||
import { Enums } from "#lib/client/prisma";
|
||||
|
||||
import { Enums } from "#lib/client/prisma.js";
|
||||
|
||||
let ausnahme: boolean = false;
|
||||
let oneBOX: boolean = false;
|
||||
@@ -25,7 +23,9 @@
|
||||
$: ausnahme =
|
||||
leerStand === "mehr als 30" ||
|
||||
heizungsAlter === "< 3" ||
|
||||
(baujahr === "vor 1978" && einheiten === "bis 4 Wohneinheiten" && sanierungsstatus === "unsaniert");
|
||||
(baujahr === "vor 1978" &&
|
||||
einheiten === "bis 4 Wohneinheiten" &&
|
||||
sanierungsstatus === "unsaniert");
|
||||
|
||||
$: isTwoBoxReason = twoBoxReason.includes(anlass);
|
||||
|
||||
@@ -37,36 +37,32 @@ $: oneBOX =
|
||||
(gebaeudetyp === "Gewerbegebäude" && leerStand === "mehr als 30");
|
||||
|
||||
$: threeBOX =
|
||||
(ausnahme && gebaeudetyp === "Mischgebäude" && isTwoBoxReason && leerStand !== "mehr als 30");
|
||||
|
||||
$: standardXL =
|
||||
(einheiten === "mehr als 4 Wohneinheiten")
|
||||
ausnahme &&
|
||||
gebaeudetyp === "Mischgebäude" &&
|
||||
isTwoBoxReason &&
|
||||
leerStand !== "mehr als 30";
|
||||
|
||||
$: standardXL = einheiten === "mehr als 4 Wohneinheiten";
|
||||
</script>
|
||||
|
||||
|
||||
<div id="IBC_app">
|
||||
|
||||
<input id="recode" type="hidden" value="widgetvorlage" />
|
||||
|
||||
<div id="OEA_input">
|
||||
|
||||
|
||||
<div id="firstrow" class="firstrow"
|
||||
<div
|
||||
id="firstrow"
|
||||
class="firstrow"
|
||||
class:sm:grid-cols-3={isTwoBoxReason}
|
||||
class:sm:grid-cols-2={!isTwoBoxReason}>
|
||||
|
||||
|
||||
|
||||
class:sm:grid-cols-2={!isTwoBoxReason}
|
||||
>
|
||||
<div class="auswahl">
|
||||
<div class="titel">Anlass</div>
|
||||
<select
|
||||
id="anlass"
|
||||
class="selectfeld"
|
||||
bind:value={anlass}>
|
||||
<select id="anlass" class="selectfeld" bind:value={anlass}>
|
||||
>
|
||||
<option selected disabled>bitte auswählen</option>
|
||||
<option value="Vermietung/Verkauf">Vermietung/Verkauf</option>
|
||||
<option value="Vermietung/Verkauf"
|
||||
>Vermietung/Verkauf</option
|
||||
>
|
||||
<option value="Modernisierung">Modernisierung</option>
|
||||
<option value="Neubau">Neubau</option>
|
||||
<option value="Erweiterung">Erweiterung</option>
|
||||
@@ -77,9 +73,7 @@ $: standardXL =
|
||||
|
||||
<div class="auswahl">
|
||||
<div class="titel">Gebäudetyp</div>
|
||||
<select
|
||||
class="selectfeld"
|
||||
bind:value={gebaeudetyp}>
|
||||
<select class="selectfeld" bind:value={gebaeudetyp}>
|
||||
>
|
||||
<option selected disabled>bitte auswählen</option>
|
||||
<option value="Einfamilienhaus">Einfamilienhaus</option>
|
||||
@@ -93,10 +87,7 @@ $: standardXL =
|
||||
{#if isTwoBoxReason}
|
||||
<div class="auswahl">
|
||||
<div class="titel">Sanierungsstand</div>
|
||||
<select
|
||||
class="selectfeld"
|
||||
bind:value={sanierungsstatus}>
|
||||
|
||||
<select class="selectfeld" bind:value={sanierungsstatus}>
|
||||
>
|
||||
<option selected disabled>bitte auswählen</option>
|
||||
<option value="saniert">saniert</option>
|
||||
@@ -104,7 +95,6 @@ $: standardXL =
|
||||
</select>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
</div>
|
||||
|
||||
{#if isTwoBoxReason}
|
||||
@@ -115,7 +105,6 @@ $: standardXL =
|
||||
id="baujahr"
|
||||
class="selectfeld"
|
||||
bind:value={baujahr}
|
||||
|
||||
>
|
||||
<option selected disabled>bitte auswählen</option>
|
||||
<option value="vor 1978">vor 1978</option>
|
||||
@@ -125,10 +114,7 @@ $: standardXL =
|
||||
|
||||
<div class="auswahl">
|
||||
<div class="titel">Heizungsalter</div>
|
||||
<select
|
||||
class="selectfeld"
|
||||
bind:value={heizungsAlter}
|
||||
>
|
||||
<select class="selectfeld" bind:value={heizungsAlter}>
|
||||
<option selected disabled>bitte auswählen</option>
|
||||
<option value="< 3">jünger als 3 Jahre</option>
|
||||
<option value=">= 3">3 Jahre oder älter</option>
|
||||
@@ -137,10 +123,7 @@ $: standardXL =
|
||||
|
||||
<div class="auswahl">
|
||||
<div class="titel">Wohneinheiten</div>
|
||||
<select
|
||||
class="selectfeld"
|
||||
bind:value={einheiten}
|
||||
>
|
||||
<select class="selectfeld" bind:value={einheiten}>
|
||||
<option selected disabled>bitte auswählen</option>
|
||||
<option value="bis 4 Wohneinheiten"
|
||||
>bis 4 Wohneinheiten</option
|
||||
@@ -153,10 +136,7 @@ $: standardXL =
|
||||
|
||||
<div class="OEA_item4">
|
||||
<div class="titel">Leerstand</div>
|
||||
<select
|
||||
class="selectfeld ausnahmen"
|
||||
bind:value={leerStand}
|
||||
>
|
||||
<select class="selectfeld ausnahmen" bind:value={leerStand}>
|
||||
<option selected disabled>bitte auswählen</option>
|
||||
<option value="bis 30">bis 30%</option>
|
||||
<option value="mehr als 30">mehr als 30%</option>
|
||||
@@ -165,23 +145,34 @@ $: standardXL =
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div id="thirdrow" class="thirdrow"
|
||||
<div
|
||||
id="thirdrow"
|
||||
class="thirdrow"
|
||||
class:grid-cols-1={oneBOX}
|
||||
class:md:grid-cols-6={threeBOX}
|
||||
class:md:grid-cols-4={!oneBOX && !threeBOX}
|
||||
>
|
||||
|
||||
|
||||
{#if isTwoBoxReason && (gebaeudetyp != "Gewerbegebäude") && (ausnahme === false)}
|
||||
|
||||
|
||||
{#if isTwoBoxReason && gebaeudetyp != "Gewerbegebäude" && ausnahme === false}
|
||||
<WidgetCardTemplate
|
||||
name="Verbrauchsausweis Wohngebäude"
|
||||
price = {PRICES.VerbrauchsausweisWohnen[standardXL ? Enums.AusweisTyp.standardXL : Enums.AusweisTyp.Standard]}
|
||||
price1 = {PRICES.VerbrauchsausweisWohnen[standardXL ? Enums.AusweisTyp.BeratungXL : Enums.AusweisTyp.Beratung]}
|
||||
price2 = {PRICES.VerbrauchsausweisWohnen[standardXL ? Enums.AusweisTyp.OfflineXL : Enums.AusweisTyp.Offline]}
|
||||
|
||||
src={'https://online-energieausweis.org/images/partner/'+partner+'/wohngebaeude.svg'}
|
||||
price={PRICES.VerbrauchsausweisWohnen[
|
||||
standardXL
|
||||
? Enums.AusweisTyp.standardXL
|
||||
: Enums.AusweisTyp.Standard
|
||||
]}
|
||||
price1={PRICES.VerbrauchsausweisWohnen[
|
||||
standardXL
|
||||
? Enums.AusweisTyp.BeratungXL
|
||||
: Enums.AusweisTyp.Beratung
|
||||
]}
|
||||
price2={PRICES.VerbrauchsausweisWohnen[
|
||||
standardXL
|
||||
? Enums.AusweisTyp.OfflineXL
|
||||
: Enums.AusweisTyp.Offline
|
||||
]}
|
||||
src={"https://online-energieausweis.org/images/partner/" +
|
||||
partner +
|
||||
"/wohngebaeude.svg"}
|
||||
alt="Wohnhaus Verbrauchsausweis"
|
||||
variant="einfach"
|
||||
empfehlung="nein"
|
||||
@@ -189,27 +180,40 @@ $: standardXL =
|
||||
services={[
|
||||
["3 Jahresverbräuche der Heizung benötigt.", true],
|
||||
["Zulässig bei Vermietung oder Verkauf.", true],
|
||||
["Unzulässig bei unsanierten Gebäuden vor 1978.", false],
|
||||
[
|
||||
"Unzulässig bei unsanierten Gebäuden vor 1978.",
|
||||
false,
|
||||
],
|
||||
["Ungenau durch individuelles Heizverhalten.", false],
|
||||
["Wird nicht immer bei den Banken akzeptiert.", false]
|
||||
["Wird nicht immer bei den Banken akzeptiert.", false],
|
||||
]}
|
||||
|
||||
href_buy1={`https://online-energieausweis.org/${partner}/energieausweis-erstellen/verbrauchsausweis-wohngebaeude/${standardXL ? '?ausweistyp=standardXL' : ''}`}
|
||||
href_buy2={`https://online-energieausweis.org/${partner}/energieausweis-erstellen/verbrauchsausweis-wohngebaeude/${standardXL ? '?ausweistyp=BeratungXL' : '?ausweistyp=Beratung'}`}
|
||||
href_buy3={`https://online-energieausweis.org/${partner}/energieausweis-erstellen/verbrauchsausweis-wohngebaeude/${standardXL ? '?ausweistyp=OfflineXL' : '?ausweistyp=Offline'}`}
|
||||
|
||||
href_buy1={`https://online-energieausweis.org/${partner}/energieausweis-erstellen/verbrauchsausweis-wohngebaeude/${standardXL ? "?ausweistyp=standardXL" : ""}`}
|
||||
href_buy2={`https://online-energieausweis.org/${partner}/energieausweis-erstellen/verbrauchsausweis-wohngebaeude/${standardXL ? "?ausweistyp=BeratungXL" : "?ausweistyp=Beratung"}`}
|
||||
href_buy3={`https://online-energieausweis.org/${partner}/energieausweis-erstellen/verbrauchsausweis-wohngebaeude/${standardXL ? "?ausweistyp=OfflineXL" : "?ausweistyp=Offline"}`}
|
||||
></WidgetCardTemplate>
|
||||
|
||||
{/if}
|
||||
|
||||
{#if isTwoBoxReason && (gebaeudetyp != "Gewerbegebäude")}
|
||||
|
||||
{#if isTwoBoxReason && gebaeudetyp != "Gewerbegebäude"}
|
||||
<WidgetCardTemplate
|
||||
name="Bedarfsausweis Wohngebäude"
|
||||
price = {PRICES.BedarfsausweisWohnen[standardXL ? Enums.AusweisTyp.standardXL : Enums.AusweisTyp.Standard]}
|
||||
price1 = {PRICES.BedarfsausweisWohnen[standardXL ? Enums.AusweisTyp.BeratungXL : Enums.AusweisTyp.Beratung]}
|
||||
price2 = {PRICES.BedarfsausweisWohnen[standardXL ? Enums.AusweisTyp.OfflineXL : Enums.AusweisTyp.Offline]}
|
||||
src={'https://online-energieausweis.org/images/partner/'+partner+'/wohngebaeude.svg'}
|
||||
price={PRICES.BedarfsausweisWohnen[
|
||||
standardXL
|
||||
? Enums.AusweisTyp.standardXL
|
||||
: Enums.AusweisTyp.Standard
|
||||
]}
|
||||
price1={PRICES.BedarfsausweisWohnen[
|
||||
standardXL
|
||||
? Enums.AusweisTyp.BeratungXL
|
||||
: Enums.AusweisTyp.Beratung
|
||||
]}
|
||||
price2={PRICES.BedarfsausweisWohnen[
|
||||
standardXL
|
||||
? Enums.AusweisTyp.OfflineXL
|
||||
: Enums.AusweisTyp.Offline
|
||||
]}
|
||||
src={"https://online-energieausweis.org/images/partner/" +
|
||||
partner +
|
||||
"/wohngebaeude.svg"}
|
||||
alt="Wohnhaus Bedarfsausweis"
|
||||
variant="fundiert"
|
||||
empfehlung="ja"
|
||||
@@ -219,164 +223,240 @@ $: standardXL =
|
||||
["Für Vermietung, Verkauf und Finanzierung.", true],
|
||||
["Zulässig auch für unsanierte Objekte.", true],
|
||||
["Kann als Grundlage für den ISFP dienen.", true],
|
||||
["Objektivere Berechnungsmethode nach DIN 18599.", true],
|
||||
[
|
||||
"Objektivere Berechnungsmethode nach DIN 18599.",
|
||||
true,
|
||||
],
|
||||
]}
|
||||
|
||||
href_buy1={`https://online-energieausweis.org/${partner}/energieausweis-erstellen/bedarfsausweis-wohngebaeude/${standardXL ? '?ausweistyp=standardXL' : ''}`}
|
||||
href_buy2={`https://online-energieausweis.org/${partner}/energieausweis-erstellen/bedarfsausweis-wohngebaeude/${standardXL ? '?ausweistyp=BeratungXL' : '?ausweistyp=Beratung'}`}
|
||||
href_buy3={`https://online-energieausweis.org/${partner}/energieausweis-erstellen/bedarfsausweis-wohngebaeude/${standardXL ? '?ausweistyp=OfflineXL' : '?ausweistyp=Offline'}`}
|
||||
|
||||
href_buy1={`https://online-energieausweis.org/${partner}/energieausweis-erstellen/bedarfsausweis-wohngebaeude/${standardXL ? "?ausweistyp=standardXL" : ""}`}
|
||||
href_buy2={`https://online-energieausweis.org/${partner}/energieausweis-erstellen/bedarfsausweis-wohngebaeude/${standardXL ? "?ausweistyp=BeratungXL" : "?ausweistyp=Beratung"}`}
|
||||
href_buy3={`https://online-energieausweis.org/${partner}/energieausweis-erstellen/bedarfsausweis-wohngebaeude/${standardXL ? "?ausweistyp=OfflineXL" : "?ausweistyp=Offline"}`}
|
||||
></WidgetCardTemplate>
|
||||
|
||||
{/if}
|
||||
|
||||
{#if isTwoBoxReason && isGewerbe && (leerStand != "mehr als 30")}
|
||||
|
||||
{#if isTwoBoxReason && isGewerbe && leerStand != "mehr als 30"}
|
||||
<WidgetCardTemplate
|
||||
name="Verbrauchsausweis Gewerbegebäude"
|
||||
price = {PRICES.VerbrauchsausweisGewerbe[standardXL ? Enums.AusweisTyp.standardXL : Enums.AusweisTyp.Standard]}
|
||||
price1 = {PRICES.VerbrauchsausweisGewerbe[standardXL ? Enums.AusweisTyp.BeratungXL : Enums.AusweisTyp.Beratung]}
|
||||
price2 = {PRICES.VerbrauchsausweisGewerbe[standardXL ? Enums.AusweisTyp.OfflineXL : Enums.AusweisTyp.Offline]}
|
||||
src={'https://online-energieausweis.org/images/partner/'+partner+'/gewerbegebaeude.svg'}
|
||||
price={PRICES.VerbrauchsausweisGewerbe[
|
||||
standardXL
|
||||
? Enums.AusweisTyp.standardXL
|
||||
: Enums.AusweisTyp.Standard
|
||||
]}
|
||||
price1={PRICES.VerbrauchsausweisGewerbe[
|
||||
standardXL
|
||||
? Enums.AusweisTyp.BeratungXL
|
||||
: Enums.AusweisTyp.Beratung
|
||||
]}
|
||||
price2={PRICES.VerbrauchsausweisGewerbe[
|
||||
standardXL
|
||||
? Enums.AusweisTyp.OfflineXL
|
||||
: Enums.AusweisTyp.Offline
|
||||
]}
|
||||
src={"https://online-energieausweis.org/images/partner/" +
|
||||
partner +
|
||||
"/gewerbegebaeude.svg"}
|
||||
alt="Gewerbe Verbrauchsausweis"
|
||||
variant="einfach"
|
||||
empfehlung="nein"
|
||||
cta="jetzt online erstellen"
|
||||
services={[
|
||||
|
||||
["3 Jahresverbräuche von Heizung Gebäudestrom nötig.", true],
|
||||
[
|
||||
"3 Jahresverbräuche von Heizung Gebäudestrom nötig.",
|
||||
true,
|
||||
],
|
||||
["Zulässig bei Vermietung oder Verkauf.", true],
|
||||
["Für bauliche und energetische Maßnahmen ungeeignet.", false],
|
||||
[
|
||||
"Für bauliche und energetische Maßnahmen ungeeignet.",
|
||||
false,
|
||||
],
|
||||
["Wird nicht immer bei den Banken akzeptiert.", false],
|
||||
["Ungenau durch individuelles Heizverhalten", false],
|
||||
]}
|
||||
|
||||
href_buy1={`https://online-energieausweis.org/${partner}/energieausweis-erstellen/verbrauchsausweis-gewerbe/${standardXL ? '?ausweistyp=standardXL' : ''}`}
|
||||
href_buy2={`https://online-energieausweis.org/${partner}/energieausweis-erstellen/verbrauchsausweis-gewerbe/${standardXL ? '?ausweistyp=BeratungXL' : '?ausweistyp=Beratung'}`}
|
||||
href_buy3={`https://online-energieausweis.org/${partner}/energieausweis-erstellen/verbrauchsausweis-gewerbe/${standardXL ? '?ausweistyp=OfflineXL' : '?ausweistyp=Offline'}`}
|
||||
href_buy1={`https://online-energieausweis.org/${partner}/energieausweis-erstellen/verbrauchsausweis-gewerbe/${standardXL ? "?ausweistyp=standardXL" : ""}`}
|
||||
href_buy2={`https://online-energieausweis.org/${partner}/energieausweis-erstellen/verbrauchsausweis-gewerbe/${standardXL ? "?ausweistyp=BeratungXL" : "?ausweistyp=Beratung"}`}
|
||||
href_buy3={`https://online-energieausweis.org/${partner}/energieausweis-erstellen/verbrauchsausweis-gewerbe/${standardXL ? "?ausweistyp=OfflineXL" : "?ausweistyp=Offline"}`}
|
||||
></WidgetCardTemplate>
|
||||
|
||||
{/if}
|
||||
|
||||
{#if isTwoBoxReason && isGewerbe}
|
||||
|
||||
<WidgetCardTemplate
|
||||
name="Bedarfsausweis Gewerbegebäude"
|
||||
price = {PRICES.BedarfsausweisGewerbe[standardXL ? Enums.AusweisTyp.standardXL : Enums.AusweisTyp.Standard]}
|
||||
price1 = {PRICES.BedarfsausweisGewerbe[standardXL ? Enums.AusweisTyp.BeratungXL : Enums.AusweisTyp.Beratung]}
|
||||
price2 = {PRICES.BedarfsausweisGewerbe[standardXL ? Enums.AusweisTyp.OfflineXL : Enums.AusweisTyp.Offline]}
|
||||
src={'https://online-energieausweis.org/images/partner/'+partner+'/gewerbegebaeude.svg'}
|
||||
price={PRICES.BedarfsausweisGewerbe[
|
||||
standardXL
|
||||
? Enums.AusweisTyp.standardXL
|
||||
: Enums.AusweisTyp.Standard
|
||||
]}
|
||||
price1={PRICES.BedarfsausweisGewerbe[
|
||||
standardXL
|
||||
? Enums.AusweisTyp.BeratungXL
|
||||
: Enums.AusweisTyp.Beratung
|
||||
]}
|
||||
price2={PRICES.BedarfsausweisGewerbe[
|
||||
standardXL
|
||||
? Enums.AusweisTyp.OfflineXL
|
||||
: Enums.AusweisTyp.Offline
|
||||
]}
|
||||
src={"https://online-energieausweis.org/images/partner/" +
|
||||
partner +
|
||||
"/gewerbegebaeude.svg"}
|
||||
alt="Gewerbe Bedarfsausweis"
|
||||
variant="fundiert"
|
||||
empfehlung="ja"
|
||||
cta="Angebot anfragen"
|
||||
services={[
|
||||
|
||||
["Mehrzonenmodell nach DIN 18599.", true],
|
||||
["Zulässig bei Vermietung oder Verkauf.", true],
|
||||
["Grundlage für Sanierung-Varianten.", true],
|
||||
["Objektiveres, besser vergleichbares Ergebnis.", true],
|
||||
["Zulässig bei Leerstand oder fehlenden Verbräuchen", true],
|
||||
[
|
||||
"Zulässig bei Leerstand oder fehlenden Verbräuchen",
|
||||
true,
|
||||
],
|
||||
]}
|
||||
|
||||
href_buy1={`https://online-energieausweis.org/${partner}/angebot-anfragen/bedarfsausweis-gewerbe-anfragen/${standardXL ? '?ausweistyp=standardXL' : ''}`}
|
||||
href_buy2={`https://online-energieausweis.org/${partner}/angebot-anfragen/bedarfsausweis-gewerbe-anfragen/${standardXL ? '?ausweistyp=BeratungXL' : '?ausweistyp=Beratung'}`}
|
||||
|
||||
href_buy1={`https://online-energieausweis.org/${partner}/angebot-anfragen/bedarfsausweis-gewerbe-anfragen/${standardXL ? "?ausweistyp=standardXL" : ""}`}
|
||||
href_buy2={`https://online-energieausweis.org/${partner}/angebot-anfragen/bedarfsausweis-gewerbe-anfragen/${standardXL ? "?ausweistyp=BeratungXL" : "?ausweistyp=Beratung"}`}
|
||||
></WidgetCardTemplate>
|
||||
|
||||
{/if}
|
||||
|
||||
{#if (anlass != "bitte auswählen") && !isTwoBoxReason && (gebaeudetyp != "Gewerbegebäude")}
|
||||
|
||||
{#if anlass != "bitte auswählen" && !isTwoBoxReason && gebaeudetyp != "Gewerbegebäude"}
|
||||
<WidgetCardTemplate
|
||||
name="GEG-Nachweis Wohngebäude"
|
||||
price = {PRICES.GEGNachweisWohnen[standardXL ? Enums.AusweisTyp.standardXL : Enums.AusweisTyp.Standard]}
|
||||
price1 = {PRICES.GEGNachweisWohnen[standardXL ? Enums.AusweisTyp.BeratungXL : Enums.AusweisTyp.Beratung]}
|
||||
price2 = {PRICES.GEGNachweisWohnen[standardXL ? Enums.AusweisTyp.OfflineXL : Enums.AusweisTyp.Offline]}
|
||||
src={'https://online-energieausweis.org/images/partner/'+partner+'/wohngebaeude.svg'}
|
||||
price={PRICES.GEGNachweisWohnen[
|
||||
standardXL
|
||||
? Enums.AusweisTyp.standardXL
|
||||
: Enums.AusweisTyp.Standard
|
||||
]}
|
||||
price1={PRICES.GEGNachweisWohnen[
|
||||
standardXL
|
||||
? Enums.AusweisTyp.BeratungXL
|
||||
: Enums.AusweisTyp.Beratung
|
||||
]}
|
||||
price2={PRICES.GEGNachweisWohnen[
|
||||
standardXL
|
||||
? Enums.AusweisTyp.OfflineXL
|
||||
: Enums.AusweisTyp.Offline
|
||||
]}
|
||||
src={"https://online-energieausweis.org/images/partner/" +
|
||||
partner +
|
||||
"/wohngebaeude.svg"}
|
||||
alt="GEG-Nachweis-Wohnen"
|
||||
variant="Bauvorlage"
|
||||
empfehlung="ja"
|
||||
cta="Angebot anfragen"
|
||||
services={[
|
||||
|
||||
["Nachweis fürs Bauamt bei Neubau oder Modernisierung.", true],
|
||||
["Beinhaltet die Effizienz der Bauteile und Anlagentechnik.", true],
|
||||
["Erstellung des Energieausweises nach Abschluss inklusive.", true],
|
||||
["Berechnung und Bilanzierung nach aktueller DIN 18599.", true],
|
||||
["Zonierung und Erstellung eines 3D Gebäudemodells.", true],
|
||||
[
|
||||
"Nachweis fürs Bauamt bei Neubau oder Modernisierung.",
|
||||
true,
|
||||
],
|
||||
[
|
||||
"Beinhaltet die Effizienz der Bauteile und Anlagentechnik.",
|
||||
true,
|
||||
],
|
||||
[
|
||||
"Erstellung des Energieausweises nach Abschluss inklusive.",
|
||||
true,
|
||||
],
|
||||
[
|
||||
"Berechnung und Bilanzierung nach aktueller DIN 18599.",
|
||||
true,
|
||||
],
|
||||
[
|
||||
"Zonierung und Erstellung eines 3D Gebäudemodells.",
|
||||
true,
|
||||
],
|
||||
]}
|
||||
|
||||
href_buy1={`https://online-energieausweis.org/${partner}/angebot-anfragen/geg-nachweis-wohnen-anfragen/${standardXL ? '?ausweistyp=standardXL' : ''}`}
|
||||
href_buy2={`https://online-energieausweis.org/${partner}/angebot-anfragen/geg-nachweis-wohnen-anfragen/${standardXL ? '?ausweistyp=BeratungXL' : '?ausweistyp=Beratung'}`}
|
||||
|
||||
|
||||
href_buy1={`https://online-energieausweis.org/${partner}/angebot-anfragen/geg-nachweis-wohnen-anfragen/${standardXL ? "?ausweistyp=standardXL" : ""}`}
|
||||
href_buy2={`https://online-energieausweis.org/${partner}/angebot-anfragen/geg-nachweis-wohnen-anfragen/${standardXL ? "?ausweistyp=BeratungXL" : "?ausweistyp=Beratung"}`}
|
||||
></WidgetCardTemplate>
|
||||
|
||||
{/if}
|
||||
|
||||
{#if (anlass != "bitte auswählen") && !isTwoBoxReason && isGewerbe}
|
||||
|
||||
{#if anlass != "bitte auswählen" && !isTwoBoxReason && isGewerbe}
|
||||
<WidgetCardTemplate
|
||||
name="GEG-Nachweis Gewerbegebäude"
|
||||
price = {PRICES.GEGNachweisGewerbe[standardXL ? Enums.AusweisTyp.standardXL : Enums.AusweisTyp.Standard]}
|
||||
price1 = {PRICES.GEGNachweisGewerbe[standardXL ? Enums.AusweisTyp.BeratungXL : Enums.AusweisTyp.Beratung]}
|
||||
price2 = {PRICES.GEGNachweisGewerbe[standardXL ? Enums.AusweisTyp.OfflineXL : Enums.AusweisTyp.Offline]}
|
||||
src={'https://online-energieausweis.org/images/partner/'+partner+'/gewerbegebaeude.svg'}
|
||||
price={PRICES.GEGNachweisGewerbe[
|
||||
standardXL
|
||||
? Enums.AusweisTyp.standardXL
|
||||
: Enums.AusweisTyp.Standard
|
||||
]}
|
||||
price1={PRICES.GEGNachweisGewerbe[
|
||||
standardXL
|
||||
? Enums.AusweisTyp.BeratungXL
|
||||
: Enums.AusweisTyp.Beratung
|
||||
]}
|
||||
price2={PRICES.GEGNachweisGewerbe[
|
||||
standardXL
|
||||
? Enums.AusweisTyp.OfflineXL
|
||||
: Enums.AusweisTyp.Offline
|
||||
]}
|
||||
src={"https://online-energieausweis.org/images/partner/" +
|
||||
partner +
|
||||
"/gewerbegebaeude.svg"}
|
||||
alt="GEG-Nachweis-Gewerbe"
|
||||
variant="Bauvorlage"
|
||||
empfehlung="ja"
|
||||
cta="Angebot anfragen"
|
||||
services={[
|
||||
|
||||
["Nachweis fürs Bauamt bei Neubau oder Modernisierung.", true],
|
||||
["Beinhaltet die Effizienz der Bauteile und Anlagentechnik.", true],
|
||||
["Erstellung des Energieausweises nach Abschluss inklusive.", true],
|
||||
["Berechnung und Bilanzierung nach aktueller DIN 18599.", true],
|
||||
["Mehrzonenmodell inkl. Erstellung eines 3D Gebäudemodells.", true],
|
||||
[
|
||||
"Nachweis fürs Bauamt bei Neubau oder Modernisierung.",
|
||||
true,
|
||||
],
|
||||
[
|
||||
"Beinhaltet die Effizienz der Bauteile und Anlagentechnik.",
|
||||
true,
|
||||
],
|
||||
[
|
||||
"Erstellung des Energieausweises nach Abschluss inklusive.",
|
||||
true,
|
||||
],
|
||||
[
|
||||
"Berechnung und Bilanzierung nach aktueller DIN 18599.",
|
||||
true,
|
||||
],
|
||||
[
|
||||
"Mehrzonenmodell inkl. Erstellung eines 3D Gebäudemodells.",
|
||||
true,
|
||||
],
|
||||
]}
|
||||
|
||||
href_buy1={`https://online-energieausweis.org/${partner}/angebot-anfragen/geg-nachweis-gewerbe-anfragen/${standardXL ? '?ausweistyp=standardXL' : ''}`}
|
||||
href_buy2={`https://online-energieausweis.org/${partner}/angebot-anfragen/geg-nachweis-gewerbe-anfragen/${standardXL ? '?ausweistyp=BeratungXL' : '?ausweistyp=Beratung'}`}
|
||||
|
||||
href_buy1={`https://online-energieausweis.org/${partner}/angebot-anfragen/geg-nachweis-gewerbe-anfragen/${standardXL ? "?ausweistyp=standardXL" : ""}`}
|
||||
href_buy2={`https://online-energieausweis.org/${partner}/angebot-anfragen/geg-nachweis-gewerbe-anfragen/${standardXL ? "?ausweistyp=BeratungXL" : "?ausweistyp=Beratung"}`}
|
||||
></WidgetCardTemplate>
|
||||
|
||||
{/if}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<style lang="postcss">
|
||||
|
||||
#IBC_app {
|
||||
|
||||
@font-face {
|
||||
font-family: "immo Sans";
|
||||
src: url('/fonts/Immo-Sans/immoSans-Regular.eot');
|
||||
src: url('/fonts/Immo-Sans/immoSans-Regular.eot?#iefix') format('embedded-opentype'),
|
||||
url('/fonts/Immo-Sans/immoSans-Regular.woff2') format('woff2'),
|
||||
url('/fonts/Immo-Sans/immoSans-Regular.woff') format('woff');
|
||||
src: url("/fonts/Immo-Sans/immoSans-Regular.eot");
|
||||
src:
|
||||
url("/fonts/Immo-Sans/immoSans-Regular.eot?#iefix")
|
||||
format("embedded-opentype"),
|
||||
url("/fonts/Immo-Sans/immoSans-Regular.woff2") format("woff2"),
|
||||
url("/fonts/Immo-Sans/immoSans-Regular.woff") format("woff");
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "immo Sans Bold";
|
||||
src: url('/fonts/Immo-Sans/immoSans-Bold.eot');
|
||||
src: url('/fonts/Immo-Sans/immoSans-Bold.eot?#iefix') format('embedded-opentype'), url('../../Fonts/Immo-Sans/immoSans-Bold.woff2') format('woff2'), url('../../Fonts/Immo-Sans/immoSans-Bold.woff') format('woff');
|
||||
src: url("/fonts/Immo-Sans/immoSans-Bold.eot");
|
||||
src:
|
||||
url("/fonts/Immo-Sans/immoSans-Bold.eot?#iefix")
|
||||
format("embedded-opentype"),
|
||||
url("../../Fonts/Immo-Sans/immoSans-Bold.woff2") format("woff2"),
|
||||
url("../../Fonts/Immo-Sans/immoSans-Bold.woff") format("woff");
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Antique Olive Compact bold';
|
||||
font-family: "Antique Olive Compact bold";
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
src: url("/fonts/Antique Olive Std Compact.woff2") format('woff2');
|
||||
src: url("/fonts/Antique Olive Std Compact.woff2") format("woff2");
|
||||
}
|
||||
|
||||
@apply min-w-[320px] max-w-[1920px] p-[4px]
|
||||
@@ -384,35 +464,42 @@ $: standardXL =
|
||||
|
||||
font-family: "immo Sans";
|
||||
select option {
|
||||
font-family: "immo Sans",sans-serif;}
|
||||
font-family: "immo Sans", sans-serif;
|
||||
}
|
||||
|
||||
.firstrow{@apply grid grid-cols-1 gap-x-4 gap-y-2
|
||||
.firstrow {
|
||||
@apply grid grid-cols-1 gap-x-4 gap-y-2
|
||||
sm:grid-cols-2 sm:gap-x-4 sm:gap-y-2;
|
||||
|
||||
.titel{@apply text-black font-bold bg-[#ffcc00] px-2 py-1 rounded-[0.25rem];}
|
||||
|
||||
.selectfeld{@apply w-full px-2 py-1 min-h-[38px] ring-1 ring-black/15}
|
||||
|
||||
.titel {
|
||||
@apply text-black font-bold bg-[#ffcc00] px-2 py-1 rounded-[0.25rem];
|
||||
}
|
||||
|
||||
.secondrow{@apply grid grid-cols-2 gap-x-4 gap-y-2 mt-4
|
||||
.selectfeld {
|
||||
@apply w-full px-2 py-1 min-h-[38px] ring-1 ring-black/15;
|
||||
}
|
||||
}
|
||||
|
||||
.secondrow {
|
||||
@apply grid grid-cols-2 gap-x-4 gap-y-2 mt-4
|
||||
sm:grid-cols-4 sm:gap-x-4 sm:gap-y-2;
|
||||
|
||||
.titel{@apply text-black font-bold bg-[#cccccc] px-2 py-1 rounded-[0.25rem];}
|
||||
|
||||
.selectfeld{@apply w-full px-2 py-1 min-h-[38px] ring-1 ring-black/15}
|
||||
|
||||
.titel {
|
||||
@apply text-black font-bold bg-[#cccccc] px-2 py-1 rounded-[0.25rem];
|
||||
}
|
||||
|
||||
#OEA_input{@apply grid}
|
||||
.selectfeld {
|
||||
@apply w-full px-2 py-1 min-h-[38px] ring-1 ring-black/15;
|
||||
}
|
||||
}
|
||||
|
||||
.thirdrow{@apply grid grid-cols-1 gap-x-4 gap-y-2 col-start-1
|
||||
#OEA_input {
|
||||
@apply grid;
|
||||
}
|
||||
|
||||
.thirdrow {
|
||||
@apply grid grid-cols-1 gap-x-4 gap-y-2 col-start-1
|
||||
md:grid-cols-4 md:gap-x-4 md:gap-y-2;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -34,7 +34,6 @@
|
||||
ab {price} €
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<hr class="col-span-2 w-full md:w-[50%] md:m-auto bg-[#ffcc00] h-[2px]" />
|
||||
@@ -52,9 +51,11 @@
|
||||
|
||||
<hr class="col-span-2 w-full md:w-[50%] md:m-auto bg-[#ffcc00] h-[2px]" />
|
||||
|
||||
<div class="sumCent buttoncols"
|
||||
<div
|
||||
class="sumCent buttoncols"
|
||||
class:md:grid-cols-3={href_buy3}
|
||||
class:md:grid-cols-2={!href_buy3}>
|
||||
class:md:grid-cols-2={!href_buy3}
|
||||
>
|
||||
<a
|
||||
href={href_buy1}
|
||||
class="buttoncol"
|
||||
|
||||
@@ -38,13 +38,13 @@ export const BedarfsausweisWohnenSchema = z.object({
|
||||
volumen: z.number().nullish(),
|
||||
dicht: z.boolean().nullish(),
|
||||
fenster_flaeche_1: z.number().nullish(),
|
||||
fenster_art_1: z.string().nullish(),
|
||||
fenster_art_1: z.number().nullish(),
|
||||
fenster_flaeche_2: z.number().nullish(),
|
||||
fenster_art_2: z.string().nullish(),
|
||||
fenster_art_2: z.number().nullish(),
|
||||
dachfenster_flaeche: z.number().nullish(),
|
||||
dachfenster_art: z.string().nullish(),
|
||||
dachfenster_art: z.number().nullish(),
|
||||
haustuer_flaeche: z.number().nullish(),
|
||||
haustuer_art: z.string().nullish(),
|
||||
haustuer_art: z.number().nullish(),
|
||||
dach_bauart: z.string().nullish(),
|
||||
decke_bauart: z.string().nullish(),
|
||||
dach_daemmung: z.string().nullish(),
|
||||
|
||||
29
src/lib/auth/time-based-hash.ts
Normal file
29
src/lib/auth/time-based-hash.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
|
||||
import { uptime } from "os"
|
||||
import crypto from "crypto";
|
||||
|
||||
/**
|
||||
* Generiert einen zeitbasierten Hash der sich alle 15 Minuten ändert und an die Uptime des servers gekoppelt ist.
|
||||
* @param email - Die E-Mail-Adresse des Benutzers, die als Teil des Hashes verwendet wird.
|
||||
* @param time - Die Zeit in Millisekunden, die seit dem Start des Servers vergangen ist (Standard ist die Uptime des Servers).
|
||||
* @param length - Die Länge des zurückgegebenen Hashes (Standard ist 6 Zeichen).
|
||||
* @returns Ein zeitbasierter Hash, der sich alle 15 Minuten ändert und auf der E-Mail-Adresse basiert.
|
||||
*/
|
||||
export function createTimeBasedHash(email: string, time: number = uptime(), length: number = 6): string {
|
||||
const now = Date.now();
|
||||
const elapsed = now - time;
|
||||
const window = Math.floor(elapsed / (15 * 60 * 1000)); // 15 minute windows
|
||||
|
||||
const data = `${email}:${window}`;
|
||||
|
||||
// Use a cryptographic hash (you can also use HMAC with a secret if you want)
|
||||
const hash = crypto.createHash('sha256').update(data).digest();
|
||||
|
||||
// Convert part of the hash to an integer
|
||||
const int = hash.readUInt32BE(0); // take first 4 bytes
|
||||
|
||||
// Modulo to get 6 digits
|
||||
const pin = (int % 1000000).toString().padStart(6, '0');
|
||||
|
||||
return pin;
|
||||
}
|
||||
41
src/lib/server/mail/code.ts
Normal file
41
src/lib/server/mail/code.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { transport } from "#lib/mail.js";
|
||||
import {
|
||||
Benutzer,
|
||||
} from "#lib/client/prisma.js";
|
||||
import { createTimeBasedHash } from "#lib/auth/time-based-hash.js";
|
||||
|
||||
export async function sendVerificationCodeMail(
|
||||
user: Benutzer
|
||||
) {
|
||||
const code = createTimeBasedHash(user.email)
|
||||
|
||||
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>
|
||||
<p>Um Ihre Registrierung abzuschließen, geben Sie folgenden Bestätigungscode auf der Website ein, um Ihre E-Mail-Adresse zu bestätigen:<br><br>
|
||||
<b>${code}</b>
|
||||
</p>
|
||||
<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>`
|
||||
});
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
} from "#lib/client/prisma.js";
|
||||
import { encodeToken } from "#lib/auth/token.js";
|
||||
import { TokenType } from "#lib/auth/types.js";
|
||||
import { createTimeBasedHash } from "#lib/auth/time-based-hash.js";
|
||||
|
||||
export async function sendRegisterMail(
|
||||
user: Benutzer
|
||||
@@ -12,9 +13,11 @@ export async function sendRegisterMail(
|
||||
const verificationJwt = encodeToken({
|
||||
typ: TokenType.Verify,
|
||||
exp: Date.now() + (15 * 60 * 1000),
|
||||
uid: user.uid
|
||||
id: user.id
|
||||
})
|
||||
|
||||
const code = createTimeBasedHash(user.email)
|
||||
|
||||
await transport.sendMail({
|
||||
from: `"IBCornelsen" <info@online-energieausweis.org>`,
|
||||
to: user.email,
|
||||
@@ -22,10 +25,11 @@ export async function sendRegisterMail(
|
||||
bcc: "info@online-energieausweis.org",
|
||||
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>
|
||||
|
||||
Um Ihre Registrierung abzuschließen, klicken Sie bitte auf den folgenden Link, um Ihre E-Mail-Adresse zu bestätigen:<br><br>
|
||||
|
||||
<a href="${BASE_URI}/auth/verify?t=${verificationJwt}">E-Mail-Adresse bestätigen</a><br></p>
|
||||
<a href="${BASE_URI}/auth/verify?t=${verificationJwt}">E-Mail-Adresse bestätigen</a><br><br>
|
||||
Oder geben Sie folgenden Bestätigungscode auf der Website ein:<br>
|
||||
<b>${code}</b>
|
||||
</p>
|
||||
<p>
|
||||
Mit freundlichen Grüßen,
|
||||
<br>
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
}
|
||||
|
||||
try {
|
||||
const { uid } = await api.user.PUT.fetch({
|
||||
await api.user.PUT.fetch({
|
||||
email,
|
||||
passwort,
|
||||
vorname,
|
||||
@@ -42,7 +42,7 @@
|
||||
return
|
||||
}
|
||||
|
||||
window.location.href = "/auth/login";
|
||||
window.location.href = `/auth/code?email=${email}`;
|
||||
} catch (e) {
|
||||
errorHidden = false;
|
||||
}
|
||||
|
||||
89
src/modules/auth/CodeModule.svelte
Normal file
89
src/modules/auth/CodeModule.svelte
Normal file
@@ -0,0 +1,89 @@
|
||||
<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";
|
||||
|
||||
export let redirect: string | null = null;
|
||||
export let email: string;
|
||||
|
||||
function verify(e: SubmitEvent) {
|
||||
e.preventDefault();
|
||||
const code = numbers.join("");
|
||||
|
||||
if (code.length !== 6) {
|
||||
addNotification({
|
||||
message: "Bitte geben Sie einen gültigen Verifizierungscode ein.",
|
||||
dismissable: true,
|
||||
timeout: 3000,
|
||||
type: "error"
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
api.auth["verification-code"].POST.fetch({ code, email }).then(() => {
|
||||
if (redirect) {
|
||||
window.location.href = redirect;
|
||||
} else {
|
||||
window.location.href = "/";
|
||||
}
|
||||
}).catch(() => {
|
||||
errorHidden = false;
|
||||
});
|
||||
}
|
||||
|
||||
// TODO
|
||||
function codeErneutAnfordern() {
|
||||
api.auth["verification-code"].GET.fetch(null, {
|
||||
headers: {
|
||||
"Authorization": "Bearer"
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
let numbers = new Array(6).fill("");
|
||||
|
||||
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">Verifizierung</h1>
|
||||
<p>Wir haben ihnen einen Verifizierungscode per Email geschickt, bitte geben sie diesen ein um ihre Registrierung fertigzustellen.</p>
|
||||
<form on:submit={verify}>
|
||||
<div class="flex flex-row gap-4 w-full justify-center my-12">
|
||||
{#each { length: 6 } as _, i}
|
||||
<input
|
||||
type="text"
|
||||
class="input input-bordered text-4xl text-base-content font-medium w-12 text-center"
|
||||
bind:value={numbers[i]}
|
||||
maxlength="1"
|
||||
on:input={function(e) {
|
||||
if (i !== 5) {
|
||||
e.target.nextSibling.focus()
|
||||
}
|
||||
}}
|
||||
required
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<div class="flex justify-between">
|
||||
<button type="submit" class="button"
|
||||
>Abschicken</button
|
||||
>
|
||||
|
||||
|
||||
<!-- <button type="button" on:click={codeErneutAnfordern} class="button"
|
||||
>Code erneut anfordern</button
|
||||
> -->
|
||||
</div>
|
||||
{#if !errorHidden}
|
||||
<div class="flex flex-row gap-4 mt-8" in:fade out:fade={{delay: 400}}>
|
||||
<CrossCircled size={24} />
|
||||
<span class="font-semibold"> Da ist wohl etwas schiefgelaufen. Der eingegebene Verifizierungscode ist ungültig.</span>
|
||||
</div>
|
||||
{/if}
|
||||
</form>
|
||||
<NotificationWrapper></NotificationWrapper>
|
||||
</div>
|
||||
70
src/pages/api/auth/verification-code.ts
Normal file
70
src/pages/api/auth/verification-code.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
import { z } from "zod";
|
||||
import { prisma } from "#lib/server/prisma.js";
|
||||
import { APIError, defineApiRoute } from "astro-typesafe-api/server";
|
||||
import { sendVerificationCodeMail } from "#lib/server/mail/code.js";
|
||||
import { authorizationMiddleware } from "#lib/middleware/authorization.js";
|
||||
import { createTimeBasedHash } from "#lib/auth/time-based-hash.js";
|
||||
|
||||
|
||||
export const GET = defineApiRoute({
|
||||
meta: {
|
||||
description:
|
||||
"Fragt einen neuen Verifizierungscode per Mail an.",
|
||||
tags: ["Benutzer"],
|
||||
summary: "Verifizierungscode anfragen.",
|
||||
},
|
||||
middleware: authorizationMiddleware,
|
||||
output: z.void(),
|
||||
async fetch(input, ctx) {
|
||||
// Falls der Nutzer nicht existiert, wird eine Fehlermeldung zurückgegeben.
|
||||
const user = await prisma.benutzer.findUnique({
|
||||
where: {
|
||||
email: input.email.toLowerCase(),
|
||||
},
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
throw new APIError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "Benutzer konnte nicht gefunden werden.",
|
||||
});
|
||||
}
|
||||
|
||||
await sendVerificationCodeMail(user);
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
export const POST = defineApiRoute({
|
||||
meta: {
|
||||
description:
|
||||
"Versucht den Nutzer mithilfe des abgeschickten Codes zu verifizieren.",
|
||||
tags: ["Benutzer"],
|
||||
summary: "Verifizieren.",
|
||||
},
|
||||
input: z.object({
|
||||
code: z.string(),
|
||||
email: z.string().email().toLowerCase(),
|
||||
}),
|
||||
output: z.void(),
|
||||
async fetch({ code, email }, ctx) {
|
||||
const generatedCode = createTimeBasedHash(email);
|
||||
console.log(generatedCode, code);
|
||||
|
||||
if (code !== generatedCode) {
|
||||
throw new APIError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "Der eingegebene Verifizierungscode ist ungültig.",
|
||||
});
|
||||
}
|
||||
|
||||
await prisma.benutzer.update({
|
||||
where: {
|
||||
email: email.toLowerCase(),
|
||||
},
|
||||
data: {
|
||||
verified: true,
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
24
src/pages/auth/code.astro
Normal file
24
src/pages/auth/code.astro
Normal file
@@ -0,0 +1,24 @@
|
||||
---
|
||||
import CodeModule from "../../modules/auth/CodeModule.svelte";
|
||||
import MinimalLayout from "#layouts/MinimalLayout.astro";
|
||||
import { validateAccessTokenServer } from "#server/lib/validateAccessToken";
|
||||
|
||||
const valid = await validateAccessTokenServer(Astro)
|
||||
|
||||
if (valid) {
|
||||
return Astro.redirect("/dashboard")
|
||||
}
|
||||
|
||||
const redirect = Astro.url.searchParams.get("redirect");
|
||||
const email = Astro.url.searchParams.get("email");
|
||||
|
||||
if (!email) {
|
||||
return Astro.redirect("/");
|
||||
}
|
||||
|
||||
|
||||
---
|
||||
|
||||
<MinimalLayout title="Verifizierung - IBCornelsen">
|
||||
<CodeModule client:load {redirect} {email}></CodeModule>
|
||||
</MinimalLayout>
|
||||
@@ -3,7 +3,7 @@
|
||||
set -e
|
||||
|
||||
# Config
|
||||
CONTAINER_NAME="online-energieausweis-database-1"
|
||||
CONTAINER_NAME="database"
|
||||
DB_USER="main"
|
||||
DB_NAME="main"
|
||||
TIMESTAMP=$(date +"%Y-%m-%d_%H-%M-%S")
|
||||
@@ -58,7 +58,7 @@ BEGIN
|
||||
FROM pg_tables
|
||||
WHERE schemaname = 'public'
|
||||
LOOP
|
||||
sql := sql || FORMAT('TRUNCATE TABLE public.%I CASCADE;', r.tablename);
|
||||
sql := sql || FORMAT('DROP TABLE public.%I CASCADE;', r.tablename);
|
||||
END LOOP;
|
||||
|
||||
-- Drop all sequences
|
||||
|
||||
Reference in New Issue
Block a user