diff --git a/.env.example b/.env.example index 8797b73..a3aa8f6 100644 --- a/.env.example +++ b/.env.example @@ -1,2 +1,3 @@ DIRECTUS_URL= +DIRECTUS_URL_GRAPHQL= DIRECTUS_TOKEN= \ No newline at end of file diff --git a/astro.config.mjs b/astro.config.mjs index 37039a2..b826835 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -12,12 +12,21 @@ export default defineConfig({ site: "https://yarkov.tech", compressHTML: true, output: "server", + server: { + port: 9966, + allowedHosts: ["showcase.yarkov.tech"], + }, adapter: node({ mode: "standalone", }), env: { schema: { DIRECTUS_URL: envField.string({ context: "server", access: "secret", default: "" }), + DIRECTUS_URL_GRAPHQL: envField.string({ + context: "server", + access: "secret", + default: "http://directus:8055/graphql", + }), DIRECTUS_TOKEN: envField.string({ context: "server", access: "secret", default: "" }), }, }, diff --git a/package.json b/package.json index d20b1da..c457a49 100644 --- a/package.json +++ b/package.json @@ -11,11 +11,12 @@ "license": "MIT", "type": "module", "scripts": { - "dev": "astro dev", + "dev": "astro dev --host", "build": "astro check && astro build", "start": "node --env-file=.env ./dist/server/entry.mjs", "preview": "astro preview", "astro": "astro", + "telemetry-disable": "astro telemetry disable", "codegen": "graphql-codegen --require dotenv/config --config codegen.ts --watch" }, "dependencies": { diff --git a/src/actions/index.ts b/src/actions/index.ts new file mode 100644 index 0000000..ddb7982 --- /dev/null +++ b/src/actions/index.ts @@ -0,0 +1,41 @@ +import { ActionError, defineAction } from "astro:actions"; +import { z } from "astro/zod"; +import { createAppeal, getLatestAppealsByClientIp } from "../queries"; + +const CREATE_APPEAL_LIMIT_HRS = 1; +const APPEALS_LAST_HRS_LIMIT = 2; + +export const server = { + appeal: defineAction({ + accept: "form", + input: z.object({ + name: z.string(), + email: z.string().email(), + subject: z.string(), + message: z.string(), + }), + handler: async (data, ctx) => { + const currentDate = new Date(); + const appealsLastHrs = (await getLatestAppealsByClientIp(ctx.clientAddress, 5)).filter((a) => { + const hourDiff = (currentDate.getTime() - new Date(a.date_created).getTime()) / (1000 * 60 * 60); + return hourDiff <= CREATE_APPEAL_LIMIT_HRS; + }); + + if (appealsLastHrs.length >= APPEALS_LAST_HRS_LIMIT) { + throw new ActionError({ + code: "TOO_MANY_REQUESTS", + message: "Appeal create limit exceeded.", + }); + } + + const appeal = await createAppeal({ ...data, client_ip: ctx.clientAddress }); + + if (appeal) return true; + + throw new ActionError({ + code: "INTERNAL_SERVER_ERROR", + message: "Unable to create appeal.", + }); + }, + }), +}; diff --git a/src/components/ContactForm.astro b/src/components/ContactForm.astro index d1a2368..f7641a4 100644 --- a/src/components/ContactForm.astro +++ b/src/components/ContactForm.astro @@ -2,34 +2,45 @@ import Button from "@components/Button.astro"; import UIString from "./UIString.astro"; import { useTranslation } from "@i18n/utils"; +import { actions } from "astro:actions"; const t = useTranslation(Astro); +const formResult = Astro.getActionResult(actions.appeal); --- -