diff --git a/.env b/.env index 3efbb70..dbedf90 100644 --- a/.env +++ b/.env @@ -4,4 +4,18 @@ GOOGLE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAAS GOOGLE_SHEET_ID=12X6qeU0W4ZDw00vS4OreyC4nFjKBeBYo7rq3wyRsrNQ # Archivo de variables de entorno para la configuración de envío de emails -EMAIL_API_KEY=9UShpS8oh5Iun92TJSfevElI3Lp99TCv \ No newline at end of file +EMAIL_API_KEY=9UShpS8oh5Iun92TJSfevElI3Lp99TCv + +# This was inserted by `prisma init`: +# Environment variables declared in this file are NOT automatically loaded by Prisma. +# Please add `import "dotenv/config";` to your `prisma.config.ts` file, or use the Prisma CLI with Bun +# to load environment variables from .env files: https://pris.ly/prisma-config-env-vars. + +# 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 + +# The following `prisma+postgres` URL is similar to the URL produced by running a local Prisma Postgres +# server with the `prisma dev` CLI command, when not choosing any non-default ports or settings. The API key, unlike the +# one found in a remote Prisma Postgres URL, does not contain any sensitive information. + +DATABASE_URL="file:./dev.db" \ No newline at end of file diff --git a/.gitignore b/.gitignore index 371b357..d12aeaa 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,5 @@ pnpm-debug.log* # jetbrains setting folder .idea/ + +/generated/prisma diff --git a/package-lock.json b/package-lock.json index a9185ec..51bed18 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "@fontsource/poppins": "^5.2.7", "@iconify-json/ph": "^1.2.2", "@iconify/vue": "^5.0.0", + "@prisma/client": "^6.19.2", "@tailwindcss/vite": "^4.1.18", "@unpic/astro": "^1.0.2", "astro": "^5.17.1", @@ -26,6 +27,7 @@ "astro-icon": "^1.1.5", "dayjs": "^1.11.19", "googleapis": "^171.4.0", + "prisma": "^6.19.2", "swiper": "^12.1.0", "tailwindcss": "^4.1.18", "vue": "^3.5.28" @@ -2196,6 +2198,153 @@ "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", "license": "MIT" }, + "node_modules/@prisma/client": { + "version": "6.19.2", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.19.2.tgz", + "integrity": "sha512-gR2EMvfK/aTxsuooaDA32D8v+us/8AAet+C3J1cc04SW35FPdZYgLF+iN4NDLUgAaUGTKdAB0CYenu1TAgGdMg==", + "hasInstallScript": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "peerDependencies": { + "prisma": "*", + "typescript": ">=5.1.0" + }, + "peerDependenciesMeta": { + "prisma": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/@prisma/config": { + "version": "6.19.2", + "resolved": "https://registry.npmjs.org/@prisma/config/-/config-6.19.2.tgz", + "integrity": "sha512-kadBGDl+aUswv/zZMk9Mx0C8UZs1kjao8H9/JpI4Wh4SHZaM7zkTwiKn/iFLfRg+XtOAo/Z/c6pAYhijKl0nzQ==", + "license": "Apache-2.0", + "dependencies": { + "c12": "3.1.0", + "deepmerge-ts": "7.1.5", + "effect": "3.18.4", + "empathic": "2.0.0" + } + }, + "node_modules/@prisma/config/node_modules/c12": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/c12/-/c12-3.1.0.tgz", + "integrity": "sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw==", + "license": "MIT", + "dependencies": { + "chokidar": "^4.0.3", + "confbox": "^0.2.2", + "defu": "^6.1.4", + "dotenv": "^16.6.1", + "exsolve": "^1.0.7", + "giget": "^2.0.0", + "jiti": "^2.4.2", + "ohash": "^2.0.11", + "pathe": "^2.0.3", + "perfect-debounce": "^1.0.0", + "pkg-types": "^2.2.0", + "rc9": "^2.1.2" + }, + "peerDependencies": { + "magicast": "^0.3.5" + }, + "peerDependenciesMeta": { + "magicast": { + "optional": true + } + } + }, + "node_modules/@prisma/config/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@prisma/config/node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/@prisma/config/node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@prisma/debug": { + "version": "6.19.2", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.19.2.tgz", + "integrity": "sha512-lFnEZsLdFLmEVCVNdskLDCL8Uup41GDfU0LUfquw+ercJC8ODTuL0WNKgOKmYxCJVvFwf0OuZBzW99DuWmoH2A==", + "license": "Apache-2.0" + }, + "node_modules/@prisma/engines": { + "version": "6.19.2", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.19.2.tgz", + "integrity": "sha512-TTkJ8r+uk/uqczX40wb+ODG0E0icVsMgwCTyTHXehaEfb0uo80M9g1aW1tEJrxmFHeOZFXdI2sTA1j1AgcHi4A==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "6.19.2", + "@prisma/engines-version": "7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7", + "@prisma/fetch-engine": "6.19.2", + "@prisma/get-platform": "6.19.2" + } + }, + "node_modules/@prisma/engines-version": { + "version": "7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7.tgz", + "integrity": "sha512-03bgb1VD5gvuumNf+7fVGBzfpJPjmqV423l/WxsWk2cNQ42JD0/SsFBPhN6z8iAvdHs07/7ei77SKu7aZfq8bA==", + "license": "Apache-2.0" + }, + "node_modules/@prisma/fetch-engine": { + "version": "6.19.2", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.19.2.tgz", + "integrity": "sha512-h4Ff4Pho+SR1S8XerMCC12X//oY2bG3Iug/fUnudfcXEUnIeRiBdXHFdGlGOgQ3HqKgosTEhkZMvGM9tWtYC+Q==", + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "6.19.2", + "@prisma/engines-version": "7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7", + "@prisma/get-platform": "6.19.2" + } + }, + "node_modules/@prisma/get-platform": { + "version": "6.19.2", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.19.2.tgz", + "integrity": "sha512-PGLr06JUSTqIvztJtAzIxOwtWKtJm5WwOG6xpsgD37Rc84FpfUBGLKz65YpJBGtkRQGXTYEFie7pYALocC3MtA==", + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "6.19.2" + } + }, "node_modules/@rolldown/pluginutils": { "version": "1.0.0-rc.4", "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.4.tgz", @@ -2640,6 +2789,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "license": "MIT" + }, "node_modules/@tailwindcss/node": { "version": "4.1.18", "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.18.tgz", @@ -4008,6 +4163,15 @@ "node": ">=8" } }, + "node_modules/citty": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", + "integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==", + "license": "MIT", + "dependencies": { + "consola": "^3.2.3" + } + }, "node_modules/cli-boxes": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz", @@ -4078,6 +4242,15 @@ "integrity": "sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==", "license": "MIT" }, + "node_modules/consola": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "license": "MIT", + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -4304,6 +4477,15 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/deepmerge-ts": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-7.1.5.tgz", + "integrity": "sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/default-browser": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.5.0.tgz", @@ -4569,6 +4751,16 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", "license": "MIT" }, + "node_modules/effect": { + "version": "3.18.4", + "resolved": "https://registry.npmjs.org/effect/-/effect-3.18.4.tgz", + "integrity": "sha512-b1LXQJLe9D11wfnOKAk3PKxuqYshQ0Heez+y5pnkd3jLj1yx9QhM72zZ9uUrOQyNvrs2GZZd/3maL0ZV18YuDA==", + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "fast-check": "^3.23.1" + } + }, "node_modules/electron-to-chromium": { "version": "1.5.286", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.286.tgz", @@ -4581,6 +4773,15 @@ "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", "license": "MIT" }, + "node_modules/empathic": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/empathic/-/empathic-2.0.0.tgz", + "integrity": "sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==", + "license": "MIT", + "engines": { + "node": ">=14" + } + }, "node_modules/encodeurl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", @@ -4847,6 +5048,28 @@ "@types/yauzl": "^2.9.1" } }, + "node_modules/fast-check": { + "version": "3.23.2", + "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-3.23.2.tgz", + "integrity": "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT", + "dependencies": { + "pure-rand": "^6.1.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/fd-slicer": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", @@ -5117,6 +5340,23 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/giget": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/giget/-/giget-2.0.0.tgz", + "integrity": "sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==", + "license": "MIT", + "dependencies": { + "citty": "^0.1.6", + "consola": "^3.4.0", + "defu": "^6.1.4", + "node-fetch-native": "^1.6.6", + "nypm": "^0.6.0", + "pathe": "^2.0.3" + }, + "bin": { + "giget": "dist/cli.mjs" + } + }, "node_modules/github-slugger": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-2.0.0.tgz", @@ -7239,6 +7479,29 @@ "url": "https://github.com/fb55/nth-check?sponsor=1" } }, + "node_modules/nypm": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.5.tgz", + "integrity": "sha512-K6AJy1GMVyfyMXRVB88700BJqNUkByijGJM8kEHpLdcAt+vSQAVfkWWHYzuRXHSY6xA2sNc5RjTj0p9rE2izVQ==", + "license": "MIT", + "dependencies": { + "citty": "^0.2.0", + "pathe": "^2.0.3", + "tinyexec": "^1.0.2" + }, + "bin": { + "nypm": "dist/cli.mjs" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/nypm/node_modules/citty": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/citty/-/citty-0.2.1.tgz", + "integrity": "sha512-kEV95lFBhQgtogAPlQfJJ0WGVSokvLr/UEoFPiKKOXF7pl98HfUVUD0ejsuTCld/9xH9vogSywZ5KqHzXrZpqg==", + "license": "MIT" + }, "node_modules/object-inspect": { "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", @@ -7620,6 +7883,31 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/prisma": { + "version": "6.19.2", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-6.19.2.tgz", + "integrity": "sha512-XTKeKxtQElcq3U9/jHyxSPgiRgeYDKxWTPOf6NkXA0dNj5j40MfEsZkMbyNpwDWCUv7YBFUl7I2VK/6ALbmhEg==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/config": "6.19.2", + "@prisma/engines": "6.19.2" + }, + "bin": { + "prisma": "build/index.js" + }, + "engines": { + "node": ">=18.18" + }, + "peerDependencies": { + "typescript": ">=5.1.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/prismjs": { "version": "1.30.0", "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", @@ -7662,6 +7950,22 @@ "once": "^1.3.1" } }, + "node_modules/pure-rand": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" + }, "node_modules/qs": { "version": "6.14.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", @@ -7708,6 +8012,16 @@ "node": ">= 0.6" } }, + "node_modules/rc9": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/rc9/-/rc9-2.1.2.tgz", + "integrity": "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==", + "license": "MIT", + "dependencies": { + "defu": "^6.1.4", + "destr": "^2.0.3" + } + }, "node_modules/readdirp": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz", diff --git a/package.json b/package.json index 371f563..839ffab 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "@fontsource/poppins": "^5.2.7", "@iconify-json/ph": "^1.2.2", "@iconify/vue": "^5.0.0", + "@prisma/client": "^6.19.2", "@tailwindcss/vite": "^4.1.18", "@unpic/astro": "^1.0.2", "astro": "^5.17.1", @@ -28,6 +29,7 @@ "astro-icon": "^1.1.5", "dayjs": "^1.11.19", "googleapis": "^171.4.0", + "prisma": "^6.19.2", "swiper": "^12.1.0", "tailwindcss": "^4.1.18", "vue": "^3.5.28" diff --git a/prisma.config.ts b/prisma.config.ts new file mode 100644 index 0000000..8ded1a5 --- /dev/null +++ b/prisma.config.ts @@ -0,0 +1,16 @@ + +// This file was generated by Prisma and assumes you have installed the following: +// npm install --save-dev prisma dotenv +import "dotenv/config"; +import { defineConfig, env } from "prisma/config"; + +export default defineConfig({ + schema: "prisma/schema.prisma", + migrations: { + path: "prisma/migrations", + }, + engine: "classic", + datasource: { + url: env("DATABASE_URL"), + }, +}); diff --git a/prisma/dev.db b/prisma/dev.db new file mode 100644 index 0000000..9fae211 Binary files /dev/null and b/prisma/dev.db differ diff --git a/prisma/migrations/20260220004001_init/migration.sql b/prisma/migrations/20260220004001_init/migration.sql new file mode 100644 index 0000000..33c8ec8 --- /dev/null +++ b/prisma/migrations/20260220004001_init/migration.sql @@ -0,0 +1,8 @@ +-- CreateTable +CREATE TABLE "Contact" ( + "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + "nombre" TEXT NOT NULL, + "email" TEXT NOT NULL, + "mensaje" TEXT NOT NULL, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP +); diff --git a/prisma/migrations/20260220004224_add_contact/migration.sql b/prisma/migrations/20260220004224_add_contact/migration.sql new file mode 100644 index 0000000..0afc2df --- /dev/null +++ b/prisma/migrations/20260220004224_add_contact/migration.sql @@ -0,0 +1,2 @@ +-- CreateIndex +CREATE INDEX "Contact_email_createdAt_idx" ON "Contact"("email", "createdAt"); diff --git a/prisma/migrations/20260220005204_make_email_unique/migration.sql b/prisma/migrations/20260220005204_make_email_unique/migration.sql new file mode 100644 index 0000000..196d18f --- /dev/null +++ b/prisma/migrations/20260220005204_make_email_unique/migration.sql @@ -0,0 +1,24 @@ +/* + Warnings: + + - Added the required column `updatedAt` to the `Contact` table without a default value. This is not possible if the table is not empty. + +*/ +-- RedefineTables +PRAGMA defer_foreign_keys=ON; +PRAGMA foreign_keys=OFF; +CREATE TABLE "new_Contact" ( + "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + "nombre" TEXT NOT NULL, + "email" TEXT NOT NULL, + "mensaje" TEXT NOT NULL, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL +); +INSERT INTO "new_Contact" ("createdAt", "email", "id", "mensaje", "nombre") SELECT "createdAt", "email", "id", "mensaje", "nombre" FROM "Contact"; +DROP TABLE "Contact"; +ALTER TABLE "new_Contact" RENAME TO "Contact"; +CREATE UNIQUE INDEX "Contact_email_key" ON "Contact"("email"); +CREATE INDEX "Contact_email_createdAt_idx" ON "Contact"("email", "createdAt"); +PRAGMA foreign_keys=ON; +PRAGMA defer_foreign_keys=OFF; diff --git a/prisma/migrations/migration_lock.toml b/prisma/migrations/migration_lock.toml new file mode 100644 index 0000000..2a5a444 --- /dev/null +++ b/prisma/migrations/migration_lock.toml @@ -0,0 +1,3 @@ +# Please do not edit this file manually +# It should be added in your version-control system (e.g., Git) +provider = "sqlite" diff --git a/prisma/schema.prisma b/prisma/schema.prisma new file mode 100644 index 0000000..2aa9ec2 --- /dev/null +++ b/prisma/schema.prisma @@ -0,0 +1,19 @@ +generator client { + provider = "prisma-client-js" +} + +datasource db { + provider = "sqlite" + url = env("DATABASE_URL") +} + +model Contact { + id Int @id @default(autoincrement()) + nombre String + email String @unique + mensaje String + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@index([email, createdAt]) +} \ No newline at end of file diff --git a/src/pages/api/emailInfo/send.ts b/src/pages/api/emailInfo/send.ts index c7cc81d..4ba6f16 100644 --- a/src/pages/api/emailInfo/send.ts +++ b/src/pages/api/emailInfo/send.ts @@ -1,49 +1,70 @@ import type { APIRoute } from "astro"; +import { prisma } from "../lib/prisma"; + export const prerender = false; export const POST: APIRoute = async ({ request }) => { try { - // 🔹 Leer datos del formulario - const formData = await request.formData(); - - const nombre = formData.get("nombre")?.toString().trim(); - const email = formData.get("email")?.toString().trim(); + const formData = await request.formData(); + + const nombre = formData.get("nombre")?.toString().trim(); + const email = formData.get("email")?.toString().trim().toLowerCase(); const mensaje = formData.get("mensaje")?.toString().trim(); - // 🔹 Validación básica if (!nombre || !email || !mensaje) { return new Response( JSON.stringify({ error: "Datos incompletos" }), - { status: 302 } + { status: 400 } + ); + } + + const now = new Date(); + const startOfDay = new Date( + now.getFullYear(), + now.getMonth(), + now.getDate() ); -} -const emailApiKey = import.meta.env.EMAIL_API_KEY; + const endOfDay = new Date( + now.getFullYear(), + now.getMonth(), + now.getDate() + 1 + ); - // 🔹 Enviar a tu backend real - const response = await fetch("http://155.138.215.11:3050/email/send", { - method: "POST", - headers: { - "Content-Type": "application/json", - "x-api-key": emailApiKey!, - }, - body: JSON.stringify({ - type: "CENTRO_DEL_REINO_PAZ_Y_JUSTICIA_INFO", + const existing = await prisma.contact.findUnique({ + where: { email }, + }); + + if (existing) { + // Si ya envió hoy → actualizar mensaje + if (existing.updatedAt >= startOfDay && existing.updatedAt < endOfDay) { + await prisma.contact.update({ + where: { email }, + data: { + nombre, + mensaje, + }, + }); + } else { + // Si es otro día → actualizar todo + await prisma.contact.update({ + where: { email }, + data: { + nombre, + mensaje, + }, + }); + } + } else { + await prisma.contact.create({ data: { nombre, email, mensaje, }, - }), - }); - if (!response.ok) { - return new Response( - JSON.stringify({ error: "Error en servicio de correo" }), - { status: 302 } - ); + }); } - // 🔹 Redirección elegante después de enviar return new Response(null, { status: 302, headers: { @@ -52,11 +73,10 @@ const emailApiKey = import.meta.env.EMAIL_API_KEY; }); } catch (error) { + console.error(error); return new Response(null, { status: 302, - headers: { - Location: "/", - }, + headers: { Location: "/" }, }); } -}; +}; \ No newline at end of file diff --git a/src/pages/api/lib/prisma.ts b/src/pages/api/lib/prisma.ts new file mode 100644 index 0000000..fa60f1f --- /dev/null +++ b/src/pages/api/lib/prisma.ts @@ -0,0 +1,10 @@ +import { PrismaClient } from "@prisma/client"; + +const globalForPrisma = globalThis as unknown as { + prisma: PrismaClient | undefined; +}; + +export const prisma = + globalForPrisma.prisma ?? new PrismaClient(); + +if (import.meta.env.DEV) globalForPrisma.prisma = prisma; \ No newline at end of file