Fixed contact form on main page

Sends data to mail
Secrets from self-hosted infisical
This commit is contained in:
Cyril Šebek 2024-06-13 13:06:41 +02:00
parent 687e8ee686
commit c3e8684127
Signed by: blboun3
SSH Key Fingerprint: SHA256:n9dMtOPzgsD+CCerUJslEnU2dzVanbaIv0XDQVRVjeg
5 changed files with 255 additions and 10 deletions

View File

@ -9,7 +9,7 @@ import react from "@astrojs/react";
// https://astro.build/config // https://astro.build/config
export default defineConfig({ export default defineConfig({
output: "hybrid", output: "server",
adapter: node({ adapter: node({
mode: "standalone", mode: "standalone",
}), }),

View File

@ -17,11 +17,14 @@
"@astrojs/sitemap": "^3.1.1", "@astrojs/sitemap": "^3.1.1",
"@astrojs/tailwind": "^5.1.0", "@astrojs/tailwind": "^5.1.0",
"@hookform/resolvers": "^3.6.0", "@hookform/resolvers": "^3.6.0",
"@infisical/sdk": "^2.2.3",
"@paralleldrive/cuid2": "^2.2.2",
"@radix-ui/react-dialog": "^1.0.5", "@radix-ui/react-dialog": "^1.0.5",
"@radix-ui/react-dropdown-menu": "^2.0.6", "@radix-ui/react-dropdown-menu": "^2.0.6",
"@radix-ui/react-label": "^2.0.2", "@radix-ui/react-label": "^2.0.2",
"@radix-ui/react-slot": "^1.0.2", "@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-tabs": "^1.0.4", "@radix-ui/react-tabs": "^1.0.4",
"@types/nodemailer": "^6.4.15",
"@types/react": "^18.2.74", "@types/react": "^18.2.74",
"@types/react-dom": "^18.2.24", "@types/react-dom": "^18.2.24",
"astro": "^4.5.6", "astro": "^4.5.6",
@ -30,6 +33,7 @@
"cmdk": "^1.0.0", "cmdk": "^1.0.0",
"embla-carousel-react": "^8.0.2", "embla-carousel-react": "^8.0.2",
"lucide-react": "^0.365.0", "lucide-react": "^0.365.0",
"nodemailer": "^6.9.13",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-hook-form": "^7.51.5", "react-hook-form": "^7.51.5",

View File

@ -1,5 +1,9 @@
lockfileVersion: '6.0' lockfileVersion: '6.0'
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
dependencies: dependencies:
'@astrojs/mdx': '@astrojs/mdx':
specifier: ^2.2.1 specifier: ^2.2.1
@ -22,6 +26,12 @@ dependencies:
'@hookform/resolvers': '@hookform/resolvers':
specifier: ^3.6.0 specifier: ^3.6.0
version: 3.6.0(react-hook-form@7.51.5) version: 3.6.0(react-hook-form@7.51.5)
'@infisical/sdk':
specifier: ^2.2.3
version: 2.2.3
'@paralleldrive/cuid2':
specifier: ^2.2.2
version: 2.2.2
'@radix-ui/react-dialog': '@radix-ui/react-dialog':
specifier: ^1.0.5 specifier: ^1.0.5
version: 1.0.5(@types/react-dom@18.2.24)(@types/react@18.2.74)(react-dom@18.2.0)(react@18.2.0) version: 1.0.5(@types/react-dom@18.2.24)(@types/react@18.2.74)(react-dom@18.2.0)(react@18.2.0)
@ -37,6 +47,9 @@ dependencies:
'@radix-ui/react-tabs': '@radix-ui/react-tabs':
specifier: ^1.0.4 specifier: ^1.0.4
version: 1.0.4(@types/react-dom@18.2.24)(@types/react@18.2.74)(react-dom@18.2.0)(react@18.2.0) version: 1.0.4(@types/react-dom@18.2.24)(@types/react@18.2.74)(react-dom@18.2.0)(react@18.2.0)
'@types/nodemailer':
specifier: ^6.4.15
version: 6.4.15
'@types/react': '@types/react':
specifier: ^18.2.74 specifier: ^18.2.74
version: 18.2.74 version: 18.2.74
@ -61,6 +74,9 @@ dependencies:
lucide-react: lucide-react:
specifier: ^0.365.0 specifier: ^0.365.0
version: 0.365.0(react@18.2.0) version: 0.365.0(react@18.2.0)
nodemailer:
specifier: ^6.9.13
version: 6.9.13
react: react:
specifier: ^18.2.0 specifier: ^18.2.0
version: 18.2.0 version: 18.2.0
@ -776,6 +792,132 @@ packages:
react-hook-form: 7.51.5(react@18.2.0) react-hook-form: 7.51.5(react@18.2.0)
dev: false dev: false
/@infisical/sdk-android-arm-eabi@2.2.3:
resolution: {integrity: sha512-eDkrjgQ1CD1UaAIbRLv4clMy4iwiprH8uMnC31d8ABdlNyvYPiC3zgy+XPuxVefnCVT2NrqM5UsPKDAIdMeOag==}
engines: {node: '>= 10'}
cpu: [arm]
os: [android]
requiresBuild: true
dev: false
optional: true
/@infisical/sdk-android-arm64@2.2.3:
resolution: {integrity: sha512-9EAKO+MgcHyODDobGi3iwG5n207rH2PQ0XPaC6VSZp90oN5unSTmNWK7qbElbTSf+MwrsyOJT87QTXicatzCBw==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [android]
requiresBuild: true
dev: false
optional: true
/@infisical/sdk-darwin-arm64@2.2.3:
resolution: {integrity: sha512-58qQA3hI9SUcRJQghiNQjt92HiWIRX1NVkWZsNN9HX6j5YaHC9hhUmJ7RopI768Bsh5jxwmyeEn9CXzFxIUA8Q==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [darwin]
requiresBuild: true
dev: false
optional: true
/@infisical/sdk-darwin-x64@2.2.3:
resolution: {integrity: sha512-AyIBGJH/EcsJrpMDgQPL4lmDAqGywn2MKB0dsOnBBwJHLdxvzra4IA9ZQHockI7kSUKu5kSA5YTk6seZ5f/BIg==}
engines: {node: '>= 10'}
cpu: [x64]
os: [darwin]
requiresBuild: true
dev: false
optional: true
/@infisical/sdk-linux-arm-gnueabihf@2.2.3:
resolution: {integrity: sha512-sFGhXqckuVUPmqnFuZbX/NFlxpdAu/IHzGQKbPYsGjPnqxC+ZkCBvuyj1IpVIElL/j09pLVbMMWZv1vf+aaHmQ==}
engines: {node: '>= 10'}
cpu: [arm]
os: [linux]
requiresBuild: true
dev: false
optional: true
/@infisical/sdk-linux-arm64-gnu@2.2.3:
resolution: {integrity: sha512-ppXvRBVINN7DSMLIaM3syUkYXvDVsrO0OzVgQ9ti9yYcBPOJlzrIU2H/yiXXv/m8U58jCja275i5K5y1VdjuPw==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
requiresBuild: true
dev: false
optional: true
/@infisical/sdk-linux-arm64-musl@2.2.3:
resolution: {integrity: sha512-/JouatsZb7KAbks682tHIRH5wMpGOoVCTBJjXNcnhV33bOxlyJ9G/UimgCYBpT9WuVhkRJKNXEQQdh7rsCsShA==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
requiresBuild: true
dev: false
optional: true
/@infisical/sdk-linux-x64-gnu@2.2.3:
resolution: {integrity: sha512-YH3DrvDfjmArKLrvhV+2mqoE5mY3l9cwXivqtNlcPftH5TW1MQvVq9lJEb8wIEkCNNiFYyJ2Xf8tJQDkervlYQ==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
requiresBuild: true
dev: false
optional: true
/@infisical/sdk-linux-x64-musl@2.2.3:
resolution: {integrity: sha512-h0+14vwyigRi8aR/BfJ8LFZOJJOUa1p7fsyVUVWuTLsaDyo5l/HW4q+C3wYX2N9Jb3NyUev4lvu19WCs8W12iQ==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
requiresBuild: true
dev: false
optional: true
/@infisical/sdk-win32-arm64-msvc@2.2.3:
resolution: {integrity: sha512-o07IXCl66/6WYDtIdPef7KeQX9aJsdmJJCMYu6K7ez9EV+nNJR7nmuhe6jdlK16kVdyfJB5lXuefCe+hWVx+HA==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [win32]
requiresBuild: true
dev: false
optional: true
/@infisical/sdk-win32-ia32-msvc@2.2.3:
resolution: {integrity: sha512-CXQK5N7YQ3RBVbKwx5HBGNM8LwV7/+hzN9py7C1Upv07XK0bmC6NvVrZVoYwSMDBIN3lCAy/cWASsNH+9Ve5uw==}
engines: {node: '>= 10'}
cpu: [ia32]
os: [win32]
requiresBuild: true
dev: false
optional: true
/@infisical/sdk-win32-x64-msvc@2.2.3:
resolution: {integrity: sha512-hBmKiKNQ3CK6I3Z1s+1Ix8kM2/ML5BiTRmM6EOP6iQBULMf1AmCoGzh6uO4l7gytcvixtC8Jp21+37rKjm4MLA==}
engines: {node: '>= 10'}
cpu: [x64]
os: [win32]
requiresBuild: true
dev: false
optional: true
/@infisical/sdk@2.2.3:
resolution: {integrity: sha512-UvJjOD0b3O4obdtEYh+++Pv7Jfj5qb0QMzEZrADDLQJH0bDswRgLCaCiZtvzkV6WJPw8EKM5CFyBuZOgvDLESQ==}
engines: {node: '>= 10'}
optionalDependencies:
'@infisical/sdk-android-arm-eabi': 2.2.3
'@infisical/sdk-android-arm64': 2.2.3
'@infisical/sdk-darwin-arm64': 2.2.3
'@infisical/sdk-darwin-x64': 2.2.3
'@infisical/sdk-linux-arm-gnueabihf': 2.2.3
'@infisical/sdk-linux-arm64-gnu': 2.2.3
'@infisical/sdk-linux-arm64-musl': 2.2.3
'@infisical/sdk-linux-x64-gnu': 2.2.3
'@infisical/sdk-linux-x64-musl': 2.2.3
'@infisical/sdk-win32-arm64-msvc': 2.2.3
'@infisical/sdk-win32-ia32-msvc': 2.2.3
'@infisical/sdk-win32-x64-msvc': 2.2.3
dev: false
/@isaacs/cliui@8.0.2: /@isaacs/cliui@8.0.2:
resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -842,6 +984,11 @@ packages:
- supports-color - supports-color
dev: false dev: false
/@noble/hashes@1.4.0:
resolution: {integrity: sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==}
engines: {node: '>= 16'}
dev: false
/@nodelib/fs.scandir@2.1.5: /@nodelib/fs.scandir@2.1.5:
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
engines: {node: '>= 8'} engines: {node: '>= 8'}
@ -860,6 +1007,12 @@ packages:
'@nodelib/fs.scandir': 2.1.5 '@nodelib/fs.scandir': 2.1.5
fastq: 1.17.1 fastq: 1.17.1
/@paralleldrive/cuid2@2.2.2:
resolution: {integrity: sha512-ZOBkgDwEdoYVlSeRbYYXs0S9MejQofiVYoTbKzy/6GQa39/q5tQU2IX46+shYnUkpEl3wc+J6wRlar7r2EK2xA==}
dependencies:
'@noble/hashes': 1.4.0
dev: false
/@pkgjs/parseargs@0.11.0: /@pkgjs/parseargs@0.11.0:
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
engines: {node: '>=14'} engines: {node: '>=14'}
@ -1621,6 +1774,12 @@ packages:
resolution: {integrity: sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==} resolution: {integrity: sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==}
dev: false dev: false
/@types/nodemailer@6.4.15:
resolution: {integrity: sha512-0EBJxawVNjPkng1zm2vopRctuWVCxk34JcIlRuXSf54habUWdz1FB7wHDqOqvDa8Mtpt0Q3LTXQkAs2LNyK5jQ==}
dependencies:
'@types/node': 17.0.45
dev: false
/@types/prop-types@15.7.12: /@types/prop-types@15.7.12:
resolution: {integrity: sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==} resolution: {integrity: sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==}
dev: false dev: false
@ -3843,6 +4002,11 @@ packages:
resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==}
dev: false dev: false
/nodemailer@6.9.13:
resolution: {integrity: sha512-7o38Yogx6krdoBf3jCAqnIN4oSQFx+fMa0I7dK1D+me9kBxx12D+/33wSb+fhOCtIxvYJ+4x4IMEhmhCKfAiOA==}
engines: {node: '>=6.0.0'}
dev: false
/normalize-path@3.0.0: /normalize-path@3.0.0:
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
@ -5360,7 +5524,3 @@ packages:
/zwitch@2.0.4: /zwitch@2.0.4:
resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}
dev: false dev: false
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false

View File

@ -46,10 +46,21 @@ export function ContactForm({ currentLocale }) {
}, },
}); });
function onSubmit(values: z.infer<typeof formSchema>) { async function onSubmit(values: z.infer<typeof formSchema>) {
// Do something with the form values. const formData = new FormData();
// ✅ This will be type-safe and validated.
console.log(values); for (const value of Object.keys(values)) {
formData.append(value, values[value])
}
console.log(formData)
const response = await fetch("/api", {
method: "POST",
body: formData
});
const data = await response.json();
console.log(data.message)
} }
return ( return (
@ -64,7 +75,7 @@ export function ContactForm({ currentLocale }) {
</DialogTrigger> </DialogTrigger>
<DialogContent className="sm:max-w-[425px]"> <DialogContent className="sm:max-w-[425px]">
<DialogHeader> <DialogHeader>
<DialogTitle><h1 className="text-foreground">{t("contact").title}</h1></DialogTitle> <DialogTitle><p className="text-foreground">{t("contact").title}</p></DialogTitle>
<DialogDescription>{t("contact").description}</DialogDescription> <DialogDescription>{t("contact").description}</DialogDescription>
</DialogHeader> </DialogHeader>

70
src/pages/api.js Normal file
View File

@ -0,0 +1,70 @@
import nodemailer from "nodemailer";
import { createId } from '@paralleldrive/cuid2';
import { InfisicalClient } from "@infisical/sdk";
const client = new InfisicalClient({
siteUrl: import.meta.env.INFISICAL_URL,
auth: {
universalAuth: {
clientId: import.meta.env.INFISICAL_CLIENTID,
clientSecret: import.meta.env.INFISICAL_SECRET
}
}
});
export const POST = async ({ request }) => {
try {
var secrets = await client.listSecrets({
environment: "dev",
projectId: import.meta.env.INFISICAL_PROJECTID,
path: "/email",
includeImports: false
});
secrets = Object.fromEntries(secrets.map(item => [item.secretKey, item.secretValue]));
const thisCuid = createId();
const data = await request.formData();
var message = {
from: `${data.get("username")} <${secrets["EMAIL"]}>`,
to: `Site Admin <${secrets["RECEIVER"]}>`,
subject: `${secrets["EMAIL_HEAD"]} | ${thisCuid}`,
text: `---\n${data.get("username")}\n${data.get("email")}\n---\n${data.get("message")}`,
html: `<p>---<br/>${data.get("username")}<br/>${data.get("email")}<br/>---<br/>${data.get("message")}</p>`,
envelope: {
from: `${data.get("username")} <${secrets["EMAIL"]}>`,
to: `Site Admin <${secrets["RECEIVER"]}>`,
}
}
var transport = nodemailer.createTransport({
host: secrets["SMTP_SERVER"],
port: secrets["SMTP_PORT"],
secure: secrets["SMTP_SECURITY"],
auth: {
user: secrets["EMAIL"],
pass: secrets["PASSWORD"]
}
});
var mailResponse = await transport.sendMail(message);
console.log(mailResponse)
return new Response(JSON.stringify({ message: "Success!" }), { status: 200 });
} catch (error) {
console.error("Error parsing form data:", error);
return new Response(JSON.stringify({ message: "Failed to parse form data" }), { status: 400 });
}
};
/*
npm install --save @types/nodemailer
npm install --save @types/nodemailer
npm install --save @types/nodemailer
npm install --save @types/nodemailer
další řádka
volná řádka
další řádka
asdasdasdasdasdad dlouhá řádka asdasdasda žádný enter tady asdsadadsas
*/