20 Commits

Author SHA1 Message Date
48c7d522c1 Refactor code 2026-02-03 13:26:47 +01:00
531bb8750e Refactor code 2026-02-03 13:12:31 +01:00
8572e22c5d Refactor code 2026-02-03 12:17:47 +01:00
ea5eb6fa59 Refactoring code 2026-02-03 10:13:41 +01:00
0c72a756c5 Changed some stuf on artworks table 2026-02-02 19:19:52 +01:00
c915df904d Add tags to commssion types and custom types. Add button for example images to cards 2026-02-02 17:00:03 +01:00
93a327c634 Add migration 2026-02-02 14:52:27 +01:00
3b8b3efa4a Merge dev into main 2026-02-02 14:44:48 +01:00
dd7cd6a944 Fix type error 2026-02-02 13:56:14 +01:00
ed81662ae5 Moving the arttags table to tags table part 2 2026-02-02 13:48:49 +01:00
7605ccb0aa Moving the arttags table to tags table part 1 2026-02-02 13:05:52 +01:00
6680ccc023 Fix dev Dockerfile 2026-02-02 00:52:39 +01:00
1b6763cc3a Fix footer copyright year 2026-02-01 22:50:18 +01:00
c2d2e6c5a7 Add versioning to footer 2026-02-01 22:38:08 +01:00
c42401383b Update packages 2026-02-01 21:59:19 +01:00
7e9c618267 Bump version to nextjs 16.1.6 2026-02-01 21:57:19 +01:00
752c93e460 Merge branch 'main' into dev 2026-02-01 19:35:07 +01:00
e83272d8e8 merged two files 2026-02-01 19:34:49 +01:00
90deab01b1 Fix artwork edit page 2026-02-01 19:32:09 +01:00
227db7e879 Add custom commission types 2026-02-01 16:27:54 +01:00
205 changed files with 11665 additions and 2422 deletions

1
.gitignore vendored
View File

@ -41,3 +41,4 @@ yarn-error.log*
next-env.d.ts next-env.d.ts
/src/generated/prisma /src/generated/prisma
/scripts/

View File

@ -18,6 +18,12 @@ RUN bunx prisma generate
# Uncomment the following line in case you want to disable telemetry during the build. # Uncomment the following line in case you want to disable telemetry during the build.
ENV NEXT_TELEMETRY_DISABLED=1 ENV NEXT_TELEMETRY_DISABLED=1
ARG GIT_SHA=unknown
ARG APP_VERSION=0.0.0
ARG DEPLOY_ENV=production
ENV NEXT_PUBLIC_GIT_SHA=$GIT_SHA \
NEXT_PUBLIC_APP_VERSION=$APP_VERSION \
NEXT_PUBLIC_DEPLOY_ENV=$DEPLOY_ENV
# Copy the rest of the code # Copy the rest of the code
RUN bun run build -d RUN bun run build -d
@ -31,7 +37,13 @@ ENV NEXT_TELEMETRY_DISABLED=1
ENV NODE_ENV=production \ ENV NODE_ENV=production \
PORT=3000 \ PORT=3000 \
HOSTNAME="0.0.0.0" HOSTNAME="0.0.0.0"
ARG GIT_SHA=unknown
ARG APP_VERSION=0.0.0
ARG DEPLOY_ENV=production
ENV NEXT_PUBLIC_GIT_SHA=$GIT_SHA \
NEXT_PUBLIC_APP_VERSION=$APP_VERSION \
NEXT_PUBLIC_DEPLOY_ENV=$DEPLOY_ENV
RUN groupadd --system --gid 1001 nodejs && \ RUN groupadd --system --gid 1001 nodejs && \
useradd --system --uid 1001 --no-log-init -g nodejs nextjs useradd --system --uid 1001 --no-log-init -g nodejs nextjs

View File

@ -14,11 +14,16 @@ FROM base AS builder
WORKDIR /app WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules COPY --from=deps /app/node_modules ./node_modules
COPY . . COPY . .
# RUN bunx prisma migrate deploy
RUN bunx prisma generate RUN bunx prisma generate
# Uncomment the following line in case you want to disable telemetry during the build. # Uncomment the following line in case you want to disable telemetry during the build.
ENV NEXT_TELEMETRY_DISABLED=1 ENV NEXT_TELEMETRY_DISABLED=1
ARG GIT_SHA=unknown
ARG APP_VERSION=0.0.0
ARG DEPLOY_ENV=production
ENV NEXT_PUBLIC_GIT_SHA=$GIT_SHA \
NEXT_PUBLIC_APP_VERSION=$APP_VERSION \
NEXT_PUBLIC_DEPLOY_ENV=$DEPLOY_ENV
# Copy the rest of the code # Copy the rest of the code
RUN bun run build -d RUN bun run build -d
@ -32,6 +37,12 @@ ENV NEXT_TELEMETRY_DISABLED=1
ENV NODE_ENV=production \ ENV NODE_ENV=production \
PORT=3000 \ PORT=3000 \
HOSTNAME="0.0.0.0" HOSTNAME="0.0.0.0"
ARG GIT_SHA=unknown
ARG APP_VERSION=0.0.0
ARG DEPLOY_ENV=production
ENV NEXT_PUBLIC_GIT_SHA=$GIT_SHA \
NEXT_PUBLIC_APP_VERSION=$APP_VERSION \
NEXT_PUBLIC_DEPLOY_ENV=$DEPLOY_ENV
RUN groupadd --system --gid 1001 nodejs && \ RUN groupadd --system --gid 1001 nodejs && \
useradd --system --uid 1001 --no-log-init -g nodejs nextjs useradd --system --uid 1001 --no-log-init -g nodejs nextjs

312
bun.lock
View File

@ -48,15 +48,15 @@
"dotenv": "^17.2.3", "dotenv": "^17.2.3",
"lowlight": "^3.3.0", "lowlight": "^3.3.0",
"lucide-react": "^0.561.0", "lucide-react": "^0.561.0",
"next": "^16.1.4", "next": "16.1.6",
"next-themes": "^0.4.6", "next-themes": "^0.4.6",
"node-vibrant": "^4.0.3", "node-vibrant": "^4.0.3",
"nodemailer": "^7.0.12", "nodemailer": "^7.0.12",
"pg": "^8.17.2", "pg": "^8.17.2",
"platejs": "^52.0.17", "platejs": "^52.0.17",
"react": "19.2.1", "react": "19.2.4",
"react-day-picker": "^9.13.0", "react-day-picker": "^9.13.0",
"react-dom": "19.2.1", "react-dom": "19.2.4",
"react-hook-form": "^7.71.1", "react-hook-form": "^7.71.1",
"remark-gfm": "^4.0.1", "remark-gfm": "^4.0.1",
"remark-math": "^6.0.0", "remark-math": "^6.0.0",
@ -75,8 +75,8 @@
"@types/node": "^20.19.30", "@types/node": "^20.19.30",
"@types/nodemailer": "^7.0.5", "@types/nodemailer": "^7.0.5",
"@types/pg": "^8.16.0", "@types/pg": "^8.16.0",
"@types/react": "^19.2.9", "@types/react": "19.2.10",
"@types/react-dom": "^19.2.3", "@types/react-dom": "19.2.3",
"@types/uuid": "^11.0.0", "@types/uuid": "^11.0.0",
"prisma": "^7.3.0", "prisma": "^7.3.0",
"tailwindcss": "^4.1.18", "tailwindcss": "^4.1.18",
@ -85,6 +85,10 @@
}, },
}, },
}, },
"overrides": {
"@types/react": "19.2.10",
"@types/react-dom": "19.2.3",
},
"packages": { "packages": {
"@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="], "@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="],
@ -102,83 +106,81 @@
"@aws-crypto/util": ["@aws-crypto/util@5.2.0", "", { "dependencies": { "@aws-sdk/types": "^3.222.0", "@smithy/util-utf8": "^2.0.0", "tslib": "^2.6.2" } }, "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ=="], "@aws-crypto/util": ["@aws-crypto/util@5.2.0", "", { "dependencies": { "@aws-sdk/types": "^3.222.0", "@smithy/util-utf8": "^2.0.0", "tslib": "^2.6.2" } }, "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ=="],
"@aws-sdk/client-s3": ["@aws-sdk/client-s3@3.974.0", "", { "dependencies": { "@aws-crypto/sha1-browser": "5.2.0", "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.973.0", "@aws-sdk/credential-provider-node": "^3.972.1", "@aws-sdk/middleware-bucket-endpoint": "^3.972.1", "@aws-sdk/middleware-expect-continue": "^3.972.1", "@aws-sdk/middleware-flexible-checksums": "^3.972.1", "@aws-sdk/middleware-host-header": "^3.972.1", "@aws-sdk/middleware-location-constraint": "^3.972.1", "@aws-sdk/middleware-logger": "^3.972.1", "@aws-sdk/middleware-recursion-detection": "^3.972.1", "@aws-sdk/middleware-sdk-s3": "^3.972.1", "@aws-sdk/middleware-ssec": "^3.972.1", "@aws-sdk/middleware-user-agent": "^3.972.1", "@aws-sdk/region-config-resolver": "^3.972.1", "@aws-sdk/signature-v4-multi-region": "3.972.0", "@aws-sdk/types": "^3.973.0", "@aws-sdk/util-endpoints": "3.972.0", "@aws-sdk/util-user-agent-browser": "^3.972.1", "@aws-sdk/util-user-agent-node": "^3.972.1", "@smithy/config-resolver": "^4.4.6", "@smithy/core": "^3.21.0", "@smithy/eventstream-serde-browser": "^4.2.8", "@smithy/eventstream-serde-config-resolver": "^4.3.8", "@smithy/eventstream-serde-node": "^4.2.8", "@smithy/fetch-http-handler": "^5.3.9", "@smithy/hash-blob-browser": "^4.2.9", "@smithy/hash-node": "^4.2.8", "@smithy/hash-stream-node": "^4.2.8", "@smithy/invalid-dependency": "^4.2.8", "@smithy/md5-js": "^4.2.8", "@smithy/middleware-content-length": "^4.2.8", "@smithy/middleware-endpoint": "^4.4.10", "@smithy/middleware-retry": "^4.4.26", "@smithy/middleware-serde": "^4.2.9", "@smithy/middleware-stack": "^4.2.8", "@smithy/node-config-provider": "^4.3.8", "@smithy/node-http-handler": "^4.4.8", "@smithy/protocol-http": "^5.3.8", "@smithy/smithy-client": "^4.10.11", "@smithy/types": "^4.12.0", "@smithy/url-parser": "^4.2.8", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.25", "@smithy/util-defaults-mode-node": "^4.2.28", "@smithy/util-endpoints": "^3.2.8", "@smithy/util-middleware": "^4.2.8", "@smithy/util-retry": "^4.2.8", "@smithy/util-stream": "^4.5.10", "@smithy/util-utf8": "^4.2.0", "@smithy/util-waiter": "^4.2.8", "tslib": "^2.6.2" } }, "sha512-X+vpXNJ8cU8Iw1FtDgDHxo9z6RxlXfcTtpdGnKws4rk+tCYKSAor/DG6BRMzbh4E5xAA7DiU1Ny3BTrRRSt/Yg=="], "@aws-sdk/client-s3": ["@aws-sdk/client-s3@3.980.0", "", { "dependencies": { "@aws-crypto/sha1-browser": "5.2.0", "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.973.5", "@aws-sdk/credential-provider-node": "^3.972.4", "@aws-sdk/middleware-bucket-endpoint": "^3.972.3", "@aws-sdk/middleware-expect-continue": "^3.972.3", "@aws-sdk/middleware-flexible-checksums": "^3.972.3", "@aws-sdk/middleware-host-header": "^3.972.3", "@aws-sdk/middleware-location-constraint": "^3.972.3", "@aws-sdk/middleware-logger": "^3.972.3", "@aws-sdk/middleware-recursion-detection": "^3.972.3", "@aws-sdk/middleware-sdk-s3": "^3.972.5", "@aws-sdk/middleware-ssec": "^3.972.3", "@aws-sdk/middleware-user-agent": "^3.972.5", "@aws-sdk/region-config-resolver": "^3.972.3", "@aws-sdk/signature-v4-multi-region": "3.980.0", "@aws-sdk/types": "^3.973.1", "@aws-sdk/util-endpoints": "3.980.0", "@aws-sdk/util-user-agent-browser": "^3.972.3", "@aws-sdk/util-user-agent-node": "^3.972.3", "@smithy/config-resolver": "^4.4.6", "@smithy/core": "^3.22.0", "@smithy/eventstream-serde-browser": "^4.2.8", "@smithy/eventstream-serde-config-resolver": "^4.3.8", "@smithy/eventstream-serde-node": "^4.2.8", "@smithy/fetch-http-handler": "^5.3.9", "@smithy/hash-blob-browser": "^4.2.9", "@smithy/hash-node": "^4.2.8", "@smithy/hash-stream-node": "^4.2.8", "@smithy/invalid-dependency": "^4.2.8", "@smithy/md5-js": "^4.2.8", "@smithy/middleware-content-length": "^4.2.8", "@smithy/middleware-endpoint": "^4.4.12", "@smithy/middleware-retry": "^4.4.29", "@smithy/middleware-serde": "^4.2.9", "@smithy/middleware-stack": "^4.2.8", "@smithy/node-config-provider": "^4.3.8", "@smithy/node-http-handler": "^4.4.8", "@smithy/protocol-http": "^5.3.8", "@smithy/smithy-client": "^4.11.1", "@smithy/types": "^4.12.0", "@smithy/url-parser": "^4.2.8", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.28", "@smithy/util-defaults-mode-node": "^4.2.31", "@smithy/util-endpoints": "^3.2.8", "@smithy/util-middleware": "^4.2.8", "@smithy/util-retry": "^4.2.8", "@smithy/util-stream": "^4.5.10", "@smithy/util-utf8": "^4.2.0", "@smithy/util-waiter": "^4.2.8", "tslib": "^2.6.2" } }, "sha512-ch8QqKehyn1WOYbd8LyDbWjv84Z9OEj9qUxz8q3IOCU3ftAVkVR0wAuN96a1xCHnpOJcQZo3rOB08RlyKdkGxQ=="],
"@aws-sdk/client-sesv2": ["@aws-sdk/client-sesv2@3.958.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.957.0", "@aws-sdk/credential-provider-node": "3.958.0", "@aws-sdk/middleware-host-header": "3.957.0", "@aws-sdk/middleware-logger": "3.957.0", "@aws-sdk/middleware-recursion-detection": "3.957.0", "@aws-sdk/middleware-user-agent": "3.957.0", "@aws-sdk/region-config-resolver": "3.957.0", "@aws-sdk/signature-v4-multi-region": "3.957.0", "@aws-sdk/types": "3.957.0", "@aws-sdk/util-endpoints": "3.957.0", "@aws-sdk/util-user-agent-browser": "3.957.0", "@aws-sdk/util-user-agent-node": "3.957.0", "@smithy/config-resolver": "^4.4.5", "@smithy/core": "^3.20.0", "@smithy/fetch-http-handler": "^5.3.8", "@smithy/hash-node": "^4.2.7", "@smithy/invalid-dependency": "^4.2.7", "@smithy/middleware-content-length": "^4.2.7", "@smithy/middleware-endpoint": "^4.4.1", "@smithy/middleware-retry": "^4.4.17", "@smithy/middleware-serde": "^4.2.8", "@smithy/middleware-stack": "^4.2.7", "@smithy/node-config-provider": "^4.3.7", "@smithy/node-http-handler": "^4.4.7", "@smithy/protocol-http": "^5.3.7", "@smithy/smithy-client": "^4.10.2", "@smithy/types": "^4.11.0", "@smithy/url-parser": "^4.2.7", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.16", "@smithy/util-defaults-mode-node": "^4.2.19", "@smithy/util-endpoints": "^3.2.7", "@smithy/util-middleware": "^4.2.7", "@smithy/util-retry": "^4.2.7", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-3x3n8IIxIMAkdpt9wy9zS7MO2lqTcJwQTdHMn6BlD7YUohb+r5Q4KCOEQ2uHWd4WIJv2tlbXnfypHaXReO/WXA=="], "@aws-sdk/client-sso": ["@aws-sdk/client-sso@3.980.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.973.5", "@aws-sdk/middleware-host-header": "^3.972.3", "@aws-sdk/middleware-logger": "^3.972.3", "@aws-sdk/middleware-recursion-detection": "^3.972.3", "@aws-sdk/middleware-user-agent": "^3.972.5", "@aws-sdk/region-config-resolver": "^3.972.3", "@aws-sdk/types": "^3.973.1", "@aws-sdk/util-endpoints": "3.980.0", "@aws-sdk/util-user-agent-browser": "^3.972.3", "@aws-sdk/util-user-agent-node": "^3.972.3", "@smithy/config-resolver": "^4.4.6", "@smithy/core": "^3.22.0", "@smithy/fetch-http-handler": "^5.3.9", "@smithy/hash-node": "^4.2.8", "@smithy/invalid-dependency": "^4.2.8", "@smithy/middleware-content-length": "^4.2.8", "@smithy/middleware-endpoint": "^4.4.12", "@smithy/middleware-retry": "^4.4.29", "@smithy/middleware-serde": "^4.2.9", "@smithy/middleware-stack": "^4.2.8", "@smithy/node-config-provider": "^4.3.8", "@smithy/node-http-handler": "^4.4.8", "@smithy/protocol-http": "^5.3.8", "@smithy/smithy-client": "^4.11.1", "@smithy/types": "^4.12.0", "@smithy/url-parser": "^4.2.8", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.28", "@smithy/util-defaults-mode-node": "^4.2.31", "@smithy/util-endpoints": "^3.2.8", "@smithy/util-middleware": "^4.2.8", "@smithy/util-retry": "^4.2.8", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-AhNXQaJ46C1I+lQ+6Kj+L24il5K9lqqIanJd8lMszPmP7bLnmX0wTKK0dxywcvrLdij3zhWttjAKEBNgLtS8/A=="],
"@aws-sdk/client-sso": ["@aws-sdk/client-sso@3.974.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.973.0", "@aws-sdk/middleware-host-header": "^3.972.1", "@aws-sdk/middleware-logger": "^3.972.1", "@aws-sdk/middleware-recursion-detection": "^3.972.1", "@aws-sdk/middleware-user-agent": "^3.972.1", "@aws-sdk/region-config-resolver": "^3.972.1", "@aws-sdk/types": "^3.973.0", "@aws-sdk/util-endpoints": "3.972.0", "@aws-sdk/util-user-agent-browser": "^3.972.1", "@aws-sdk/util-user-agent-node": "^3.972.1", "@smithy/config-resolver": "^4.4.6", "@smithy/core": "^3.21.0", "@smithy/fetch-http-handler": "^5.3.9", "@smithy/hash-node": "^4.2.8", "@smithy/invalid-dependency": "^4.2.8", "@smithy/middleware-content-length": "^4.2.8", "@smithy/middleware-endpoint": "^4.4.10", "@smithy/middleware-retry": "^4.4.26", "@smithy/middleware-serde": "^4.2.9", "@smithy/middleware-stack": "^4.2.8", "@smithy/node-config-provider": "^4.3.8", "@smithy/node-http-handler": "^4.4.8", "@smithy/protocol-http": "^5.3.8", "@smithy/smithy-client": "^4.10.11", "@smithy/types": "^4.12.0", "@smithy/url-parser": "^4.2.8", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.25", "@smithy/util-defaults-mode-node": "^4.2.28", "@smithy/util-endpoints": "^3.2.8", "@smithy/util-middleware": "^4.2.8", "@smithy/util-retry": "^4.2.8", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-ci+GiM0c4ULo4D79UMcY06LcOLcfvUfiyt8PzNY0vbt5O8BfCPYf4QomwVgkNcLLCYmroO4ge2Yy1EsLUlcD6g=="], "@aws-sdk/core": ["@aws-sdk/core@3.973.5", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@aws-sdk/xml-builder": "^3.972.2", "@smithy/core": "^3.22.0", "@smithy/node-config-provider": "^4.3.8", "@smithy/property-provider": "^4.2.8", "@smithy/protocol-http": "^5.3.8", "@smithy/signature-v4": "^5.3.8", "@smithy/smithy-client": "^4.11.1", "@smithy/types": "^4.12.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-middleware": "^4.2.8", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-IMM7xGfLGW6lMvubsA4j6BHU5FPgGAxoQ/NA63KqNLMwTS+PeMBcx8DPHL12Vg6yqOZnqok9Mu4H2BdQyq7gSA=="],
"@aws-sdk/core": ["@aws-sdk/core@3.973.0", "", { "dependencies": { "@aws-sdk/types": "^3.973.0", "@aws-sdk/xml-builder": "^3.972.1", "@smithy/core": "^3.21.0", "@smithy/node-config-provider": "^4.3.8", "@smithy/property-provider": "^4.2.8", "@smithy/protocol-http": "^5.3.8", "@smithy/signature-v4": "^5.3.8", "@smithy/smithy-client": "^4.10.11", "@smithy/types": "^4.12.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-middleware": "^4.2.8", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-qy3Fmt8z4PRInM3ZqJmHihQ2tfCdj/MzbGaZpuHjYjgl1/Gcar4Pyp/zzHXh9hGEb61WNbWgsJcDUhnGIiX1TA=="],
"@aws-sdk/crc64-nvme": ["@aws-sdk/crc64-nvme@3.972.0", "", { "dependencies": { "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-ThlLhTqX68jvoIVv+pryOdb5coP1cX1/MaTbB9xkGDCbWbsqQcLqzPxuSoW1DCnAAIacmXCWpzUNOB9pv+xXQw=="], "@aws-sdk/crc64-nvme": ["@aws-sdk/crc64-nvme@3.972.0", "", { "dependencies": { "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-ThlLhTqX68jvoIVv+pryOdb5coP1cX1/MaTbB9xkGDCbWbsqQcLqzPxuSoW1DCnAAIacmXCWpzUNOB9pv+xXQw=="],
"@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.972.1", "", { "dependencies": { "@aws-sdk/core": "^3.973.0", "@aws-sdk/types": "^3.973.0", "@smithy/property-provider": "^4.2.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-/etNHqnx96phy/SjI0HRC588o4vKH5F0xfkZ13yAATV7aNrb+5gYGNE6ePWafP+FuZ3HkULSSlJFj0AxgrAqYw=="], "@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.972.3", "", { "dependencies": { "@aws-sdk/core": "^3.973.5", "@aws-sdk/types": "^3.973.1", "@smithy/property-provider": "^4.2.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-OBYNY4xQPq7Rx+oOhtyuyO0AQvdJSpXRg7JuPNBJH4a1XXIzJQl4UHQTPKZKwfJXmYLpv4+OkcFen4LYmDPd3g=="],
"@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.972.1", "", { "dependencies": { "@aws-sdk/core": "^3.973.0", "@aws-sdk/types": "^3.973.0", "@smithy/fetch-http-handler": "^5.3.9", "@smithy/node-http-handler": "^4.4.8", "@smithy/property-provider": "^4.2.8", "@smithy/protocol-http": "^5.3.8", "@smithy/smithy-client": "^4.10.11", "@smithy/types": "^4.12.0", "@smithy/util-stream": "^4.5.10", "tslib": "^2.6.2" } }, "sha512-AeopObGW5lpWbDRZ+t4EAtS7wdfSrHPLeFts7jaBzgIaCCD7TL7jAyAB9Y5bCLOPF+17+GL54djCCsjePljUAw=="], "@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.972.5", "", { "dependencies": { "@aws-sdk/core": "^3.973.5", "@aws-sdk/types": "^3.973.1", "@smithy/fetch-http-handler": "^5.3.9", "@smithy/node-http-handler": "^4.4.8", "@smithy/property-provider": "^4.2.8", "@smithy/protocol-http": "^5.3.8", "@smithy/smithy-client": "^4.11.1", "@smithy/types": "^4.12.0", "@smithy/util-stream": "^4.5.10", "tslib": "^2.6.2" } }, "sha512-GpvBgEmSZPvlDekd26Zi+XsI27Qz7y0utUx0g2fSTSiDzhnd1FSa1owuodxR0BcUKNL7U2cOVhhDxgZ4iSoPVg=="],
"@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.972.1", "", { "dependencies": { "@aws-sdk/core": "^3.973.0", "@aws-sdk/credential-provider-env": "^3.972.1", "@aws-sdk/credential-provider-http": "^3.972.1", "@aws-sdk/credential-provider-login": "^3.972.1", "@aws-sdk/credential-provider-process": "^3.972.1", "@aws-sdk/credential-provider-sso": "^3.972.1", "@aws-sdk/credential-provider-web-identity": "^3.972.1", "@aws-sdk/nested-clients": "3.974.0", "@aws-sdk/types": "^3.973.0", "@smithy/credential-provider-imds": "^4.2.8", "@smithy/property-provider": "^4.2.8", "@smithy/shared-ini-file-loader": "^4.4.3", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-OdbJA3v+XlNDsrYzNPRUwr8l7gw1r/nR8l4r96MDzSBDU8WEo8T6C06SvwaXR8SpzsjO3sq5KMP86wXWg7Rj4g=="], "@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.972.3", "", { "dependencies": { "@aws-sdk/core": "^3.973.5", "@aws-sdk/credential-provider-env": "^3.972.3", "@aws-sdk/credential-provider-http": "^3.972.5", "@aws-sdk/credential-provider-login": "^3.972.3", "@aws-sdk/credential-provider-process": "^3.972.3", "@aws-sdk/credential-provider-sso": "^3.972.3", "@aws-sdk/credential-provider-web-identity": "^3.972.3", "@aws-sdk/nested-clients": "3.980.0", "@aws-sdk/types": "^3.973.1", "@smithy/credential-provider-imds": "^4.2.8", "@smithy/property-provider": "^4.2.8", "@smithy/shared-ini-file-loader": "^4.4.3", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-rMQAIxstP7cLgYfsRGrGOlpyMl0l8JL2mcke3dsIPLWke05zKOFyR7yoJzWCsI/QiIxjRbxpvPiAeKEA6CoYkg=="],
"@aws-sdk/credential-provider-login": ["@aws-sdk/credential-provider-login@3.972.1", "", { "dependencies": { "@aws-sdk/core": "^3.973.0", "@aws-sdk/nested-clients": "3.974.0", "@aws-sdk/types": "^3.973.0", "@smithy/property-provider": "^4.2.8", "@smithy/protocol-http": "^5.3.8", "@smithy/shared-ini-file-loader": "^4.4.3", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-CccqDGL6ZrF3/EFWZefvKW7QwwRdxlHUO8NVBKNVcNq6womrPDvqB6xc9icACtE0XB0a7PLoSTkAg8bQVkTO2w=="], "@aws-sdk/credential-provider-login": ["@aws-sdk/credential-provider-login@3.972.3", "", { "dependencies": { "@aws-sdk/core": "^3.973.5", "@aws-sdk/nested-clients": "3.980.0", "@aws-sdk/types": "^3.973.1", "@smithy/property-provider": "^4.2.8", "@smithy/protocol-http": "^5.3.8", "@smithy/shared-ini-file-loader": "^4.4.3", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-Gc3O91iVvA47kp2CLIXOwuo5ffo1cIpmmyIewcYjAcvurdFHQ8YdcBe1KHidnbbBO4/ZtywGBACsAX5vr3UdoA=="],
"@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.972.1", "", { "dependencies": { "@aws-sdk/credential-provider-env": "^3.972.1", "@aws-sdk/credential-provider-http": "^3.972.1", "@aws-sdk/credential-provider-ini": "^3.972.1", "@aws-sdk/credential-provider-process": "^3.972.1", "@aws-sdk/credential-provider-sso": "^3.972.1", "@aws-sdk/credential-provider-web-identity": "^3.972.1", "@aws-sdk/types": "^3.973.0", "@smithy/credential-provider-imds": "^4.2.8", "@smithy/property-provider": "^4.2.8", "@smithy/shared-ini-file-loader": "^4.4.3", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-DwXPk9GfuU/xG9tmCyXFVkCr6X3W8ZCoL5Ptb0pbltEx1/LCcg7T+PBqDlPiiinNCD6ilIoMJDWsnJ8ikzZA7Q=="], "@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.972.4", "", { "dependencies": { "@aws-sdk/credential-provider-env": "^3.972.3", "@aws-sdk/credential-provider-http": "^3.972.5", "@aws-sdk/credential-provider-ini": "^3.972.3", "@aws-sdk/credential-provider-process": "^3.972.3", "@aws-sdk/credential-provider-sso": "^3.972.3", "@aws-sdk/credential-provider-web-identity": "^3.972.3", "@aws-sdk/types": "^3.973.1", "@smithy/credential-provider-imds": "^4.2.8", "@smithy/property-provider": "^4.2.8", "@smithy/shared-ini-file-loader": "^4.4.3", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-UwerdzosMSY7V5oIZm3NsMDZPv2aSVzSkZxYxIOWHBeKTZlUqW7XpHtJMZ4PZpJ+HMRhgP+MDGQx4THndgqJfQ=="],
"@aws-sdk/credential-provider-process": ["@aws-sdk/credential-provider-process@3.972.1", "", { "dependencies": { "@aws-sdk/core": "^3.973.0", "@aws-sdk/types": "^3.973.0", "@smithy/property-provider": "^4.2.8", "@smithy/shared-ini-file-loader": "^4.4.3", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-bi47Zigu3692SJwdBvo8y1dEwE6B61stCwCFnuRWJVTfiM84B+VTSCV661CSWJmIZzmcy7J5J3kWyxL02iHj0w=="], "@aws-sdk/credential-provider-process": ["@aws-sdk/credential-provider-process@3.972.3", "", { "dependencies": { "@aws-sdk/core": "^3.973.5", "@aws-sdk/types": "^3.973.1", "@smithy/property-provider": "^4.2.8", "@smithy/shared-ini-file-loader": "^4.4.3", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-xkSY7zjRqeVc6TXK2xr3z1bTLm0wD8cj3lAkproRGaO4Ku7dPlKy843YKnHrUOUzOnMezdZ4xtmFc0eKIDTo2w=="],
"@aws-sdk/credential-provider-sso": ["@aws-sdk/credential-provider-sso@3.972.1", "", { "dependencies": { "@aws-sdk/client-sso": "3.974.0", "@aws-sdk/core": "^3.973.0", "@aws-sdk/token-providers": "3.974.0", "@aws-sdk/types": "^3.973.0", "@smithy/property-provider": "^4.2.8", "@smithy/shared-ini-file-loader": "^4.4.3", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-dLZVNhM7wSgVUFsgVYgI5hb5Z/9PUkT46pk/SHrSmUqfx6YDvoV4YcPtaiRqviPpEGGiRtdQMEadyOKIRqulUQ=="], "@aws-sdk/credential-provider-sso": ["@aws-sdk/credential-provider-sso@3.972.3", "", { "dependencies": { "@aws-sdk/client-sso": "3.980.0", "@aws-sdk/core": "^3.973.5", "@aws-sdk/token-providers": "3.980.0", "@aws-sdk/types": "^3.973.1", "@smithy/property-provider": "^4.2.8", "@smithy/shared-ini-file-loader": "^4.4.3", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-8Ww3F5Ngk8dZ6JPL/V5LhCU1BwMfQd3tLdoEuzaewX8FdnT633tPr+KTHySz9FK7fFPcz5qG3R5edVEhWQD4AA=="],
"@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.972.1", "", { "dependencies": { "@aws-sdk/core": "^3.973.0", "@aws-sdk/nested-clients": "3.974.0", "@aws-sdk/types": "^3.973.0", "@smithy/property-provider": "^4.2.8", "@smithy/shared-ini-file-loader": "^4.4.3", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-YMDeYgi0u687Ay0dAq/pFPKuijrlKTgsaB/UATbxCs/FzZfMiG4If5ksywHmmW7MiYUF8VVv+uou3TczvLrN4w=="], "@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.972.3", "", { "dependencies": { "@aws-sdk/core": "^3.973.5", "@aws-sdk/nested-clients": "3.980.0", "@aws-sdk/types": "^3.973.1", "@smithy/property-provider": "^4.2.8", "@smithy/shared-ini-file-loader": "^4.4.3", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-62VufdcH5rRfiRKZRcf1wVbbt/1jAntMj1+J0qAd+r5pQRg2t0/P9/Rz16B1o5/0Se9lVL506LRjrhIJAhYBfA=="],
"@aws-sdk/middleware-bucket-endpoint": ["@aws-sdk/middleware-bucket-endpoint@3.972.1", "", { "dependencies": { "@aws-sdk/types": "^3.973.0", "@aws-sdk/util-arn-parser": "^3.972.1", "@smithy/node-config-provider": "^4.3.8", "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "@smithy/util-config-provider": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-YVvoitBdE8WOpHqIXvv49efT73F4bJ99XH2bi3Dn3mx7WngI4RwHwn/zF5i0q1Wdi5frGSCNF3vuh+pY817//w=="], "@aws-sdk/middleware-bucket-endpoint": ["@aws-sdk/middleware-bucket-endpoint@3.972.3", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@aws-sdk/util-arn-parser": "^3.972.2", "@smithy/node-config-provider": "^4.3.8", "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "@smithy/util-config-provider": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-fmbgWYirF67YF1GfD7cg5N6HHQ96EyRNx/rDIrTF277/zTWVuPI2qS/ZHgofwR1NZPe/NWvoppflQY01LrbVLg=="],
"@aws-sdk/middleware-expect-continue": ["@aws-sdk/middleware-expect-continue@3.972.1", "", { "dependencies": { "@aws-sdk/types": "^3.973.0", "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-6lfl2/J/kutzw/RLu1kjbahsz4vrGPysrdxWaw8fkjLYG+6M6AswocIAZFS/LgAVi/IWRwPTx9YC0/NH2wDrSw=="], "@aws-sdk/middleware-expect-continue": ["@aws-sdk/middleware-expect-continue@3.972.3", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-4msC33RZsXQpUKR5QR4HnvBSNCPLGHmB55oDiROqqgyOc+TOfVu2xgi5goA7ms6MdZLeEh2905UfWMnMMF4mRg=="],
"@aws-sdk/middleware-flexible-checksums": ["@aws-sdk/middleware-flexible-checksums@3.972.1", "", { "dependencies": { "@aws-crypto/crc32": "5.2.0", "@aws-crypto/crc32c": "5.2.0", "@aws-crypto/util": "5.2.0", "@aws-sdk/core": "^3.973.0", "@aws-sdk/crc64-nvme": "3.972.0", "@aws-sdk/types": "^3.973.0", "@smithy/is-array-buffer": "^4.2.0", "@smithy/node-config-provider": "^4.3.8", "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "@smithy/util-middleware": "^4.2.8", "@smithy/util-stream": "^4.5.10", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kjVVREpqeUkYQsXr78AcsJbEUlxGH7+H6yS7zkjrnu6HyEVxbdSndkKX6VpKneFOihjCAhIXlk4wf3butDHkNQ=="], "@aws-sdk/middleware-flexible-checksums": ["@aws-sdk/middleware-flexible-checksums@3.972.3", "", { "dependencies": { "@aws-crypto/crc32": "5.2.0", "@aws-crypto/crc32c": "5.2.0", "@aws-crypto/util": "5.2.0", "@aws-sdk/core": "^3.973.5", "@aws-sdk/crc64-nvme": "3.972.0", "@aws-sdk/types": "^3.973.1", "@smithy/is-array-buffer": "^4.2.0", "@smithy/node-config-provider": "^4.3.8", "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "@smithy/util-middleware": "^4.2.8", "@smithy/util-stream": "^4.5.10", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-MkNGJ6qB9kpsLwL18kC/ZXppsJbftHVGCisqpEVbTQsum8CLYDX1Bmp/IvhRGNxsqCO2w9/4PwhDKBjG3Uvr4Q=="],
"@aws-sdk/middleware-host-header": ["@aws-sdk/middleware-host-header@3.972.1", "", { "dependencies": { "@aws-sdk/types": "^3.973.0", "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-/R82lXLPmZ9JaUGSUdKtBp2k/5xQxvBT3zZWyKiBOhyulFotlfvdlrO8TnqstBimsl4lYEYySDL+W6ldFh6ALg=="], "@aws-sdk/middleware-host-header": ["@aws-sdk/middleware-host-header@3.972.3", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-aknPTb2M+G3s+0qLCx4Li/qGZH8IIYjugHMv15JTYMe6mgZO8VBpYgeGYsNMGCqCZOcWzuf900jFBG5bopfzmA=="],
"@aws-sdk/middleware-location-constraint": ["@aws-sdk/middleware-location-constraint@3.972.1", "", { "dependencies": { "@aws-sdk/types": "^3.973.0", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-YisPaCbvBk9gY5aUI8jDMDKXsLZ9Fet0WYj1MviK8tZYMgxBIYHM6l3O/OHaAIujojZvamd9F3haYYYWp5/V3w=="], "@aws-sdk/middleware-location-constraint": ["@aws-sdk/middleware-location-constraint@3.972.3", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-nIg64CVrsXp67vbK0U1/Is8rik3huS3QkRHn2DRDx4NldrEFMgdkZGI/+cZMKD9k4YOS110Dfu21KZLHrFA/1g=="],
"@aws-sdk/middleware-logger": ["@aws-sdk/middleware-logger@3.972.1", "", { "dependencies": { "@aws-sdk/types": "^3.973.0", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-JGgFl6cHg9G2FHu4lyFIzmFN8KESBiRr84gLC3Aeni0Gt1nKm+KxWLBuha/RPcXxJygGXCcMM4AykkIwxor8RA=="], "@aws-sdk/middleware-logger": ["@aws-sdk/middleware-logger@3.972.3", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-Ftg09xNNRqaz9QNzlfdQWfpqMCJbsQdnZVJP55jfhbKi1+FTWxGuvfPoBhDHIovqWKjqbuiew3HuhxbJ0+OjgA=="],
"@aws-sdk/middleware-recursion-detection": ["@aws-sdk/middleware-recursion-detection@3.972.1", "", { "dependencies": { "@aws-sdk/types": "^3.973.0", "@aws/lambda-invoke-store": "^0.2.2", "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-taGzNRe8vPHjnliqXIHp9kBgIemLE/xCaRTMH1NH0cncHeaPcjxtnCroAAM9aOlPuKvBe2CpZESyvM1+D8oI7Q=="], "@aws-sdk/middleware-recursion-detection": ["@aws-sdk/middleware-recursion-detection@3.972.3", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@aws/lambda-invoke-store": "^0.2.2", "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-PY57QhzNuXHnwbJgbWYTrqIDHYSeOlhfYERTAuc16LKZpTZRJUjzBFokp9hF7u1fuGeE3D70ERXzdbMBOqQz7Q=="],
"@aws-sdk/middleware-sdk-s3": ["@aws-sdk/middleware-sdk-s3@3.972.1", "", { "dependencies": { "@aws-sdk/core": "^3.973.0", "@aws-sdk/types": "^3.973.0", "@aws-sdk/util-arn-parser": "^3.972.1", "@smithy/core": "^3.21.0", "@smithy/node-config-provider": "^4.3.8", "@smithy/protocol-http": "^5.3.8", "@smithy/signature-v4": "^5.3.8", "@smithy/smithy-client": "^4.10.11", "@smithy/types": "^4.12.0", "@smithy/util-config-provider": "^4.2.0", "@smithy/util-middleware": "^4.2.8", "@smithy/util-stream": "^4.5.10", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-q/hK0ZNf/aafFRv2wIlDM3p+izi5cXwktVNvRvW646A0MvVZmT4/vwadv/jPA9AORFbnpyf/0luxiMz181f9yg=="], "@aws-sdk/middleware-sdk-s3": ["@aws-sdk/middleware-sdk-s3@3.972.5", "", { "dependencies": { "@aws-sdk/core": "^3.973.5", "@aws-sdk/types": "^3.973.1", "@aws-sdk/util-arn-parser": "^3.972.2", "@smithy/core": "^3.22.0", "@smithy/node-config-provider": "^4.3.8", "@smithy/protocol-http": "^5.3.8", "@smithy/signature-v4": "^5.3.8", "@smithy/smithy-client": "^4.11.1", "@smithy/types": "^4.12.0", "@smithy/util-config-provider": "^4.2.0", "@smithy/util-middleware": "^4.2.8", "@smithy/util-stream": "^4.5.10", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-3IgeIDiQ15tmMBFIdJ1cTy3A9rXHGo+b9p22V38vA3MozeMyVC8VmCYdDLA0iMWo4VHA9LDJTgCM0+xU3wjBOg=="],
"@aws-sdk/middleware-ssec": ["@aws-sdk/middleware-ssec@3.972.1", "", { "dependencies": { "@aws-sdk/types": "^3.973.0", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-fLtRTPd/MxJT2drJKft2GVGKm35PiNEeQ1Dvz1vc/WhhgAteYrp4f1SfSgjgLaYWGMExESJL4bt8Dxqp6tVsog=="], "@aws-sdk/middleware-ssec": ["@aws-sdk/middleware-ssec@3.972.3", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-dU6kDuULN3o3jEHcjm0c4zWJlY1zWVkjG9NPe9qxYLLpcbdj5kRYBS2DdWYD+1B9f910DezRuws7xDEqKkHQIg=="],
"@aws-sdk/middleware-user-agent": ["@aws-sdk/middleware-user-agent@3.972.1", "", { "dependencies": { "@aws-sdk/core": "^3.973.0", "@aws-sdk/types": "^3.973.0", "@aws-sdk/util-endpoints": "3.972.0", "@smithy/core": "^3.21.0", "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-6SVg4pY/9Oq9MLzO48xuM3lsOb8Rxg55qprEtFRpkUmuvKij31f5SQHEGxuiZ4RqIKrfjr2WMuIgXvqJ0eJsPA=="], "@aws-sdk/middleware-user-agent": ["@aws-sdk/middleware-user-agent@3.972.5", "", { "dependencies": { "@aws-sdk/core": "^3.973.5", "@aws-sdk/types": "^3.973.1", "@aws-sdk/util-endpoints": "3.980.0", "@smithy/core": "^3.22.0", "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-TVZQ6PWPwQbahUI8V+Er+gS41ctIawcI/uMNmQtQ7RMcg3JYn6gyKAFKUb3HFYx2OjYlx1u11sETSwwEUxVHTg=="],
"@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.974.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.973.0", "@aws-sdk/middleware-host-header": "^3.972.1", "@aws-sdk/middleware-logger": "^3.972.1", "@aws-sdk/middleware-recursion-detection": "^3.972.1", "@aws-sdk/middleware-user-agent": "^3.972.1", "@aws-sdk/region-config-resolver": "^3.972.1", "@aws-sdk/types": "^3.973.0", "@aws-sdk/util-endpoints": "3.972.0", "@aws-sdk/util-user-agent-browser": "^3.972.1", "@aws-sdk/util-user-agent-node": "^3.972.1", "@smithy/config-resolver": "^4.4.6", "@smithy/core": "^3.21.0", "@smithy/fetch-http-handler": "^5.3.9", "@smithy/hash-node": "^4.2.8", "@smithy/invalid-dependency": "^4.2.8", "@smithy/middleware-content-length": "^4.2.8", "@smithy/middleware-endpoint": "^4.4.10", "@smithy/middleware-retry": "^4.4.26", "@smithy/middleware-serde": "^4.2.9", "@smithy/middleware-stack": "^4.2.8", "@smithy/node-config-provider": "^4.3.8", "@smithy/node-http-handler": "^4.4.8", "@smithy/protocol-http": "^5.3.8", "@smithy/smithy-client": "^4.10.11", "@smithy/types": "^4.12.0", "@smithy/url-parser": "^4.2.8", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.25", "@smithy/util-defaults-mode-node": "^4.2.28", "@smithy/util-endpoints": "^3.2.8", "@smithy/util-middleware": "^4.2.8", "@smithy/util-retry": "^4.2.8", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-k3dwdo/vOiHMJc9gMnkPl1BA5aQfTrZbz+8fiDkWrPagqAioZgmo5oiaOaeX0grObfJQKDtcpPFR4iWf8cgl8Q=="], "@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.980.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.973.5", "@aws-sdk/middleware-host-header": "^3.972.3", "@aws-sdk/middleware-logger": "^3.972.3", "@aws-sdk/middleware-recursion-detection": "^3.972.3", "@aws-sdk/middleware-user-agent": "^3.972.5", "@aws-sdk/region-config-resolver": "^3.972.3", "@aws-sdk/types": "^3.973.1", "@aws-sdk/util-endpoints": "3.980.0", "@aws-sdk/util-user-agent-browser": "^3.972.3", "@aws-sdk/util-user-agent-node": "^3.972.3", "@smithy/config-resolver": "^4.4.6", "@smithy/core": "^3.22.0", "@smithy/fetch-http-handler": "^5.3.9", "@smithy/hash-node": "^4.2.8", "@smithy/invalid-dependency": "^4.2.8", "@smithy/middleware-content-length": "^4.2.8", "@smithy/middleware-endpoint": "^4.4.12", "@smithy/middleware-retry": "^4.4.29", "@smithy/middleware-serde": "^4.2.9", "@smithy/middleware-stack": "^4.2.8", "@smithy/node-config-provider": "^4.3.8", "@smithy/node-http-handler": "^4.4.8", "@smithy/protocol-http": "^5.3.8", "@smithy/smithy-client": "^4.11.1", "@smithy/types": "^4.12.0", "@smithy/url-parser": "^4.2.8", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.28", "@smithy/util-defaults-mode-node": "^4.2.31", "@smithy/util-endpoints": "^3.2.8", "@smithy/util-middleware": "^4.2.8", "@smithy/util-retry": "^4.2.8", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-/dONY5xc5/CCKzOqHZCTidtAR4lJXWkGefXvTRKdSKMGaYbbKsxDckisd6GfnvPSLxWtvQzwgRGRutMRoYUApQ=="],
"@aws-sdk/region-config-resolver": ["@aws-sdk/region-config-resolver@3.972.1", "", { "dependencies": { "@aws-sdk/types": "^3.973.0", "@smithy/config-resolver": "^4.4.6", "@smithy/node-config-provider": "^4.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-voIY8RORpxLAEgEkYaTFnkaIuRwVBEc+RjVZYcSSllPV+ZEKAacai6kNhJeE3D70Le+JCfvRb52tng/AVHY+jQ=="], "@aws-sdk/region-config-resolver": ["@aws-sdk/region-config-resolver@3.972.3", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@smithy/config-resolver": "^4.4.6", "@smithy/node-config-provider": "^4.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-v4J8qYAWfOMcZ4MJUyatntOicTzEMaU7j3OpkRCGGFSL2NgXQ5VbxauIyORA+pxdKZ0qQG2tCQjQjZDlXEC3Ow=="],
"@aws-sdk/s3-request-presigner": ["@aws-sdk/s3-request-presigner@3.974.0", "", { "dependencies": { "@aws-sdk/signature-v4-multi-region": "3.972.0", "@aws-sdk/types": "^3.973.0", "@aws-sdk/util-format-url": "^3.972.1", "@smithy/middleware-endpoint": "^4.4.10", "@smithy/protocol-http": "^5.3.8", "@smithy/smithy-client": "^4.10.11", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-tApmJb4XXBdNQzxTYIBq9aYj8vjJqiMPyeUF25wzvGjLQfXgvcv5sTR4yyzXBxRc8+O7quWDBgMJGtcNerapRQ=="], "@aws-sdk/s3-request-presigner": ["@aws-sdk/s3-request-presigner@3.980.0", "", { "dependencies": { "@aws-sdk/signature-v4-multi-region": "3.980.0", "@aws-sdk/types": "^3.973.1", "@aws-sdk/util-format-url": "^3.972.3", "@smithy/middleware-endpoint": "^4.4.12", "@smithy/protocol-http": "^5.3.8", "@smithy/smithy-client": "^4.11.1", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-qX1Ptvja9Le0Wt1VadgsJ7Kw8Xf57pTIVmIcvYD5HrdAot71qgXdfBtcbuvNKZPeD+HfcUITwxxHpDiXfSoTsA=="],
"@aws-sdk/signature-v4-multi-region": ["@aws-sdk/signature-v4-multi-region@3.972.0", "", { "dependencies": { "@aws-sdk/middleware-sdk-s3": "3.972.0", "@aws-sdk/types": "3.972.0", "@smithy/protocol-http": "^5.3.8", "@smithy/signature-v4": "^5.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-2udiRijmjpN81Pvajje4TsjbXDZNP6K9bYUanBYH8hXa/tZG5qfGCySD+TyX0sgDxCQmEDMg3LaQdfjNHBDEgQ=="], "@aws-sdk/signature-v4-multi-region": ["@aws-sdk/signature-v4-multi-region@3.980.0", "", { "dependencies": { "@aws-sdk/middleware-sdk-s3": "^3.972.5", "@aws-sdk/types": "^3.973.1", "@smithy/protocol-http": "^5.3.8", "@smithy/signature-v4": "^5.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-tO2jBj+ZIVM0nEgi1SyxWtaYGpuAJdsrugmWcI3/U2MPWCYsrvKasUo0026NvJJao38wyUq9B8XTG8Xu53j/VA=="],
"@aws-sdk/token-providers": ["@aws-sdk/token-providers@3.974.0", "", { "dependencies": { "@aws-sdk/core": "^3.973.0", "@aws-sdk/nested-clients": "3.974.0", "@aws-sdk/types": "^3.973.0", "@smithy/property-provider": "^4.2.8", "@smithy/shared-ini-file-loader": "^4.4.3", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-cBykL0LiccKIgNhGWvQRTPvsBLPZxnmJU3pYxG538jpFX8lQtrCy1L7mmIHNEdxIdIGEPgAEHF8/JQxgBToqUQ=="], "@aws-sdk/token-providers": ["@aws-sdk/token-providers@3.980.0", "", { "dependencies": { "@aws-sdk/core": "^3.973.5", "@aws-sdk/nested-clients": "3.980.0", "@aws-sdk/types": "^3.973.1", "@smithy/property-provider": "^4.2.8", "@smithy/shared-ini-file-loader": "^4.4.3", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-1nFileg1wAgDmieRoj9dOawgr2hhlh7xdvcH57b1NnqfPaVlcqVJyPc6k3TLDUFPY69eEwNxdGue/0wIz58vjA=="],
"@aws-sdk/types": ["@aws-sdk/types@3.973.0", "", { "dependencies": { "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-jYIdB7a7jhRTvyb378nsjyvJh1Si+zVduJ6urMNGpz8RjkmHZ+9vM2H07XaIB2Cfq0GhJRZYOfUCH8uqQhqBkQ=="], "@aws-sdk/types": ["@aws-sdk/types@3.973.1", "", { "dependencies": { "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-DwHBiMNOB468JiX6+i34c+THsKHErYUdNQ3HexeXZvVn4zouLjgaS4FejiGSi2HyBuzuyHg7SuOPmjSvoU9NRg=="],
"@aws-sdk/util-arn-parser": ["@aws-sdk/util-arn-parser@3.972.1", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-XnNit6H9PPHhqUXW/usjX6JeJ6Pm8ZNqivTjmNjgWHeOfVpblUc/MTic02UmCNR0jJLPjQ3mBKiMen0tnkNQjQ=="], "@aws-sdk/util-arn-parser": ["@aws-sdk/util-arn-parser@3.972.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-VkykWbqMjlSgBFDyrY3nOSqupMc6ivXuGmvci6Q3NnLq5kC+mKQe2QBZ4nrWRE/jqOxeFP2uYzLtwncYYcvQDg=="],
"@aws-sdk/util-endpoints": ["@aws-sdk/util-endpoints@3.972.0", "", { "dependencies": { "@aws-sdk/types": "3.972.0", "@smithy/types": "^4.12.0", "@smithy/url-parser": "^4.2.8", "@smithy/util-endpoints": "^3.2.8", "tslib": "^2.6.2" } }, "sha512-6JHsl1V/a1ZW8D8AFfd4R52fwZPnZ5H4U6DS8m/bWT8qad72NvbOFAC7U2cDtFs2TShqUO3TEiX/EJibtY3ijg=="], "@aws-sdk/util-endpoints": ["@aws-sdk/util-endpoints@3.980.0", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@smithy/types": "^4.12.0", "@smithy/url-parser": "^4.2.8", "@smithy/util-endpoints": "^3.2.8", "tslib": "^2.6.2" } }, "sha512-AjKBNEc+rjOZQE1HwcD9aCELqg1GmUj1rtICKuY8cgwB73xJ4U/kNyqKKpN2k9emGqlfDY2D8itIp/vDc6OKpw=="],
"@aws-sdk/util-format-url": ["@aws-sdk/util-format-url@3.972.1", "", { "dependencies": { "@aws-sdk/types": "^3.973.0", "@smithy/querystring-builder": "^4.2.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-8wJ4/XOLU/RIYBHsXsIOTR04bNmalC8F2YPMyf3oL8YC750M3Rv5WGywW0Fo07HCv770KXJOzVq03Gyl68moFg=="], "@aws-sdk/util-format-url": ["@aws-sdk/util-format-url@3.972.3", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@smithy/querystring-builder": "^4.2.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-n7F2ycckcKFXa01vAsT/SJdjFHfKH9s96QHcs5gn8AaaigASICeME8WdUL9uBp8XV/OVwEt8+6gzn6KFUgQa8g=="],
"@aws-sdk/util-locate-window": ["@aws-sdk/util-locate-window@3.953.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-mPxK+I1LcrgC/RSa3G5AMAn8eN2Ay0VOgw8lSRmV1jCtO+iYvNeCqOdxoJUjOW6I5BA4niIRWqVORuRP07776Q=="], "@aws-sdk/util-locate-window": ["@aws-sdk/util-locate-window@3.953.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-mPxK+I1LcrgC/RSa3G5AMAn8eN2Ay0VOgw8lSRmV1jCtO+iYvNeCqOdxoJUjOW6I5BA4niIRWqVORuRP07776Q=="],
"@aws-sdk/util-user-agent-browser": ["@aws-sdk/util-user-agent-browser@3.972.1", "", { "dependencies": { "@aws-sdk/types": "^3.973.0", "@smithy/types": "^4.12.0", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-IgF55NFmJX8d9Wql9M0nEpk2eYbuD8G4781FN4/fFgwTXBn86DvlZJuRWDCMcMqZymnBVX7HW9r+3r9ylqfW0w=="], "@aws-sdk/util-user-agent-browser": ["@aws-sdk/util-user-agent-browser@3.972.3", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@smithy/types": "^4.12.0", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-JurOwkRUcXD/5MTDBcqdyQ9eVedtAsZgw5rBwktsPTN7QtPiS2Ld1jkJepNgYoCufz1Wcut9iup7GJDoIHp8Fw=="],
"@aws-sdk/util-user-agent-node": ["@aws-sdk/util-user-agent-node@3.972.1", "", { "dependencies": { "@aws-sdk/middleware-user-agent": "^3.972.1", "@aws-sdk/types": "^3.973.0", "@smithy/node-config-provider": "^4.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" }, "peerDependencies": { "aws-crt": ">=1.0.0" }, "optionalPeers": ["aws-crt"] }, "sha512-oIs4JFcADzoZ0c915R83XvK2HltWupxNsXUIuZse2rgk7b97zTpkxaqXiH0h9ylh31qtgo/t8hp4tIqcsMrEbQ=="], "@aws-sdk/util-user-agent-node": ["@aws-sdk/util-user-agent-node@3.972.3", "", { "dependencies": { "@aws-sdk/middleware-user-agent": "^3.972.5", "@aws-sdk/types": "^3.973.1", "@smithy/node-config-provider": "^4.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" }, "peerDependencies": { "aws-crt": ">=1.0.0" }, "optionalPeers": ["aws-crt"] }, "sha512-gqG+02/lXQtO0j3US6EVnxtwwoXQC5l2qkhLCrqUrqdtcQxV7FDMbm9wLjKqoronSHyELGTjbFKK/xV5q1bZNA=="],
"@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.972.1", "", { "dependencies": { "@smithy/types": "^4.12.0", "fast-xml-parser": "5.2.5", "tslib": "^2.6.2" } }, "sha512-6zZGlPOqn7Xb+25MAXGb1JhgvaC5HjZj6GzszuVrnEgbhvzBRFGKYemuHBV4bho+dtqeYKPgaZUv7/e80hIGNg=="], "@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.972.2", "", { "dependencies": { "@smithy/types": "^4.12.0", "fast-xml-parser": "5.2.5", "tslib": "^2.6.2" } }, "sha512-jGOOV/bV1DhkkUhHiZ3/1GZ67cZyOXaDb7d1rYD6ZiXf5V9tBNOcgqXwRRPvrCbYaFRa1pPMFb3ZjqjWpR3YfA=="],
"@aws/lambda-invoke-store": ["@aws/lambda-invoke-store@0.2.2", "", {}, "sha512-C0NBLsIqzDIae8HFw9YIrIBsbc0xTiOtt7fAukGPnqQ/+zZNaq+4jhuccltK0QuWHBnNm/a6kLIRA6GFiM10eg=="], "@aws/lambda-invoke-store": ["@aws/lambda-invoke-store@0.2.2", "", {}, "sha512-C0NBLsIqzDIae8HFw9YIrIBsbc0xTiOtt7fAukGPnqQ/+zZNaq+4jhuccltK0QuWHBnNm/a6kLIRA6GFiM10eg=="],
"@better-auth/core": ["@better-auth/core@1.4.17", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "zod": "^4.3.5" }, "peerDependencies": { "@better-auth/utils": "0.3.0", "@better-fetch/fetch": "1.1.21", "better-call": "1.1.8", "jose": "^6.1.0", "kysely": "^0.28.5", "nanostores": "^1.0.1" } }, "sha512-WSaEQDdUO6B1CzAmissN6j0lx9fM9lcslEYzlApB5UzFaBeAOHNUONTdglSyUs6/idiZBoRvt0t/qMXCgIU8ug=="], "@better-auth/core": ["@better-auth/core@1.4.18", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "zod": "^4.3.5" }, "peerDependencies": { "@better-auth/utils": "0.3.0", "@better-fetch/fetch": "1.1.21", "better-call": "1.1.8", "jose": "^6.1.0", "kysely": "^0.28.5", "nanostores": "^1.0.1" } }, "sha512-q+awYgC7nkLEBdx2sW0iJjkzgSHlIxGnOpsN1r/O1+a4m7osJNHtfK2mKJSL1I+GfNyIlxJF8WvD/NLuYMpmcg=="],
"@better-auth/telemetry": ["@better-auth/telemetry@1.4.17", "", { "dependencies": { "@better-auth/utils": "0.3.0", "@better-fetch/fetch": "1.1.21" }, "peerDependencies": { "@better-auth/core": "1.4.17" } }, "sha512-R1BC4e/bNjQbXu7lG6ubpgmsPj7IMqky5DvMlzAtnAJWJhh99pMh/n6w5gOHa0cqDZgEAuj75IPTxv+q3YiInA=="], "@better-auth/telemetry": ["@better-auth/telemetry@1.4.18", "", { "dependencies": { "@better-auth/utils": "0.3.0", "@better-fetch/fetch": "1.1.21" }, "peerDependencies": { "@better-auth/core": "1.4.18" } }, "sha512-e5rDF8S4j3Um/0LIVATL2in9dL4lfO2fr2v1Wio4qTMRbfxqnUDTa+6SZtwdeJrbc4O+a3c+IyIpjG9Q/6GpfQ=="],
"@better-auth/utils": ["@better-auth/utils@0.3.0", "", {}, "sha512-W+Adw6ZA6mgvnSnhOki270rwJ42t4XzSK6YWGF//BbVXL6SwCLWfyzBc1lN2m/4RM28KubdBKQ4X5VMoLRNPQw=="], "@better-auth/utils": ["@better-auth/utils@0.3.0", "", {}, "sha512-W+Adw6ZA6mgvnSnhOki270rwJ42t4XzSK6YWGF//BbVXL6SwCLWfyzBc1lN2m/4RM28KubdBKQ4X5VMoLRNPQw=="],
@ -328,23 +330,23 @@
"@mrleebo/prisma-ast": ["@mrleebo/prisma-ast@0.13.1", "", { "dependencies": { "chevrotain": "^10.5.0", "lilconfig": "^2.1.0" } }, "sha512-XyroGQXcHrZdvmrGJvsA9KNeOOgGMg1Vg9OlheUsBOSKznLMDl+YChxbkboRHvtFYJEMRYmlV3uoo/njCw05iw=="], "@mrleebo/prisma-ast": ["@mrleebo/prisma-ast@0.13.1", "", { "dependencies": { "chevrotain": "^10.5.0", "lilconfig": "^2.1.0" } }, "sha512-XyroGQXcHrZdvmrGJvsA9KNeOOgGMg1Vg9OlheUsBOSKznLMDl+YChxbkboRHvtFYJEMRYmlV3uoo/njCw05iw=="],
"@next/env": ["@next/env@16.1.4", "", {}, "sha512-gkrXnZyxPUy0Gg6SrPQPccbNVLSP3vmW8LU5dwEttEEC1RwDivk8w4O+sZIjFvPrSICXyhQDCG+y3VmjlJf+9A=="], "@next/env": ["@next/env@16.1.6", "", {}, "sha512-N1ySLuZjnAtN3kFnwhAwPvZah8RJxKasD7x1f8shFqhncnWZn4JMfg37diLNuoHsLAlrDfM3g4mawVdtAG8XLQ=="],
"@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@16.1.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-T8atLKuvk13XQUdVLCv1ZzMPgLPW0+DWWbHSQXs0/3TjPrKNxTmUIhOEaoEyl3Z82k8h/gEtqyuoZGv6+Ugawg=="], "@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@16.1.6", "", { "os": "darwin", "cpu": "arm64" }, "sha512-wTzYulosJr/6nFnqGW7FrG3jfUUlEf8UjGA0/pyypJl42ExdVgC6xJgcXQ+V8QFn6niSG2Pb8+MIG1mZr2vczw=="],
"@next/swc-darwin-x64": ["@next/swc-darwin-x64@16.1.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-AKC/qVjUGUQDSPI6gESTx0xOnOPQ5gttogNS3o6bA83yiaSZJek0Am5yXy82F1KcZCx3DdOwdGPZpQCluonuxg=="], "@next/swc-darwin-x64": ["@next/swc-darwin-x64@16.1.6", "", { "os": "darwin", "cpu": "x64" }, "sha512-BLFPYPDO+MNJsiDWbeVzqvYd4NyuRrEYVB5k2N3JfWncuHAy2IVwMAOlVQDFjj+krkWzhY2apvmekMkfQR0CUQ=="],
"@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@16.1.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-POQ65+pnYOkZNdngWfMEt7r53bzWiKkVNbjpmCt1Zb3V6lxJNXSsjwRuTQ8P/kguxDC8LRkqaL3vvsFrce4dMQ=="], "@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@16.1.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-OJYkCd5pj/QloBvoEcJ2XiMnlJkRv9idWA/j0ugSuA34gMT6f5b7vOiCQHVRpvStoZUknhl6/UxOXL4OwtdaBw=="],
"@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@16.1.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-3Wm0zGYVCs6qDFAiSSDL+Z+r46EdtCv/2l+UlIdMbAq9hPJBvGu/rZOeuvCaIUjbArkmXac8HnTyQPJFzFWA0Q=="], "@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@16.1.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-S4J2v+8tT3NIO9u2q+S0G5KdvNDjXfAv06OhfOzNDaBn5rw84DGXWndOEB7d5/x852A20sW1M56vhC/tRVbccQ=="],
"@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@16.1.4", "", { "os": "linux", "cpu": "x64" }, "sha512-lWAYAezFinaJiD5Gv8HDidtsZdT3CDaCeqoPoJjeB57OqzvMajpIhlZFce5sCAH6VuX4mdkxCRqecCJFwfm2nQ=="], "@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@16.1.6", "", { "os": "linux", "cpu": "x64" }, "sha512-2eEBDkFlMMNQnkTyPBhQOAyn2qMxyG2eE7GPH2WIDGEpEILcBPI/jdSv4t6xupSP+ot/jkfrCShLAa7+ZUPcJQ=="],
"@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@16.1.4", "", { "os": "linux", "cpu": "x64" }, "sha512-fHaIpT7x4gA6VQbdEpYUXRGyge/YbRrkG6DXM60XiBqDM2g2NcrsQaIuj375egnGFkJow4RHacgBOEsHfGbiUw=="], "@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@16.1.6", "", { "os": "linux", "cpu": "x64" }, "sha512-oicJwRlyOoZXVlxmIMaTq7f8pN9QNbdes0q2FXfRsPhfCi8n8JmOZJm5oo1pwDaFbnnD421rVU409M3evFbIqg=="],
"@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@16.1.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-MCrXxrTSE7jPN1NyXJr39E+aNFBrQZtO154LoCz7n99FuKqJDekgxipoodLNWdQP7/DZ5tKMc/efybx1l159hw=="], "@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@16.1.6", "", { "os": "win32", "cpu": "arm64" }, "sha512-gQmm8izDTPgs+DCWH22kcDmuUp7NyiJgEl18bcr8irXA5N2m2O+JQIr6f3ct42GOs9c0h8QF3L5SzIxcYAAXXw=="],
"@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@16.1.4", "", { "os": "win32", "cpu": "x64" }, "sha512-JSVlm9MDhmTXw/sO2PE/MRj+G6XOSMZB+BcZ0a7d6KwVFZVpkHcb2okyoYFBaco6LeiL53BBklRlOrDDbOeE5w=="], "@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@16.1.6", "", { "os": "win32", "cpu": "x64" }, "sha512-NRfO39AIrzBnixKbjuo2YiYhB6o9d8v/ymU9m/Xk8cyVk+k7XylniXkHwjs4s70wedVffc6bQNbufk5v0xEm0A=="],
"@noble/ciphers": ["@noble/ciphers@2.1.1", "", {}, "sha512-bysYuiVfhxNJuldNXlFEitTVdNnYUc+XNJZd7Qm2a5j1vZHgY+fazadNFWFaMK/2vye0JVlxV3gHmC0WDfAOQw=="], "@noble/ciphers": ["@noble/ciphers@2.1.1", "", {}, "sha512-bysYuiVfhxNJuldNXlFEitTVdNnYUc+XNJZd7Qm2a5j1vZHgY+fazadNFWFaMK/2vye0JVlxV3gHmC0WDfAOQw=="],
@ -496,7 +498,7 @@
"@smithy/config-resolver": ["@smithy/config-resolver@4.4.6", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.8", "@smithy/types": "^4.12.0", "@smithy/util-config-provider": "^4.2.0", "@smithy/util-endpoints": "^3.2.8", "@smithy/util-middleware": "^4.2.8", "tslib": "^2.6.2" } }, "sha512-qJpzYC64kaj3S0fueiu3kXm8xPrR3PcXDPEgnaNMRn0EjNSZFoFjvbUp0YUDsRhN1CB90EnHJtbxWKevnH99UQ=="], "@smithy/config-resolver": ["@smithy/config-resolver@4.4.6", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.8", "@smithy/types": "^4.12.0", "@smithy/util-config-provider": "^4.2.0", "@smithy/util-endpoints": "^3.2.8", "@smithy/util-middleware": "^4.2.8", "tslib": "^2.6.2" } }, "sha512-qJpzYC64kaj3S0fueiu3kXm8xPrR3PcXDPEgnaNMRn0EjNSZFoFjvbUp0YUDsRhN1CB90EnHJtbxWKevnH99UQ=="],
"@smithy/core": ["@smithy/core@3.21.1", "", { "dependencies": { "@smithy/middleware-serde": "^4.2.9", "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-middleware": "^4.2.8", "@smithy/util-stream": "^4.5.10", "@smithy/util-utf8": "^4.2.0", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" } }, "sha512-NUH8R4O6FkN8HKMojzbGg/5pNjsfTjlMmeFclyPfPaXXUrbr5TzhWgbf7t92wfrpCHRgpjyz7ffASIS3wX28aA=="], "@smithy/core": ["@smithy/core@3.22.0", "", { "dependencies": { "@smithy/middleware-serde": "^4.2.9", "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-middleware": "^4.2.8", "@smithy/util-stream": "^4.5.10", "@smithy/util-utf8": "^4.2.0", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" } }, "sha512-6vjCHD6vaY8KubeNw2Fg3EK0KLGQYdldG4fYgQmA0xSW0dJ8G2xFhSOdrlUakWVoP5JuWHtFODg3PNd/DN3FDA=="],
"@smithy/credential-provider-imds": ["@smithy/credential-provider-imds@4.2.8", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.8", "@smithy/property-provider": "^4.2.8", "@smithy/types": "^4.12.0", "@smithy/url-parser": "^4.2.8", "tslib": "^2.6.2" } }, "sha512-FNT0xHS1c/CPN8upqbMFP83+ul5YgdisfCfkZ86Jh2NSmnqw/AJ6x5pEogVCTVvSm7j9MopRU89bmDelxuDMYw=="], "@smithy/credential-provider-imds": ["@smithy/credential-provider-imds@4.2.8", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.8", "@smithy/property-provider": "^4.2.8", "@smithy/types": "^4.12.0", "@smithy/url-parser": "^4.2.8", "tslib": "^2.6.2" } }, "sha512-FNT0xHS1c/CPN8upqbMFP83+ul5YgdisfCfkZ86Jh2NSmnqw/AJ6x5pEogVCTVvSm7j9MopRU89bmDelxuDMYw=="],
@ -526,9 +528,9 @@
"@smithy/middleware-content-length": ["@smithy/middleware-content-length@4.2.8", "", { "dependencies": { "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-RO0jeoaYAB1qBRhfVyq0pMgBoUK34YEJxVxyjOWYZiOKOq2yMZ4MnVXMZCUDenpozHue207+9P5ilTV1zeda0A=="], "@smithy/middleware-content-length": ["@smithy/middleware-content-length@4.2.8", "", { "dependencies": { "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-RO0jeoaYAB1qBRhfVyq0pMgBoUK34YEJxVxyjOWYZiOKOq2yMZ4MnVXMZCUDenpozHue207+9P5ilTV1zeda0A=="],
"@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.4.11", "", { "dependencies": { "@smithy/core": "^3.21.1", "@smithy/middleware-serde": "^4.2.9", "@smithy/node-config-provider": "^4.3.8", "@smithy/shared-ini-file-loader": "^4.4.3", "@smithy/types": "^4.12.0", "@smithy/url-parser": "^4.2.8", "@smithy/util-middleware": "^4.2.8", "tslib": "^2.6.2" } }, "sha512-/WqsrycweGGfb9sSzME4CrsuayjJF6BueBmkKlcbeU5q18OhxRrvvKlmfw3tpDsK5ilx2XUJvoukwxHB0nHs/Q=="], "@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.4.12", "", { "dependencies": { "@smithy/core": "^3.22.0", "@smithy/middleware-serde": "^4.2.9", "@smithy/node-config-provider": "^4.3.8", "@smithy/shared-ini-file-loader": "^4.4.3", "@smithy/types": "^4.12.0", "@smithy/url-parser": "^4.2.8", "@smithy/util-middleware": "^4.2.8", "tslib": "^2.6.2" } }, "sha512-9JMKHVJtW9RysTNjcBZQHDwB0p3iTP6B1IfQV4m+uCevkVd/VuLgwfqk5cnI4RHcp4cPwoIvxQqN4B1sxeHo8Q=="],
"@smithy/middleware-retry": ["@smithy/middleware-retry@4.4.27", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.8", "@smithy/protocol-http": "^5.3.8", "@smithy/service-error-classification": "^4.2.8", "@smithy/smithy-client": "^4.10.12", "@smithy/types": "^4.12.0", "@smithy/util-middleware": "^4.2.8", "@smithy/util-retry": "^4.2.8", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" } }, "sha512-xFUYCGRVsfgiN5EjsJJSzih9+yjStgMTCLANPlf0LVQkPDYCe0hz97qbdTZosFOiYlGBlHYityGRxrQ/hxhfVQ=="], "@smithy/middleware-retry": ["@smithy/middleware-retry@4.4.29", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.8", "@smithy/protocol-http": "^5.3.8", "@smithy/service-error-classification": "^4.2.8", "@smithy/smithy-client": "^4.11.1", "@smithy/types": "^4.12.0", "@smithy/util-middleware": "^4.2.8", "@smithy/util-retry": "^4.2.8", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" } }, "sha512-bmTn75a4tmKRkC5w61yYQLb3DmxNzB8qSVu9SbTYqW6GAL0WXO2bDZuMAn/GJSbOdHEdjZvWxe+9Kk015bw6Cg=="],
"@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.9", "", { "dependencies": { "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-eMNiej0u/snzDvlqRGSN3Vl0ESn3838+nKyVfF2FKNXFbi4SERYT6PR392D39iczngbqqGG0Jl1DlCnp7tBbXQ=="], "@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.9", "", { "dependencies": { "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-eMNiej0u/snzDvlqRGSN3Vl0ESn3838+nKyVfF2FKNXFbi4SERYT6PR392D39iczngbqqGG0Jl1DlCnp7tBbXQ=="],
@ -552,7 +554,7 @@
"@smithy/signature-v4": ["@smithy/signature-v4@5.3.8", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-middleware": "^4.2.8", "@smithy/util-uri-escape": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-6A4vdGj7qKNRF16UIcO8HhHjKW27thsxYci+5r/uVRkdcBEkOEiY8OMPuydLX4QHSrJqGHPJzPRwwVTqbLZJhg=="], "@smithy/signature-v4": ["@smithy/signature-v4@5.3.8", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-middleware": "^4.2.8", "@smithy/util-uri-escape": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-6A4vdGj7qKNRF16UIcO8HhHjKW27thsxYci+5r/uVRkdcBEkOEiY8OMPuydLX4QHSrJqGHPJzPRwwVTqbLZJhg=="],
"@smithy/smithy-client": ["@smithy/smithy-client@4.10.12", "", { "dependencies": { "@smithy/core": "^3.21.1", "@smithy/middleware-endpoint": "^4.4.11", "@smithy/middleware-stack": "^4.2.8", "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "@smithy/util-stream": "^4.5.10", "tslib": "^2.6.2" } }, "sha512-VKO/HKoQ5OrSHW6AJUmEnUKeXI1/5LfCwO9cwyao7CmLvGnZeM1i36Lyful3LK1XU7HwTVieTqO1y2C/6t3qtA=="], "@smithy/smithy-client": ["@smithy/smithy-client@4.11.1", "", { "dependencies": { "@smithy/core": "^3.22.0", "@smithy/middleware-endpoint": "^4.4.12", "@smithy/middleware-stack": "^4.2.8", "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "@smithy/util-stream": "^4.5.10", "tslib": "^2.6.2" } }, "sha512-SERgNg5Z1U+jfR6/2xPYjSEHY1t3pyTHC/Ma3YQl6qWtmiL42bvNId3W/oMUWIwu7ekL2FMPdqAmwbQegM7HeQ=="],
"@smithy/types": ["@smithy/types@4.12.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-9YcuJVTOBDjg9LWo23Qp0lTQ3D7fQsQtwle0jVfpbUHy9qBwCEgKuVH4FqFB3VYu0nwdHKiEMA+oXz7oV8X1kw=="], "@smithy/types": ["@smithy/types@4.12.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-9YcuJVTOBDjg9LWo23Qp0lTQ3D7fQsQtwle0jVfpbUHy9qBwCEgKuVH4FqFB3VYu0nwdHKiEMA+oXz7oV8X1kw=="],
@ -568,9 +570,9 @@
"@smithy/util-config-provider": ["@smithy/util-config-provider@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q=="], "@smithy/util-config-provider": ["@smithy/util-config-provider@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q=="],
"@smithy/util-defaults-mode-browser": ["@smithy/util-defaults-mode-browser@4.3.26", "", { "dependencies": { "@smithy/property-provider": "^4.2.8", "@smithy/smithy-client": "^4.10.12", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-vva0dzYUTgn7DdE0uaha10uEdAgmdLnNFowKFjpMm6p2R0XDk5FHPX3CBJLzWQkQXuEprsb0hGz9YwbicNWhjw=="], "@smithy/util-defaults-mode-browser": ["@smithy/util-defaults-mode-browser@4.3.28", "", { "dependencies": { "@smithy/property-provider": "^4.2.8", "@smithy/smithy-client": "^4.11.1", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-/9zcatsCao9h6g18p/9vH9NIi5PSqhCkxQ/tb7pMgRFnqYp9XUOyOlGPDMHzr8n5ih6yYgwJEY2MLEobUgi47w=="],
"@smithy/util-defaults-mode-node": ["@smithy/util-defaults-mode-node@4.2.29", "", { "dependencies": { "@smithy/config-resolver": "^4.4.6", "@smithy/credential-provider-imds": "^4.2.8", "@smithy/node-config-provider": "^4.3.8", "@smithy/property-provider": "^4.2.8", "@smithy/smithy-client": "^4.10.12", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-c6D7IUBsZt/aNnTBHMTf+OVh+h/JcxUUgfTcIJaWRe6zhOum1X+pNKSZtZ+7fbOn5I99XVFtmrnXKv8yHHErTQ=="], "@smithy/util-defaults-mode-node": ["@smithy/util-defaults-mode-node@4.2.31", "", { "dependencies": { "@smithy/config-resolver": "^4.4.6", "@smithy/credential-provider-imds": "^4.2.8", "@smithy/node-config-provider": "^4.3.8", "@smithy/property-provider": "^4.2.8", "@smithy/smithy-client": "^4.11.1", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-JTvoApUXA5kbpceI2vuqQzRjeTbLpx1eoa5R/YEZbTgtxvIB7AQZxFJ0SEyfCpgPCyVV9IT7we+ytSeIB3CyWA=="],
"@smithy/util-endpoints": ["@smithy/util-endpoints@3.2.8", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-8JaVTn3pBDkhZgHQ8R0epwWt+BqPSLCjdjXXusK1onwJlRuN69fbvSK66aIKKO7SwVFM6x2J2ox5X8pOaWcUEw=="], "@smithy/util-endpoints": ["@smithy/util-endpoints@3.2.8", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-8JaVTn3pBDkhZgHQ8R0epwWt+BqPSLCjdjXXusK1onwJlRuN69fbvSK66aIKKO7SwVFM6x2J2ox5X8pOaWcUEw=="],
@ -654,11 +656,11 @@
"@types/node": ["@types/node@20.19.30", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-WJtwWJu7UdlvzEAUm484QNg5eAoq5QR08KDNx7g45Usrs2NtOPiX8ugDqmKdXkyL03rBqU5dYNYVQetEpBHq2g=="], "@types/node": ["@types/node@20.19.30", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-WJtwWJu7UdlvzEAUm484QNg5eAoq5QR08KDNx7g45Usrs2NtOPiX8ugDqmKdXkyL03rBqU5dYNYVQetEpBHq2g=="],
"@types/nodemailer": ["@types/nodemailer@7.0.5", "", { "dependencies": { "@aws-sdk/client-sesv2": "^3.839.0", "@types/node": "*" } }, "sha512-7WtR4MFJUNN2UFy0NIowBRJswj5KXjXDhlZY43Hmots5eGu5q/dTeFd/I6GgJA/qj3RqO6dDy4SvfcV3fOVeIA=="], "@types/nodemailer": ["@types/nodemailer@7.0.9", "", { "dependencies": { "@types/node": "*" } }, "sha512-vI8oF1M+8JvQhsId0Pc38BdUP2evenIIys7c7p+9OZXSPOH5c1dyINP1jT8xQ2xPuBUXmIC87s+91IZMDjH8Ow=="],
"@types/pg": ["@types/pg@8.16.0", "", { "dependencies": { "@types/node": "*", "pg-protocol": "*", "pg-types": "^2.2.0" } }, "sha512-RmhMd/wD+CF8Dfo+cVIy3RR5cl8CyfXQ0tGgW6XBL8L4LM/UTEbNXYRbLwU6w+CgrKBNbrQWt4FUtTfaU5jSYQ=="], "@types/pg": ["@types/pg@8.16.0", "", { "dependencies": { "@types/node": "*", "pg-protocol": "*", "pg-types": "^2.2.0" } }, "sha512-RmhMd/wD+CF8Dfo+cVIy3RR5cl8CyfXQ0tGgW6XBL8L4LM/UTEbNXYRbLwU6w+CgrKBNbrQWt4FUtTfaU5jSYQ=="],
"@types/react": ["@types/react@19.2.9", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-Lpo8kgb/igvMIPeNV2rsYKTgaORYdO1XGVZ4Qz3akwOj0ySGYMPlQWa8BaLn0G63D1aSaAQ5ldR06wCpChQCjA=="], "@types/react": ["@types/react@19.2.10", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-WPigyYuGhgZ/cTPRXB2EwUw+XvsRA3GqHlsP4qteqrnnjDrApbS7MxcGr/hke5iUoeB7E/gQtrs9I37zAJ0Vjw=="],
"@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="], "@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="],
@ -674,27 +676,27 @@
"@udecode/utils": ["@udecode/utils@52.0.1", "", {}, "sha512-T/qa4++J2MMY9CbhZ05ONpcpkvsRV0uBvOfMhhYWKkAND4DSVAVzmZTV7WQ5fjGq81agAped0WQxcqsEg617uQ=="], "@udecode/utils": ["@udecode/utils@52.0.1", "", {}, "sha512-T/qa4++J2MMY9CbhZ05ONpcpkvsRV0uBvOfMhhYWKkAND4DSVAVzmZTV7WQ5fjGq81agAped0WQxcqsEg617uQ=="],
"@vibrant/color": ["@vibrant/color@4.0.0", "", {}, "sha512-S9ItdqS1135wTXoIIqAJu8df9dqlOo6Boc5Y4MGsBTu9UmUOvOwfj5b4Ga6S5yrLAKmKYIactkz7zYJdMddkig=="], "@vibrant/color": ["@vibrant/color@4.0.4", "", {}, "sha512-Fq2tAszz4QOPWfHZ+KuEAchXUD8i594BM2fOJt8dI/fvYbiVoBycBF/BlNH6F4IWBubxXoPqD4JmmAHvFYbNew=="],
"@vibrant/core": ["@vibrant/core@4.0.0", "", { "dependencies": { "@vibrant/color": "^4.0.0", "@vibrant/generator": "^4.0.0", "@vibrant/image": "^4.0.0", "@vibrant/quantizer": "^4.0.0", "@vibrant/worker": "^4.0.0" } }, "sha512-fqlVRUTDjEws9VNKvI3cDXM4wUT7fMFS+cVqEjJk3im+R5EvjJzPF6OAbNhfPzW04NvHNE555eY9FfhYuX3PRw=="], "@vibrant/core": ["@vibrant/core@4.0.4", "", { "dependencies": { "@vibrant/color": "^4.0.4", "@vibrant/generator": "^4.0.4", "@vibrant/image": "^4.0.4", "@vibrant/quantizer": "^4.0.4", "@vibrant/worker": "^4.0.4" } }, "sha512-yZ0XSpW2biKyaJPpBC31AVYgn7NseKSO2q3KNMmDrkL2qC6TEWsBMnSQ28n0m///chZELXpQLx1CCOsWg5pj8w=="],
"@vibrant/generator": ["@vibrant/generator@4.0.0", "", { "dependencies": { "@vibrant/color": "^4.0.0", "@vibrant/types": "^4.0.0" } }, "sha512-CqKAjmgHVDXJVo3Q5+9pUJOvksR7cN3bzx/6MbURYh7lA4rhsIewkUK155M6q0vfcUN3ETi/eTneCi0tLuM2Sg=="], "@vibrant/generator": ["@vibrant/generator@4.0.4", "", { "dependencies": { "@vibrant/color": "^4.0.4", "@vibrant/types": "^4.0.4" } }, "sha512-rwq8PnlpKdch4YqaA1FAwdm71gKE2cMrUsbu72TqRFGa8rpP1roaZlQCVXIIwElXVc3r9axZyAcqyTLaMjhrTg=="],
"@vibrant/generator-default": ["@vibrant/generator-default@4.0.3", "", { "dependencies": { "@vibrant/color": "^4.0.0", "@vibrant/generator": "^4.0.0" } }, "sha512-HZlfp19sDokODEkZF4p70QceARHgjP3a1Dmxg+dlblYMJM98jPq+azA0fzqKNR7R17JJNHxexpJEepEsNlG0gw=="], "@vibrant/generator-default": ["@vibrant/generator-default@4.0.4", "", { "dependencies": { "@vibrant/color": "^4.0.4", "@vibrant/generator": "^4.0.4" } }, "sha512-QeVDeH2dz9lityvJCb84Ml4hlBTElwCpU7SVpiDFBh6gPoCLnzcb1H9G4NgG3hOlAPyrBM+Ivq1Pg+1lZj5Ywg=="],
"@vibrant/image": ["@vibrant/image@4.0.0", "", { "dependencies": { "@vibrant/color": "^4.0.0" } }, "sha512-Asv/7R/L701norosgvbjOVkodFiwcFihkXixA/gbAd6C+5GCts1Wm1NPk14FNKnM7eKkfAN+0wwPkdOH+PY/YA=="], "@vibrant/image": ["@vibrant/image@4.0.4", "", { "dependencies": { "@vibrant/color": "^4.0.4" } }, "sha512-NBIJj7umfDRVpFjJHQo1AFSCWCzQyjfil+Yxu7W62PEL72GPCif0CDiglPkvVF8QhDLmnx/x1k3LIBb9jWF2sw=="],
"@vibrant/image-browser": ["@vibrant/image-browser@4.0.0", "", { "dependencies": { "@vibrant/image": "^4.0.0" } }, "sha512-mXckzvJWiP575Y/wNtP87W/TPgyJoGlPBjW4E9YmNS6n4Jb6RqyHQA0ZVulqDslOxjSsihDzY7gpAORRclaoLg=="], "@vibrant/image-browser": ["@vibrant/image-browser@4.0.4", "", { "dependencies": { "@vibrant/image": "^4.0.4" } }, "sha512-7qVyAm+z9t98iwMDzUgGCwgRg0KBB5RXQFgiO2Um5Izd1wO7BKC0SHVEz2k7sRx3XNfBf+JExp8quPrvSz17gg=="],
"@vibrant/image-node": ["@vibrant/image-node@4.0.0", "", { "dependencies": { "@jimp/custom": "^0.22.12", "@jimp/plugin-resize": "^0.22.12", "@jimp/types": "^0.22.12", "@vibrant/image": "^4.0.0" } }, "sha512-m7yfnQtmo2y8z+tOjRFBx6q/qGnhl/ax2uCaj4TBkm4TtXfR4Dsn90wT6OWXmCFFzxIKHXKKEBShkxR+4RHseA=="], "@vibrant/image-node": ["@vibrant/image-node@4.0.4", "", { "dependencies": { "@jimp/custom": "^0.22.12", "@jimp/plugin-resize": "^0.22.12", "@jimp/types": "^0.22.12", "@vibrant/image": "^4.0.4" } }, "sha512-aG8Ukt9oTa6FWaAV5oBKsBetkKASWH31hZiFJ2R1291f3TZlphUyQTJz5TubucIRsCEl4dgG1xyxFPgse2IABA=="],
"@vibrant/quantizer": ["@vibrant/quantizer@4.0.0", "", { "dependencies": { "@vibrant/color": "^4.0.0", "@vibrant/image": "^4.0.0", "@vibrant/types": "^4.0.0" } }, "sha512-YDGxmCv/RvHFtZghDlVRwH5GMxdGGozWS1JpUOUt73/F5zAKGiiier8F31K1npIXARn6/Gspvg/Rbg7qqyEr2A=="], "@vibrant/quantizer": ["@vibrant/quantizer@4.0.4", "", { "dependencies": { "@vibrant/color": "^4.0.4", "@vibrant/image": "^4.0.4", "@vibrant/types": "^4.0.4" } }, "sha512-722CooC2W4mlBiv+zyAsIrIvARnMCN/P2Muo8bnWd0SQlVWFtQnFxJWGOUPOPS4DGe3pGoqmNfvS0let4dICZQ=="],
"@vibrant/quantizer-mmcq": ["@vibrant/quantizer-mmcq@4.0.0", "", { "dependencies": { "@vibrant/color": "^4.0.0", "@vibrant/image": "^4.0.0", "@vibrant/quantizer": "^4.0.0" } }, "sha512-TZqNiRoGGyCP8fH1XE6rvhFwLNv9D8MP1Xhz3K8tsuUweC6buWax3qLfrfEnkhtQnPJHaqvTfTOlIIXVMfRpow=="], "@vibrant/quantizer-mmcq": ["@vibrant/quantizer-mmcq@4.0.4", "", { "dependencies": { "@vibrant/color": "^4.0.4", "@vibrant/image": "^4.0.4", "@vibrant/quantizer": "^4.0.4" } }, "sha512-/1CNnM96J8K+OBCWNUzywo6VdnmdFJyiKO+ty/nkfe8H0NseOEHIL7PrVtWGgtsb0rh2uTAq2rjXv65TfgPy8g=="],
"@vibrant/types": ["@vibrant/types@4.0.0", "", {}, "sha512-tA5TAbuROXcPkt+PWjmGfoaiEXyySVaNnCZovf6vXhCbMdrTTCQXvNCde2geiVl6YwtuU/Qrj9iZxS5jZ6yVIw=="], "@vibrant/types": ["@vibrant/types@4.0.4", "", {}, "sha512-Qq3mVTJamn7yD4OBgBEUKaxfDlm3sxBK55N7dH3XzI9Ey7KR00R06uwtqOcEJMsziWTEXdYN3VUlYaj2Tkt7hw=="],
"@vibrant/worker": ["@vibrant/worker@4.0.0", "", { "dependencies": { "@vibrant/types": "^4.0.0" } }, "sha512-nSaZZwWQKOgN/nPYUAIRF0/uoa7KpK91A+gjLmZZDgfN1enqxaiihmn+75ayNadW0c6cxAEpEFEHTONR5u9tMw=="], "@vibrant/worker": ["@vibrant/worker@4.0.4", "", { "dependencies": { "@vibrant/types": "^4.0.4" } }, "sha512-Q/R6PYhSMWCXEk/IcXbWIzIu7Z4b58ABkGvcdF8Y+q/7g+KnpxKW5x/jfQ/6ciyYSby13wZZoEdNr3QQVgsdBQ=="],
"abort-controller": ["abort-controller@3.0.0", "", { "dependencies": { "event-target-shim": "^5.0.0" } }, "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg=="], "abort-controller": ["abort-controller@3.0.0", "", { "dependencies": { "event-target-shim": "^5.0.0" } }, "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg=="],
@ -730,7 +732,7 @@
"baseline-browser-mapping": ["baseline-browser-mapping@2.9.11", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-Sg0xJUNDU1sJNGdfGWhVHX0kkZ+HWcvmVymJbj6NSgZZmW/8S9Y2HQ5euytnIgakgxN6papOAWiwDo1ctFDcoQ=="], "baseline-browser-mapping": ["baseline-browser-mapping@2.9.11", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-Sg0xJUNDU1sJNGdfGWhVHX0kkZ+HWcvmVymJbj6NSgZZmW/8S9Y2HQ5euytnIgakgxN6papOAWiwDo1ctFDcoQ=="],
"better-auth": ["better-auth@1.4.17", "", { "dependencies": { "@better-auth/core": "1.4.17", "@better-auth/telemetry": "1.4.17", "@better-auth/utils": "0.3.0", "@better-fetch/fetch": "1.1.21", "@noble/ciphers": "^2.0.0", "@noble/hashes": "^2.0.0", "better-call": "1.1.8", "defu": "^6.1.4", "jose": "^6.1.0", "kysely": "^0.28.5", "nanostores": "^1.0.1", "zod": "^4.3.5" }, "peerDependencies": { "@lynx-js/react": "*", "@prisma/client": "^5.0.0 || ^6.0.0 || ^7.0.0", "@sveltejs/kit": "^2.0.0", "@tanstack/react-start": "^1.0.0", "@tanstack/solid-start": "^1.0.0", "better-sqlite3": "^12.0.0", "drizzle-kit": ">=0.31.4", "drizzle-orm": ">=0.41.0", "mongodb": "^6.0.0 || ^7.0.0", "mysql2": "^3.0.0", "next": "^14.0.0 || ^15.0.0 || ^16.0.0", "pg": "^8.0.0", "prisma": "^5.0.0 || ^6.0.0 || ^7.0.0", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0", "solid-js": "^1.0.0", "svelte": "^4.0.0 || ^5.0.0", "vitest": "^2.0.0 || ^3.0.0 || ^4.0.0", "vue": "^3.0.0" }, "optionalPeers": ["@lynx-js/react", "@prisma/client", "@sveltejs/kit", "@tanstack/react-start", "@tanstack/solid-start", "better-sqlite3", "drizzle-kit", "drizzle-orm", "mongodb", "mysql2", "next", "pg", "prisma", "react", "react-dom", "solid-js", "svelte", "vitest", "vue"] }, "sha512-VmHGQyKsEahkEs37qguROKg/6ypYpNF13D7v/lkbO7w7Aivz0Bv2h+VyUkH4NzrGY0QBKXi1577mGhDCVwp0ew=="], "better-auth": ["better-auth@1.4.18", "", { "dependencies": { "@better-auth/core": "1.4.18", "@better-auth/telemetry": "1.4.18", "@better-auth/utils": "0.3.0", "@better-fetch/fetch": "1.1.21", "@noble/ciphers": "^2.0.0", "@noble/hashes": "^2.0.0", "better-call": "1.1.8", "defu": "^6.1.4", "jose": "^6.1.0", "kysely": "^0.28.5", "nanostores": "^1.0.1", "zod": "^4.3.5" }, "peerDependencies": { "@lynx-js/react": "*", "@prisma/client": "^5.0.0 || ^6.0.0 || ^7.0.0", "@sveltejs/kit": "^2.0.0", "@tanstack/react-start": "^1.0.0", "@tanstack/solid-start": "^1.0.0", "better-sqlite3": "^12.0.0", "drizzle-kit": ">=0.31.4", "drizzle-orm": ">=0.41.0", "mongodb": "^6.0.0 || ^7.0.0", "mysql2": "^3.0.0", "next": "^14.0.0 || ^15.0.0 || ^16.0.0", "pg": "^8.0.0", "prisma": "^5.0.0 || ^6.0.0 || ^7.0.0", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0", "solid-js": "^1.0.0", "svelte": "^4.0.0 || ^5.0.0", "vitest": "^2.0.0 || ^3.0.0 || ^4.0.0", "vue": "^3.0.0" }, "optionalPeers": ["@lynx-js/react", "@prisma/client", "@sveltejs/kit", "@tanstack/react-start", "@tanstack/solid-start", "better-sqlite3", "drizzle-kit", "drizzle-orm", "mongodb", "mysql2", "next", "pg", "prisma", "react", "react-dom", "solid-js", "svelte", "vitest", "vue"] }, "sha512-bnyifLWBPcYVltH3RhS7CM62MoelEqC6Q+GnZwfiDWNfepXoQZBjEvn4urcERC7NTKgKq5zNBM8rvPvRBa6xcg=="],
"better-call": ["better-call@1.1.8", "", { "dependencies": { "@better-auth/utils": "^0.3.0", "@better-fetch/fetch": "^1.1.4", "rou3": "^0.7.10", "set-cookie-parser": "^2.7.1" }, "peerDependencies": { "zod": "^4.0.0" }, "optionalPeers": ["zod"] }, "sha512-XMQ2rs6FNXasGNfMjzbyroSwKwYbZ/T3IxruSS6U2MJRsSYh3wYtG3o6H00ZlKZ/C/UPOAD97tqgQJNsxyeTXw=="], "better-call": ["better-call@1.1.8", "", { "dependencies": { "@better-auth/utils": "^0.3.0", "@better-fetch/fetch": "^1.1.4", "rou3": "^0.7.10", "set-cookie-parser": "^2.7.1" }, "peerDependencies": { "zod": "^4.0.0" }, "optionalPeers": ["zod"] }, "sha512-XMQ2rs6FNXasGNfMjzbyroSwKwYbZ/T3IxruSS6U2MJRsSYh3wYtG3o6H00ZlKZ/C/UPOAD97tqgQJNsxyeTXw=="],
@ -1114,7 +1116,7 @@
"nanostores": ["nanostores@1.1.0", "", {}, "sha512-yJBmDJr18xy47dbNVlHcgdPrulSn1nhSE6Ns9vTG+Nx9VPT6iV1MD6aQFp/t52zpf82FhLLTXAXr30NuCnxvwA=="], "nanostores": ["nanostores@1.1.0", "", {}, "sha512-yJBmDJr18xy47dbNVlHcgdPrulSn1nhSE6Ns9vTG+Nx9VPT6iV1MD6aQFp/t52zpf82FhLLTXAXr30NuCnxvwA=="],
"next": ["next@16.1.4", "", { "dependencies": { "@next/env": "16.1.4", "@swc/helpers": "0.5.15", "baseline-browser-mapping": "^2.8.3", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "16.1.4", "@next/swc-darwin-x64": "16.1.4", "@next/swc-linux-arm64-gnu": "16.1.4", "@next/swc-linux-arm64-musl": "16.1.4", "@next/swc-linux-x64-gnu": "16.1.4", "@next/swc-linux-x64-musl": "16.1.4", "@next/swc-win32-arm64-msvc": "16.1.4", "@next/swc-win32-x64-msvc": "16.1.4", "sharp": "^0.34.4" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-gKSecROqisnV7Buen5BfjmXAm7Xlpx9o2ueVQRo5DxQcjC8d330dOM1xiGWc2k3Dcnz0In3VybyRPOsudwgiqQ=="], "next": ["next@16.1.6", "", { "dependencies": { "@next/env": "16.1.6", "@swc/helpers": "0.5.15", "baseline-browser-mapping": "^2.8.3", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "16.1.6", "@next/swc-darwin-x64": "16.1.6", "@next/swc-linux-arm64-gnu": "16.1.6", "@next/swc-linux-arm64-musl": "16.1.6", "@next/swc-linux-x64-gnu": "16.1.6", "@next/swc-linux-x64-musl": "16.1.6", "@next/swc-win32-arm64-msvc": "16.1.6", "@next/swc-win32-x64-msvc": "16.1.6", "sharp": "^0.34.4" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-hkyRkcu5x/41KoqnROkfTm2pZVbKxvbZRuNvKXLRXxs3VfyO0WhY50TQS40EuKO9SW3rBj/sF3WbVwDACeMZyw=="],
"next-themes": ["next-themes@0.4.6", "", { "peerDependencies": { "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" } }, "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA=="], "next-themes": ["next-themes@0.4.6", "", { "peerDependencies": { "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" } }, "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA=="],
@ -1122,9 +1124,9 @@
"node-fetch-native": ["node-fetch-native@1.6.7", "", {}, "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q=="], "node-fetch-native": ["node-fetch-native@1.6.7", "", {}, "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q=="],
"node-vibrant": ["node-vibrant@4.0.3", "", { "dependencies": { "@types/node": "^18.15.3", "@vibrant/core": "^4.0.0", "@vibrant/generator-default": "^4.0.3", "@vibrant/image-browser": "^4.0.0", "@vibrant/image-node": "^4.0.0", "@vibrant/quantizer-mmcq": "^4.0.0" } }, "sha512-kzoIuJK90BH/k65Avt077JCX4Nhqz1LNc8cIOm2rnYEvFdJIYd8b3SQwU1MTpzcHtr8z8jxkl1qdaCfbP3olFg=="], "node-vibrant": ["node-vibrant@4.0.4", "", { "dependencies": { "@types/node": "^18.15.3", "@vibrant/core": "^4.0.4", "@vibrant/generator-default": "^4.0.4", "@vibrant/image-browser": "^4.0.4", "@vibrant/image-node": "^4.0.4", "@vibrant/quantizer-mmcq": "^4.0.4" } }, "sha512-hA/pUXBE9TJ41G9FlTkzeqD5JdxgvvPGYZb/HNpdkaxxXUEnP36imSolZ644JuPun+lTd+FpWWtBpTYdp2noQA=="],
"nodemailer": ["nodemailer@7.0.12", "", {}, "sha512-H+rnK5bX2Pi/6ms3sN4/jRQvYSMltV6vqup/0SFOrxYYY/qoNvhXPlYq3e+Pm9RFJRwrMGbMIwi81M4dxpomhA=="], "nodemailer": ["nodemailer@7.0.13", "", {}, "sha512-PNDFSJdP+KFgdsG3ZzMXCgquO7I6McjY2vlqILjtJd0hy8wEvtugS9xKRF2NWlPNGxvLCXlTNIae4serI7dinw=="],
"normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="], "normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="],
@ -1152,11 +1154,11 @@
"perfect-debounce": ["perfect-debounce@1.0.0", "", {}, "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA=="], "perfect-debounce": ["perfect-debounce@1.0.0", "", {}, "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA=="],
"pg": ["pg@8.17.2", "", { "dependencies": { "pg-connection-string": "^2.10.1", "pg-pool": "^3.11.0", "pg-protocol": "^1.11.0", "pg-types": "2.2.0", "pgpass": "1.0.5" }, "optionalDependencies": { "pg-cloudflare": "^1.3.0" }, "peerDependencies": { "pg-native": ">=3.0.1" }, "optionalPeers": ["pg-native"] }, "sha512-vjbKdiBJRqzcYw1fNU5KuHyYvdJ1qpcQg1CeBrHFqV1pWgHeVR6j/+kX0E1AAXfyuLUGY1ICrN2ELKA/z2HWzw=="], "pg": ["pg@8.18.0", "", { "dependencies": { "pg-connection-string": "^2.11.0", "pg-pool": "^3.11.0", "pg-protocol": "^1.11.0", "pg-types": "2.2.0", "pgpass": "1.0.5" }, "optionalDependencies": { "pg-cloudflare": "^1.3.0" }, "peerDependencies": { "pg-native": ">=3.0.1" }, "optionalPeers": ["pg-native"] }, "sha512-xqrUDL1b9MbkydY/s+VZ6v+xiMUmOUk7SS9d/1kpyQxoJ6U9AO1oIJyUWVZojbfe5Cc/oluutcgFG4L9RDP1iQ=="],
"pg-cloudflare": ["pg-cloudflare@1.3.0", "", {}, "sha512-6lswVVSztmHiRtD6I8hw4qP/nDm1EJbKMRhf3HCYaqud7frGysPv7FYJ5noZQdhQtN2xJnimfMtvQq21pdbzyQ=="], "pg-cloudflare": ["pg-cloudflare@1.3.0", "", {}, "sha512-6lswVVSztmHiRtD6I8hw4qP/nDm1EJbKMRhf3HCYaqud7frGysPv7FYJ5noZQdhQtN2xJnimfMtvQq21pdbzyQ=="],
"pg-connection-string": ["pg-connection-string@2.10.1", "", {}, "sha512-iNzslsoeSH2/gmDDKiyMqF64DATUCWj3YJ0wP14kqcsf2TUklwimd+66yYojKwZCA7h2yRNLGug71hCBA2a4sw=="], "pg-connection-string": ["pg-connection-string@2.11.0", "", {}, "sha512-kecgoJwhOpxYU21rZjULrmrBJ698U2RxXofKVzOn5UDj61BPj/qMb7diYUR1nLScCDbrztQFl1TaQZT0t1EtzQ=="],
"pg-int8": ["pg-int8@1.0.1", "", {}, "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw=="], "pg-int8": ["pg-int8@1.0.1", "", {}, "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw=="],
@ -1204,13 +1206,13 @@
"rc9": ["rc9@2.1.2", "", { "dependencies": { "defu": "^6.1.4", "destr": "^2.0.3" } }, "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg=="], "rc9": ["rc9@2.1.2", "", { "dependencies": { "defu": "^6.1.4", "destr": "^2.0.3" } }, "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg=="],
"react": ["react@19.2.1", "", {}, "sha512-DGrYcCWK7tvYMnWh79yrPHt+vdx9tY+1gPZa7nJQtO/p8bLTDaHp4dzwEhQB7pZ4Xe3ok4XKuEPrVuc+wlpkmw=="], "react": ["react@19.2.4", "", {}, "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ=="],
"react-compiler-runtime": ["react-compiler-runtime@1.0.0", "", { "peerDependencies": { "react": "^17.0.0 || ^18.0.0 || ^19.0.0 || ^0.0.0-experimental" } }, "sha512-rRfjYv66HlG8896yPUDONgKzG5BxZD1nV9U6rkm+7VCuvQc903C4MjcoZR4zPw53IKSOX9wMQVpA1IAbRtzQ7w=="], "react-compiler-runtime": ["react-compiler-runtime@1.0.0", "", { "peerDependencies": { "react": "^17.0.0 || ^18.0.0 || ^19.0.0 || ^0.0.0-experimental" } }, "sha512-rRfjYv66HlG8896yPUDONgKzG5BxZD1nV9U6rkm+7VCuvQc903C4MjcoZR4zPw53IKSOX9wMQVpA1IAbRtzQ7w=="],
"react-day-picker": ["react-day-picker@9.13.0", "", { "dependencies": { "@date-fns/tz": "^1.4.1", "date-fns": "^4.1.0", "date-fns-jalali": "^4.1.0-0" }, "peerDependencies": { "react": ">=16.8.0" } }, "sha512-euzj5Hlq+lOHqI53NiuNhCP8HWgsPf/bBAVijR50hNaY1XwjKjShAnIe8jm8RD2W9IJUvihDIZ+KrmqfFzNhFQ=="], "react-day-picker": ["react-day-picker@9.13.0", "", { "dependencies": { "@date-fns/tz": "^1.4.1", "date-fns": "^4.1.0", "date-fns-jalali": "^4.1.0-0" }, "peerDependencies": { "react": ">=16.8.0" } }, "sha512-euzj5Hlq+lOHqI53NiuNhCP8HWgsPf/bBAVijR50hNaY1XwjKjShAnIe8jm8RD2W9IJUvihDIZ+KrmqfFzNhFQ=="],
"react-dom": ["react-dom@19.2.1", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.1" } }, "sha512-ibrK8llX2a4eOskq1mXKu/TGZj9qzomO+sNfO98M6d9zIPOEhlBkMkBUBLd1vgS0gQsLDBzA+8jJBVXDnfHmJg=="], "react-dom": ["react-dom@19.2.4", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.4" } }, "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ=="],
"react-hook-form": ["react-hook-form@7.71.1", "", { "peerDependencies": { "react": "^16.8.0 || ^17 || ^18 || ^19" } }, "sha512-9SUJKCGKo8HUSsCO+y0CtqkqI5nNuaDqTxyqPsZPqIwudpj4rCrAz/jZV+jn57bx5gtZKOh3neQu94DXMc+w5w=="], "react-hook-form": ["react-hook-form@7.71.1", "", { "peerDependencies": { "react": "^16.8.0 || ^17 || ^18 || ^19" } }, "sha512-9SUJKCGKo8HUSsCO+y0CtqkqI5nNuaDqTxyqPsZPqIwudpj4rCrAz/jZV+jn57bx5gtZKOh3neQu94DXMc+w5w=="],
@ -1426,82 +1428,12 @@
"@aws-crypto/util/@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="], "@aws-crypto/util/@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="],
"@aws-sdk/client-sesv2/@aws-sdk/core": ["@aws-sdk/core@3.957.0", "", { "dependencies": { "@aws-sdk/types": "3.957.0", "@aws-sdk/xml-builder": "3.957.0", "@smithy/core": "^3.20.0", "@smithy/node-config-provider": "^4.3.7", "@smithy/property-provider": "^4.2.7", "@smithy/protocol-http": "^5.3.7", "@smithy/signature-v4": "^5.3.7", "@smithy/smithy-client": "^4.10.2", "@smithy/types": "^4.11.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-middleware": "^4.2.7", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-DrZgDnF1lQZv75a52nFWs6MExihJF2GZB6ETZRqr6jMwhrk2kbJPUtvgbifwcL7AYmVqHQDJBrR/MqkwwFCpiw=="],
"@aws-sdk/client-sesv2/@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.958.0", "", { "dependencies": { "@aws-sdk/credential-provider-env": "3.957.0", "@aws-sdk/credential-provider-http": "3.957.0", "@aws-sdk/credential-provider-ini": "3.958.0", "@aws-sdk/credential-provider-process": "3.957.0", "@aws-sdk/credential-provider-sso": "3.958.0", "@aws-sdk/credential-provider-web-identity": "3.958.0", "@aws-sdk/types": "3.957.0", "@smithy/credential-provider-imds": "^4.2.7", "@smithy/property-provider": "^4.2.7", "@smithy/shared-ini-file-loader": "^4.4.2", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-vdoZbNG2dt66I7EpN3fKCzi6fp9xjIiwEA/vVVgqO4wXCGw8rKPIdDUus4e13VvTr330uQs2W0UNg/7AgtquEQ=="],
"@aws-sdk/client-sesv2/@aws-sdk/middleware-host-header": ["@aws-sdk/middleware-host-header@3.957.0", "", { "dependencies": { "@aws-sdk/types": "3.957.0", "@smithy/protocol-http": "^5.3.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-BBgKawVyfQZglEkNTuBBdC3azlyqNXsvvN4jPkWAiNYcY0x1BasaJFl+7u/HisfULstryweJq/dAvIZIxzlZaA=="],
"@aws-sdk/client-sesv2/@aws-sdk/middleware-logger": ["@aws-sdk/middleware-logger@3.957.0", "", { "dependencies": { "@aws-sdk/types": "3.957.0", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-w1qfKrSKHf9b5a8O76yQ1t69u6NWuBjr5kBX+jRWFx/5mu6RLpqERXRpVJxfosbep7k3B+DSB5tZMZ82GKcJtQ=="],
"@aws-sdk/client-sesv2/@aws-sdk/middleware-recursion-detection": ["@aws-sdk/middleware-recursion-detection@3.957.0", "", { "dependencies": { "@aws-sdk/types": "3.957.0", "@aws/lambda-invoke-store": "^0.2.2", "@smithy/protocol-http": "^5.3.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-D2H/WoxhAZNYX+IjkKTdOhOkWQaK0jjJrDBj56hKjU5c9ltQiaX/1PqJ4dfjHntEshJfu0w+E6XJ+/6A6ILBBA=="],
"@aws-sdk/client-sesv2/@aws-sdk/middleware-user-agent": ["@aws-sdk/middleware-user-agent@3.957.0", "", { "dependencies": { "@aws-sdk/core": "3.957.0", "@aws-sdk/types": "3.957.0", "@aws-sdk/util-endpoints": "3.957.0", "@smithy/core": "^3.20.0", "@smithy/protocol-http": "^5.3.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-50vcHu96XakQnIvlKJ1UoltrFODjsq2KvtTgHiPFteUS884lQnK5VC/8xd1Msz/1ONpLMzdCVproCQqhDTtMPQ=="],
"@aws-sdk/client-sesv2/@aws-sdk/region-config-resolver": ["@aws-sdk/region-config-resolver@3.957.0", "", { "dependencies": { "@aws-sdk/types": "3.957.0", "@smithy/config-resolver": "^4.4.5", "@smithy/node-config-provider": "^4.3.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-V8iY3blh8l2iaOqXWW88HbkY5jDoWjH56jonprG/cpyqqCnprvpMUZWPWYJoI8rHRf2bqzZeql1slxG6EnKI7A=="],
"@aws-sdk/client-sesv2/@aws-sdk/signature-v4-multi-region": ["@aws-sdk/signature-v4-multi-region@3.957.0", "", { "dependencies": { "@aws-sdk/middleware-sdk-s3": "3.957.0", "@aws-sdk/types": "3.957.0", "@smithy/protocol-http": "^5.3.7", "@smithy/signature-v4": "^5.3.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-t6UfP1xMUigMMzHcb7vaZcjv7dA2DQkk9C/OAP1dKyrE0vb4lFGDaTApi17GN6Km9zFxJthEMUbBc7DL0hq1Bg=="],
"@aws-sdk/client-sesv2/@aws-sdk/types": ["@aws-sdk/types@3.957.0", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-wzWC2Nrt859ABk6UCAVY/WYEbAd7FjkdrQL6m24+tfmWYDNRByTJ9uOgU/kw9zqLCAwb//CPvrJdhqjTznWXAg=="],
"@aws-sdk/client-sesv2/@aws-sdk/util-endpoints": ["@aws-sdk/util-endpoints@3.957.0", "", { "dependencies": { "@aws-sdk/types": "3.957.0", "@smithy/types": "^4.11.0", "@smithy/url-parser": "^4.2.7", "@smithy/util-endpoints": "^3.2.7", "tslib": "^2.6.2" } }, "sha512-xwF9K24mZSxcxKS3UKQFeX/dPYkEps9wF1b+MGON7EvnbcucrJGyQyK1v1xFPn1aqXkBTFi+SZaMRx5E5YCVFw=="],
"@aws-sdk/client-sesv2/@aws-sdk/util-user-agent-browser": ["@aws-sdk/util-user-agent-browser@3.957.0", "", { "dependencies": { "@aws-sdk/types": "3.957.0", "@smithy/types": "^4.11.0", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-exueuwxef0lUJRnGaVkNSC674eAiWU07ORhxBnevFFZEKisln+09Qrtw823iyv5I1N8T+wKfh95xvtWQrNKNQw=="],
"@aws-sdk/client-sesv2/@aws-sdk/util-user-agent-node": ["@aws-sdk/util-user-agent-node@3.957.0", "", { "dependencies": { "@aws-sdk/middleware-user-agent": "3.957.0", "@aws-sdk/types": "3.957.0", "@smithy/node-config-provider": "^4.3.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" }, "peerDependencies": { "aws-crt": ">=1.0.0" }, "optionalPeers": ["aws-crt"] }, "sha512-ycbYCwqXk4gJGp0Oxkzf2KBeeGBdTxz559D41NJP8FlzSej1Gh7Rk40Zo6AyTfsNWkrl/kVi1t937OIzC5t+9Q=="],
"@aws-sdk/client-sesv2/@smithy/config-resolver": ["@smithy/config-resolver@4.4.5", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.7", "@smithy/types": "^4.11.0", "@smithy/util-config-provider": "^4.2.0", "@smithy/util-endpoints": "^3.2.7", "@smithy/util-middleware": "^4.2.7", "tslib": "^2.6.2" } }, "sha512-HAGoUAFYsUkoSckuKbCPayECeMim8pOu+yLy1zOxt1sifzEbrsRpYa+mKcMdiHKMeiqOibyPG0sFJnmaV/OGEg=="],
"@aws-sdk/client-sesv2/@smithy/core": ["@smithy/core@3.20.0", "", { "dependencies": { "@smithy/middleware-serde": "^4.2.8", "@smithy/protocol-http": "^5.3.7", "@smithy/types": "^4.11.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-middleware": "^4.2.7", "@smithy/util-stream": "^4.5.8", "@smithy/util-utf8": "^4.2.0", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" } }, "sha512-WsSHCPq/neD5G/MkK4csLI5Y5Pkd9c1NMfpYEKeghSGaD4Ja1qLIohRQf2D5c1Uy5aXp76DeKHkzWZ9KAlHroQ=="],
"@aws-sdk/client-sesv2/@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.3.8", "", { "dependencies": { "@smithy/protocol-http": "^5.3.7", "@smithy/querystring-builder": "^4.2.7", "@smithy/types": "^4.11.0", "@smithy/util-base64": "^4.3.0", "tslib": "^2.6.2" } }, "sha512-h/Fi+o7mti4n8wx1SR6UHWLaakwHRx29sizvp8OOm7iqwKGFneT06GCSFhml6Bha5BT6ot5pj3CYZnCHhGC2Rg=="],
"@aws-sdk/client-sesv2/@smithy/hash-node": ["@smithy/hash-node@4.2.7", "", { "dependencies": { "@smithy/types": "^4.11.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-PU/JWLTBCV1c8FtB8tEFnY4eV1tSfBc7bDBADHfn1K+uRbPgSJ9jnJp0hyjiFN2PMdPzxsf1Fdu0eo9fJ760Xw=="],
"@aws-sdk/client-sesv2/@smithy/invalid-dependency": ["@smithy/invalid-dependency@4.2.7", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-ncvgCr9a15nPlkhIUx3CU4d7E7WEuVJOV7fS7nnK2hLtPK9tYRBkMHQbhXU1VvvKeBm/O0x26OEoBq+ngFpOEQ=="],
"@aws-sdk/client-sesv2/@smithy/middleware-content-length": ["@smithy/middleware-content-length@4.2.7", "", { "dependencies": { "@smithy/protocol-http": "^5.3.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-GszfBfCcvt7kIbJ41LuNa5f0wvQCHhnGx/aDaZJCCT05Ld6x6U2s0xsc/0mBFONBZjQJp2U/0uSJ178OXOwbhg=="],
"@aws-sdk/client-sesv2/@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.4.1", "", { "dependencies": { "@smithy/core": "^3.20.0", "@smithy/middleware-serde": "^4.2.8", "@smithy/node-config-provider": "^4.3.7", "@smithy/shared-ini-file-loader": "^4.4.2", "@smithy/types": "^4.11.0", "@smithy/url-parser": "^4.2.7", "@smithy/util-middleware": "^4.2.7", "tslib": "^2.6.2" } }, "sha512-gpLspUAoe6f1M6H0u4cVuFzxZBrsGZmjx2O9SigurTx4PbntYa4AJ+o0G0oGm1L2oSX6oBhcGHwrfJHup2JnJg=="],
"@aws-sdk/client-sesv2/@smithy/middleware-retry": ["@smithy/middleware-retry@4.4.17", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.7", "@smithy/protocol-http": "^5.3.7", "@smithy/service-error-classification": "^4.2.7", "@smithy/smithy-client": "^4.10.2", "@smithy/types": "^4.11.0", "@smithy/util-middleware": "^4.2.7", "@smithy/util-retry": "^4.2.7", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" } }, "sha512-MqbXK6Y9uq17h+4r0ogu/sBT6V/rdV+5NvYL7ZV444BKfQygYe8wAhDrVXagVebN6w2RE0Fm245l69mOsPGZzg=="],
"@aws-sdk/client-sesv2/@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.8", "", { "dependencies": { "@smithy/protocol-http": "^5.3.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-8rDGYen5m5+NV9eHv9ry0sqm2gI6W7mc1VSFMtn6Igo25S507/HaOX9LTHAS2/J32VXD0xSzrY0H5FJtOMS4/w=="],
"@aws-sdk/client-sesv2/@smithy/middleware-stack": ["@smithy/middleware-stack@4.2.7", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-bsOT0rJ+HHlZd9crHoS37mt8qRRN/h9jRve1SXUhVbkRzu0QaNYZp1i1jha4n098tsvROjcwfLlfvcFuJSXEsw=="],
"@aws-sdk/client-sesv2/@smithy/node-config-provider": ["@smithy/node-config-provider@4.3.7", "", { "dependencies": { "@smithy/property-provider": "^4.2.7", "@smithy/shared-ini-file-loader": "^4.4.2", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-7r58wq8sdOcrwWe+klL9y3bc4GW1gnlfnFOuL7CXa7UzfhzhxKuzNdtqgzmTV+53lEp9NXh5hY/S4UgjLOzPfw=="],
"@aws-sdk/client-sesv2/@smithy/node-http-handler": ["@smithy/node-http-handler@4.4.7", "", { "dependencies": { "@smithy/abort-controller": "^4.2.7", "@smithy/protocol-http": "^5.3.7", "@smithy/querystring-builder": "^4.2.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-NELpdmBOO6EpZtWgQiHjoShs1kmweaiNuETUpuup+cmm/xJYjT4eUjfhrXRP4jCOaAsS3c3yPsP3B+K+/fyPCQ=="],
"@aws-sdk/client-sesv2/@smithy/protocol-http": ["@smithy/protocol-http@5.3.7", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-1r07pb994I20dD/c2seaZhoCuNYm0rWrvBxhCQ70brNh11M5Ml2ew6qJVo0lclB3jMIXirD4s2XRXRe7QEi0xA=="],
"@aws-sdk/client-sesv2/@smithy/smithy-client": ["@smithy/smithy-client@4.10.2", "", { "dependencies": { "@smithy/core": "^3.20.0", "@smithy/middleware-endpoint": "^4.4.1", "@smithy/middleware-stack": "^4.2.7", "@smithy/protocol-http": "^5.3.7", "@smithy/types": "^4.11.0", "@smithy/util-stream": "^4.5.8", "tslib": "^2.6.2" } }, "sha512-D5z79xQWpgrGpAHb054Fn2CCTQZpog7JELbVQ6XAvXs5MNKWf28U9gzSBlJkOyMl9LA1TZEjRtwvGXfP0Sl90g=="],
"@aws-sdk/client-sesv2/@smithy/types": ["@smithy/types@4.11.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-mlrmL0DRDVe3mNrjTcVcZEgkFmufITfUAPBEA+AHYiIeYyJebso/He1qLbP3PssRe22KUzLRpQSdBPbXdgZ2VA=="],
"@aws-sdk/client-sesv2/@smithy/url-parser": ["@smithy/url-parser@4.2.7", "", { "dependencies": { "@smithy/querystring-parser": "^4.2.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-/RLtVsRV4uY3qPWhBDsjwahAtt3x2IsMGnP5W1b2VZIe+qgCqkLxI1UOHDZp1Q1QSOrdOR32MF3Ph2JfWT1VHg=="],
"@aws-sdk/client-sesv2/@smithy/util-defaults-mode-browser": ["@smithy/util-defaults-mode-browser@4.3.16", "", { "dependencies": { "@smithy/property-provider": "^4.2.7", "@smithy/smithy-client": "^4.10.2", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-/eiSP3mzY3TsvUOYMeL4EqUX6fgUOj2eUOU4rMMgVbq67TiRLyxT7Xsjxq0bW3OwuzK009qOwF0L2OgJqperAQ=="],
"@aws-sdk/client-sesv2/@smithy/util-defaults-mode-node": ["@smithy/util-defaults-mode-node@4.2.19", "", { "dependencies": { "@smithy/config-resolver": "^4.4.5", "@smithy/credential-provider-imds": "^4.2.7", "@smithy/node-config-provider": "^4.3.7", "@smithy/property-provider": "^4.2.7", "@smithy/smithy-client": "^4.10.2", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-3a4+4mhf6VycEJyHIQLypRbiwG6aJvbQAeRAVXydMmfweEPnLLabRbdyo/Pjw8Rew9vjsh5WCdhmDaHkQnhhhA=="],
"@aws-sdk/client-sesv2/@smithy/util-endpoints": ["@smithy/util-endpoints@3.2.7", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-s4ILhyAvVqhMDYREeTS68R43B1V5aenV5q/V1QpRQJkCXib5BPRo4s7uNdzGtIKxaPHCfU/8YkvPAEvTpxgspg=="],
"@aws-sdk/client-sesv2/@smithy/util-middleware": ["@smithy/util-middleware@4.2.7", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-i1IkpbOae6NvIKsEeLLM9/2q4X+M90KV3oCFgWQI4q0Qz+yUZvsr+gZPdAEAtFhWQhAHpTsJO8DRJPuwVyln+w=="],
"@aws-sdk/client-sesv2/@smithy/util-retry": ["@smithy/util-retry@4.2.7", "", { "dependencies": { "@smithy/service-error-classification": "^4.2.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-SvDdsQyF5CIASa4EYVT02LukPHVzAgUA4kMAuZ97QJc2BpAqZfA4PINB8/KOoCXEw9tsuv/jQjMeaHFvxdLNGg=="],
"@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3": ["@aws-sdk/middleware-sdk-s3@3.972.0", "", { "dependencies": { "@aws-sdk/core": "3.972.0", "@aws-sdk/types": "3.972.0", "@aws-sdk/util-arn-parser": "3.972.0", "@smithy/core": "^3.20.6", "@smithy/node-config-provider": "^4.3.8", "@smithy/protocol-http": "^5.3.8", "@smithy/signature-v4": "^5.3.8", "@smithy/smithy-client": "^4.10.8", "@smithy/types": "^4.12.0", "@smithy/util-config-provider": "^4.2.0", "@smithy/util-middleware": "^4.2.8", "@smithy/util-stream": "^4.5.10", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-0bcKFXWx+NZ7tIlOo7KjQ+O2rydiHdIQahrq+fN6k9Osky29v17guy68urUKfhTobR6iY6KvxkroFWaFtTgS5w=="],
"@aws-sdk/signature-v4-multi-region/@aws-sdk/types": ["@aws-sdk/types@3.972.0", "", { "dependencies": { "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-U7xBIbLSetONxb2bNzHyDgND3oKGoIfmknrEVnoEU4GUSs+0augUOIn9DIWGUO2ETcRFdsRUnmx9KhPT9Ojbug=="],
"@aws-sdk/util-endpoints/@aws-sdk/types": ["@aws-sdk/types@3.972.0", "", { "dependencies": { "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-U7xBIbLSetONxb2bNzHyDgND3oKGoIfmknrEVnoEU4GUSs+0augUOIn9DIWGUO2ETcRFdsRUnmx9KhPT9Ojbug=="],
"@jimp/core/buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="], "@jimp/core/buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="],
"@platejs/core/nanoid": ["nanoid@5.1.6", "", { "bin": { "nanoid": "bin/nanoid.js" } }, "sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg=="], "@platejs/core/nanoid": ["nanoid@5.1.6", "", { "bin": { "nanoid": "bin/nanoid.js" } }, "sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg=="],
"@prisma/adapter-pg/pg": ["pg@8.17.2", "", { "dependencies": { "pg-connection-string": "^2.10.1", "pg-pool": "^3.11.0", "pg-protocol": "^1.11.0", "pg-types": "2.2.0", "pgpass": "1.0.5" }, "optionalDependencies": { "pg-cloudflare": "^1.3.0" }, "peerDependencies": { "pg-native": ">=3.0.1" }, "optionalPeers": ["pg-native"] }, "sha512-vjbKdiBJRqzcYw1fNU5KuHyYvdJ1qpcQg1CeBrHFqV1pWgHeVR6j/+kX0E1AAXfyuLUGY1ICrN2ELKA/z2HWzw=="],
"@prisma/engines/@prisma/get-platform": ["@prisma/get-platform@7.3.0", "", { "dependencies": { "@prisma/debug": "7.3.0" } }, "sha512-N7c6m4/I0Q6JYmWKP2RCD/sM9eWiyCPY98g5c0uEktObNSZnugW2U/PO+pwL0UaqzxqTXt7gTsYsb0FnMnJNbg=="], "@prisma/engines/@prisma/get-platform": ["@prisma/get-platform@7.3.0", "", { "dependencies": { "@prisma/debug": "7.3.0" } }, "sha512-N7c6m4/I0Q6JYmWKP2RCD/sM9eWiyCPY98g5c0uEktObNSZnugW2U/PO+pwL0UaqzxqTXt7gTsYsb0FnMnJNbg=="],
"@prisma/fetch-engine/@prisma/get-platform": ["@prisma/get-platform@7.3.0", "", { "dependencies": { "@prisma/debug": "7.3.0" } }, "sha512-N7c6m4/I0Q6JYmWKP2RCD/sM9eWiyCPY98g5c0uEktObNSZnugW2U/PO+pwL0UaqzxqTXt7gTsYsb0FnMnJNbg=="], "@prisma/fetch-engine/@prisma/get-platform": ["@prisma/get-platform@7.3.0", "", { "dependencies": { "@prisma/debug": "7.3.0" } }, "sha512-N7c6m4/I0Q6JYmWKP2RCD/sM9eWiyCPY98g5c0uEktObNSZnugW2U/PO+pwL0UaqzxqTXt7gTsYsb0FnMnJNbg=="],
@ -1588,65 +1520,9 @@
"@aws-crypto/util/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@2.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA=="], "@aws-crypto/util/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@2.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA=="],
"@aws-sdk/client-sesv2/@aws-sdk/core/@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.957.0", "", { "dependencies": { "@smithy/types": "^4.11.0", "fast-xml-parser": "5.2.5", "tslib": "^2.6.2" } }, "sha512-Ai5iiQqS8kJ5PjzMhWcLKN0G2yasAkvpnPlq2EnqlIMdB48HsizElt62qcktdxp4neRMyGkFq4NzgmDbXnhRiA=="], "@prisma/adapter-pg/pg/pg-connection-string": ["pg-connection-string@2.10.1", "", {}, "sha512-iNzslsoeSH2/gmDDKiyMqF64DATUCWj3YJ0wP14kqcsf2TUklwimd+66yYojKwZCA7h2yRNLGug71hCBA2a4sw=="],
"@aws-sdk/client-sesv2/@aws-sdk/core/@smithy/property-provider": ["@smithy/property-provider@4.2.7", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-jmNYKe9MGGPoSl/D7JDDs1C8b3dC8f/w78LbaVfoTtWy4xAd5dfjaFG9c9PWPihY4ggMQNQSMtzU77CNgAJwmA=="], "@prisma/adapter-pg/pg/pg-protocol": ["pg-protocol@1.11.0", "", {}, "sha512-pfsxk2M9M3BuGgDOfuy37VNRRX3jmKgMjcvAcWqNDpZSf4cUmv8HSOl5ViRQFsfARFn0KuUQTgLxVMbNq5NW3g=="],
"@aws-sdk/client-sesv2/@aws-sdk/core/@smithy/signature-v4": ["@smithy/signature-v4@5.3.7", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "@smithy/protocol-http": "^5.3.7", "@smithy/types": "^4.11.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-middleware": "^4.2.7", "@smithy/util-uri-escape": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-9oNUlqBlFZFOSdxgImA6X5GFuzE7V2H7VG/7E70cdLhidFbdtvxxt81EHgykGK5vq5D3FafH//X+Oy31j3CKOg=="],
"@aws-sdk/client-sesv2/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.957.0", "", { "dependencies": { "@aws-sdk/core": "3.957.0", "@aws-sdk/types": "3.957.0", "@smithy/property-provider": "^4.2.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-475mkhGaWCr+Z52fOOVb/q2VHuNvqEDixlYIkeaO6xJ6t9qR0wpLt4hOQaR6zR1wfZV0SlE7d8RErdYq/PByog=="],
"@aws-sdk/client-sesv2/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.957.0", "", { "dependencies": { "@aws-sdk/core": "3.957.0", "@aws-sdk/types": "3.957.0", "@smithy/fetch-http-handler": "^5.3.8", "@smithy/node-http-handler": "^4.4.7", "@smithy/property-provider": "^4.2.7", "@smithy/protocol-http": "^5.3.7", "@smithy/smithy-client": "^4.10.2", "@smithy/types": "^4.11.0", "@smithy/util-stream": "^4.5.8", "tslib": "^2.6.2" } }, "sha512-8dS55QHRxXgJlHkEYaCGZIhieCs9NU1HU1BcqQ4RfUdSsfRdxxktqUKgCnBnOOn0oD3PPA8cQOCAVgIyRb3Rfw=="],
"@aws-sdk/client-sesv2/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.958.0", "", { "dependencies": { "@aws-sdk/core": "3.957.0", "@aws-sdk/credential-provider-env": "3.957.0", "@aws-sdk/credential-provider-http": "3.957.0", "@aws-sdk/credential-provider-login": "3.958.0", "@aws-sdk/credential-provider-process": "3.957.0", "@aws-sdk/credential-provider-sso": "3.958.0", "@aws-sdk/credential-provider-web-identity": "3.958.0", "@aws-sdk/nested-clients": "3.958.0", "@aws-sdk/types": "3.957.0", "@smithy/credential-provider-imds": "^4.2.7", "@smithy/property-provider": "^4.2.7", "@smithy/shared-ini-file-loader": "^4.4.2", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-u7twvZa1/6GWmPBZs6DbjlegCoNzNjBsMS/6fvh5quByYrcJr/uLd8YEr7S3UIq4kR/gSnHqcae7y2nL2bqZdg=="],
"@aws-sdk/client-sesv2/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process": ["@aws-sdk/credential-provider-process@3.957.0", "", { "dependencies": { "@aws-sdk/core": "3.957.0", "@aws-sdk/types": "3.957.0", "@smithy/property-provider": "^4.2.7", "@smithy/shared-ini-file-loader": "^4.4.2", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-/KIz9kadwbeLy6SKvT79W81Y+hb/8LMDyeloA2zhouE28hmne+hLn0wNCQXAAupFFlYOAtZR2NTBs7HBAReJlg=="],
"@aws-sdk/client-sesv2/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso": ["@aws-sdk/credential-provider-sso@3.958.0", "", { "dependencies": { "@aws-sdk/client-sso": "3.958.0", "@aws-sdk/core": "3.957.0", "@aws-sdk/token-providers": "3.958.0", "@aws-sdk/types": "3.957.0", "@smithy/property-provider": "^4.2.7", "@smithy/shared-ini-file-loader": "^4.4.2", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-CBYHJ5ufp8HC4q+o7IJejCUctJXWaksgpmoFpXerbjAso7/Fg7LLUu9inXVOxlHKLlvYekDXjIUBXDJS2WYdgg=="],
"@aws-sdk/client-sesv2/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.958.0", "", { "dependencies": { "@aws-sdk/core": "3.957.0", "@aws-sdk/nested-clients": "3.958.0", "@aws-sdk/types": "3.957.0", "@smithy/property-provider": "^4.2.7", "@smithy/shared-ini-file-loader": "^4.4.2", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-dgnvwjMq5Y66WozzUzxNkCFap+umHUtqMMKlr8z/vl9NYMLem/WUbWNpFFOVFWquXikc+ewtpBMR4KEDXfZ+KA=="],
"@aws-sdk/client-sesv2/@aws-sdk/credential-provider-node/@smithy/credential-provider-imds": ["@smithy/credential-provider-imds@4.2.7", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.7", "@smithy/property-provider": "^4.2.7", "@smithy/types": "^4.11.0", "@smithy/url-parser": "^4.2.7", "tslib": "^2.6.2" } }, "sha512-CmduWdCiILCRNbQWFR0OcZlUPVtyE49Sr8yYL0rZQ4D/wKxiNzBNS/YHemvnbkIWj623fplgkexUd/c9CAKdoA=="],
"@aws-sdk/client-sesv2/@aws-sdk/credential-provider-node/@smithy/property-provider": ["@smithy/property-provider@4.2.7", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-jmNYKe9MGGPoSl/D7JDDs1C8b3dC8f/w78LbaVfoTtWy4xAd5dfjaFG9c9PWPihY4ggMQNQSMtzU77CNgAJwmA=="],
"@aws-sdk/client-sesv2/@aws-sdk/credential-provider-node/@smithy/shared-ini-file-loader": ["@smithy/shared-ini-file-loader@4.4.2", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-M7iUUff/KwfNunmrgtqBfvZSzh3bmFgv/j/t1Y1dQ+8dNo34br1cqVEqy6v0mYEgi0DkGO7Xig0AnuOaEGVlcg=="],
"@aws-sdk/client-sesv2/@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3": ["@aws-sdk/middleware-sdk-s3@3.957.0", "", { "dependencies": { "@aws-sdk/core": "3.957.0", "@aws-sdk/types": "3.957.0", "@aws-sdk/util-arn-parser": "3.957.0", "@smithy/core": "^3.20.0", "@smithy/node-config-provider": "^4.3.7", "@smithy/protocol-http": "^5.3.7", "@smithy/signature-v4": "^5.3.7", "@smithy/smithy-client": "^4.10.2", "@smithy/types": "^4.11.0", "@smithy/util-config-provider": "^4.2.0", "@smithy/util-middleware": "^4.2.7", "@smithy/util-stream": "^4.5.8", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-5B2qY2nR2LYpxoQP0xUum5A1UNvH2JQpLHDH1nWFNF/XetV7ipFHksMxPNhtJJ6ARaWhQIDXfOUj0jcnkJxXUg=="],
"@aws-sdk/client-sesv2/@aws-sdk/signature-v4-multi-region/@smithy/signature-v4": ["@smithy/signature-v4@5.3.7", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "@smithy/protocol-http": "^5.3.7", "@smithy/types": "^4.11.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-middleware": "^4.2.7", "@smithy/util-uri-escape": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-9oNUlqBlFZFOSdxgImA6X5GFuzE7V2H7VG/7E70cdLhidFbdtvxxt81EHgykGK5vq5D3FafH//X+Oy31j3CKOg=="],
"@aws-sdk/client-sesv2/@smithy/core/@smithy/util-stream": ["@smithy/util-stream@4.5.8", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.8", "@smithy/node-http-handler": "^4.4.7", "@smithy/types": "^4.11.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-ZnnBhTapjM0YPGUSmOs0Mcg/Gg87k503qG4zU2v/+Js2Gu+daKOJMeqcQns8ajepY8tgzzfYxl6kQyZKml6O2w=="],
"@aws-sdk/client-sesv2/@smithy/fetch-http-handler/@smithy/querystring-builder": ["@smithy/querystring-builder@4.2.7", "", { "dependencies": { "@smithy/types": "^4.11.0", "@smithy/util-uri-escape": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-eKONSywHZxK4tBxe2lXEysh8wbBdvDWiA+RIuaxZSgCMmA0zMgoDpGLJhnyj+c0leOQprVnXOmcB4m+W9Rw7sg=="],
"@aws-sdk/client-sesv2/@smithy/middleware-endpoint/@smithy/shared-ini-file-loader": ["@smithy/shared-ini-file-loader@4.4.2", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-M7iUUff/KwfNunmrgtqBfvZSzh3bmFgv/j/t1Y1dQ+8dNo34br1cqVEqy6v0mYEgi0DkGO7Xig0AnuOaEGVlcg=="],
"@aws-sdk/client-sesv2/@smithy/middleware-retry/@smithy/service-error-classification": ["@smithy/service-error-classification@4.2.7", "", { "dependencies": { "@smithy/types": "^4.11.0" } }, "sha512-YB7oCbukqEb2Dlh3340/8g8vNGbs/QsNNRms+gv3N2AtZz9/1vSBx6/6tpwQpZMEJFs7Uq8h4mmOn48ZZ72MkA=="],
"@aws-sdk/client-sesv2/@smithy/node-config-provider/@smithy/property-provider": ["@smithy/property-provider@4.2.7", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-jmNYKe9MGGPoSl/D7JDDs1C8b3dC8f/w78LbaVfoTtWy4xAd5dfjaFG9c9PWPihY4ggMQNQSMtzU77CNgAJwmA=="],
"@aws-sdk/client-sesv2/@smithy/node-config-provider/@smithy/shared-ini-file-loader": ["@smithy/shared-ini-file-loader@4.4.2", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-M7iUUff/KwfNunmrgtqBfvZSzh3bmFgv/j/t1Y1dQ+8dNo34br1cqVEqy6v0mYEgi0DkGO7Xig0AnuOaEGVlcg=="],
"@aws-sdk/client-sesv2/@smithy/node-http-handler/@smithy/abort-controller": ["@smithy/abort-controller@4.2.7", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-rzMY6CaKx2qxrbYbqjXWS0plqEy7LOdKHS0bg4ixJ6aoGDPNUcLWk/FRNuCILh7GKLG9TFUXYYeQQldMBBwuyw=="],
"@aws-sdk/client-sesv2/@smithy/node-http-handler/@smithy/querystring-builder": ["@smithy/querystring-builder@4.2.7", "", { "dependencies": { "@smithy/types": "^4.11.0", "@smithy/util-uri-escape": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-eKONSywHZxK4tBxe2lXEysh8wbBdvDWiA+RIuaxZSgCMmA0zMgoDpGLJhnyj+c0leOQprVnXOmcB4m+W9Rw7sg=="],
"@aws-sdk/client-sesv2/@smithy/smithy-client/@smithy/util-stream": ["@smithy/util-stream@4.5.8", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.8", "@smithy/node-http-handler": "^4.4.7", "@smithy/types": "^4.11.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-ZnnBhTapjM0YPGUSmOs0Mcg/Gg87k503qG4zU2v/+Js2Gu+daKOJMeqcQns8ajepY8tgzzfYxl6kQyZKml6O2w=="],
"@aws-sdk/client-sesv2/@smithy/url-parser/@smithy/querystring-parser": ["@smithy/querystring-parser@4.2.7", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-3X5ZvzUHmlSTHAXFlswrS6EGt8fMSIxX/c3Rm1Pni3+wYWB6cjGocmRIoqcQF9nU5OgGmL0u7l9m44tSUpfj9w=="],
"@aws-sdk/client-sesv2/@smithy/util-defaults-mode-browser/@smithy/property-provider": ["@smithy/property-provider@4.2.7", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-jmNYKe9MGGPoSl/D7JDDs1C8b3dC8f/w78LbaVfoTtWy4xAd5dfjaFG9c9PWPihY4ggMQNQSMtzU77CNgAJwmA=="],
"@aws-sdk/client-sesv2/@smithy/util-defaults-mode-node/@smithy/credential-provider-imds": ["@smithy/credential-provider-imds@4.2.7", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.7", "@smithy/property-provider": "^4.2.7", "@smithy/types": "^4.11.0", "@smithy/url-parser": "^4.2.7", "tslib": "^2.6.2" } }, "sha512-CmduWdCiILCRNbQWFR0OcZlUPVtyE49Sr8yYL0rZQ4D/wKxiNzBNS/YHemvnbkIWj623fplgkexUd/c9CAKdoA=="],
"@aws-sdk/client-sesv2/@smithy/util-defaults-mode-node/@smithy/property-provider": ["@smithy/property-provider@4.2.7", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-jmNYKe9MGGPoSl/D7JDDs1C8b3dC8f/w78LbaVfoTtWy4xAd5dfjaFG9c9PWPihY4ggMQNQSMtzU77CNgAJwmA=="],
"@aws-sdk/client-sesv2/@smithy/util-retry/@smithy/service-error-classification": ["@smithy/service-error-classification@4.2.7", "", { "dependencies": { "@smithy/types": "^4.11.0" } }, "sha512-YB7oCbukqEb2Dlh3340/8g8vNGbs/QsNNRms+gv3N2AtZz9/1vSBx6/6tpwQpZMEJFs7Uq8h4mmOn48ZZ72MkA=="],
"@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3/@aws-sdk/core": ["@aws-sdk/core@3.972.0", "", { "dependencies": { "@aws-sdk/types": "3.972.0", "@aws-sdk/xml-builder": "3.972.0", "@smithy/core": "^3.20.6", "@smithy/node-config-provider": "^4.3.8", "@smithy/property-provider": "^4.2.8", "@smithy/protocol-http": "^5.3.8", "@smithy/signature-v4": "^5.3.8", "@smithy/smithy-client": "^4.10.8", "@smithy/types": "^4.12.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-middleware": "^4.2.8", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-nEeUW2M9F+xdIaD98F5MBcQ4ITtykj3yKbgFZ6J0JtL3bq+Z90szQ6Yy8H/BLPYXTs3V4n9ifnBo8cprRDiE6A=="],
"@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3/@aws-sdk/util-arn-parser": ["@aws-sdk/util-arn-parser@3.972.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-RM5Mmo/KJ593iMSrALlHEOcc9YOIyOsDmS5x2NLOMdEmzv1o00fcpAkCQ02IGu1eFneBFT7uX0Mpag0HI+Cz2g=="],
"lazystream/readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="], "lazystream/readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="],
@ -1665,25 +1541,5 @@
"@aws-crypto/sha256-browser/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="], "@aws-crypto/sha256-browser/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="],
"@aws-crypto/util/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="], "@aws-crypto/util/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="],
"@aws-sdk/client-sesv2/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/util-stream": ["@smithy/util-stream@4.5.8", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.8", "@smithy/node-http-handler": "^4.4.7", "@smithy/types": "^4.11.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-ZnnBhTapjM0YPGUSmOs0Mcg/Gg87k503qG4zU2v/+Js2Gu+daKOJMeqcQns8ajepY8tgzzfYxl6kQyZKml6O2w=="],
"@aws-sdk/client-sesv2/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/credential-provider-login": ["@aws-sdk/credential-provider-login@3.958.0", "", { "dependencies": { "@aws-sdk/core": "3.957.0", "@aws-sdk/nested-clients": "3.958.0", "@aws-sdk/types": "3.957.0", "@smithy/property-provider": "^4.2.7", "@smithy/protocol-http": "^5.3.7", "@smithy/shared-ini-file-loader": "^4.4.2", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-sDwtDnBSszUIbzbOORGh5gmXGl9aK25+BHb4gb1aVlqB+nNL2+IUEJA62+CE55lXSH8qXF90paivjK8tOHTwPA=="],
"@aws-sdk/client-sesv2/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.958.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.957.0", "@aws-sdk/middleware-host-header": "3.957.0", "@aws-sdk/middleware-logger": "3.957.0", "@aws-sdk/middleware-recursion-detection": "3.957.0", "@aws-sdk/middleware-user-agent": "3.957.0", "@aws-sdk/region-config-resolver": "3.957.0", "@aws-sdk/types": "3.957.0", "@aws-sdk/util-endpoints": "3.957.0", "@aws-sdk/util-user-agent-browser": "3.957.0", "@aws-sdk/util-user-agent-node": "3.957.0", "@smithy/config-resolver": "^4.4.5", "@smithy/core": "^3.20.0", "@smithy/fetch-http-handler": "^5.3.8", "@smithy/hash-node": "^4.2.7", "@smithy/invalid-dependency": "^4.2.7", "@smithy/middleware-content-length": "^4.2.7", "@smithy/middleware-endpoint": "^4.4.1", "@smithy/middleware-retry": "^4.4.17", "@smithy/middleware-serde": "^4.2.8", "@smithy/middleware-stack": "^4.2.7", "@smithy/node-config-provider": "^4.3.7", "@smithy/node-http-handler": "^4.4.7", "@smithy/protocol-http": "^5.3.7", "@smithy/smithy-client": "^4.10.2", "@smithy/types": "^4.11.0", "@smithy/url-parser": "^4.2.7", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.16", "@smithy/util-defaults-mode-node": "^4.2.19", "@smithy/util-endpoints": "^3.2.7", "@smithy/util-middleware": "^4.2.7", "@smithy/util-retry": "^4.2.7", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-/KuCcS8b5TpQXkYOrPLYytrgxBhv81+5pChkOlhegbeHttjM69pyUpQVJqyfDM/A7wPLnDrzCAnk4zaAOkY0Nw=="],
"@aws-sdk/client-sesv2/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso": ["@aws-sdk/client-sso@3.958.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.957.0", "@aws-sdk/middleware-host-header": "3.957.0", "@aws-sdk/middleware-logger": "3.957.0", "@aws-sdk/middleware-recursion-detection": "3.957.0", "@aws-sdk/middleware-user-agent": "3.957.0", "@aws-sdk/region-config-resolver": "3.957.0", "@aws-sdk/types": "3.957.0", "@aws-sdk/util-endpoints": "3.957.0", "@aws-sdk/util-user-agent-browser": "3.957.0", "@aws-sdk/util-user-agent-node": "3.957.0", "@smithy/config-resolver": "^4.4.5", "@smithy/core": "^3.20.0", "@smithy/fetch-http-handler": "^5.3.8", "@smithy/hash-node": "^4.2.7", "@smithy/invalid-dependency": "^4.2.7", "@smithy/middleware-content-length": "^4.2.7", "@smithy/middleware-endpoint": "^4.4.1", "@smithy/middleware-retry": "^4.4.17", "@smithy/middleware-serde": "^4.2.8", "@smithy/middleware-stack": "^4.2.7", "@smithy/node-config-provider": "^4.3.7", "@smithy/node-http-handler": "^4.4.7", "@smithy/protocol-http": "^5.3.7", "@smithy/smithy-client": "^4.10.2", "@smithy/types": "^4.11.0", "@smithy/url-parser": "^4.2.7", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.16", "@smithy/util-defaults-mode-node": "^4.2.19", "@smithy/util-endpoints": "^3.2.7", "@smithy/util-middleware": "^4.2.7", "@smithy/util-retry": "^4.2.7", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-6qNCIeaMzKzfqasy2nNRuYnMuaMebCcCPP4J2CVGkA8QYMbIVKPlkn9bpB20Vxe6H/r3jtCCLQaOJjVTx/6dXg=="],
"@aws-sdk/client-sesv2/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers": ["@aws-sdk/token-providers@3.958.0", "", { "dependencies": { "@aws-sdk/core": "3.957.0", "@aws-sdk/nested-clients": "3.958.0", "@aws-sdk/types": "3.957.0", "@smithy/property-provider": "^4.2.7", "@smithy/shared-ini-file-loader": "^4.4.2", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-UCj7lQXODduD1myNJQkV+LYcGYJ9iiMggR8ow8Hva1g3A/Na5imNXzz6O67k7DAee0TYpy+gkNw+SizC6min8Q=="],
"@aws-sdk/client-sesv2/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.958.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.957.0", "@aws-sdk/middleware-host-header": "3.957.0", "@aws-sdk/middleware-logger": "3.957.0", "@aws-sdk/middleware-recursion-detection": "3.957.0", "@aws-sdk/middleware-user-agent": "3.957.0", "@aws-sdk/region-config-resolver": "3.957.0", "@aws-sdk/types": "3.957.0", "@aws-sdk/util-endpoints": "3.957.0", "@aws-sdk/util-user-agent-browser": "3.957.0", "@aws-sdk/util-user-agent-node": "3.957.0", "@smithy/config-resolver": "^4.4.5", "@smithy/core": "^3.20.0", "@smithy/fetch-http-handler": "^5.3.8", "@smithy/hash-node": "^4.2.7", "@smithy/invalid-dependency": "^4.2.7", "@smithy/middleware-content-length": "^4.2.7", "@smithy/middleware-endpoint": "^4.4.1", "@smithy/middleware-retry": "^4.4.17", "@smithy/middleware-serde": "^4.2.8", "@smithy/middleware-stack": "^4.2.7", "@smithy/node-config-provider": "^4.3.7", "@smithy/node-http-handler": "^4.4.7", "@smithy/protocol-http": "^5.3.7", "@smithy/smithy-client": "^4.10.2", "@smithy/types": "^4.11.0", "@smithy/url-parser": "^4.2.7", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.16", "@smithy/util-defaults-mode-node": "^4.2.19", "@smithy/util-endpoints": "^3.2.7", "@smithy/util-middleware": "^4.2.7", "@smithy/util-retry": "^4.2.7", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-/KuCcS8b5TpQXkYOrPLYytrgxBhv81+5pChkOlhegbeHttjM69pyUpQVJqyfDM/A7wPLnDrzCAnk4zaAOkY0Nw=="],
"@aws-sdk/client-sesv2/@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3/@aws-sdk/util-arn-parser": ["@aws-sdk/util-arn-parser@3.957.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-Aj6m+AyrhWyg8YQ4LDPg2/gIfGHCEcoQdBt5DeSFogN5k9mmJPOJ+IAmNSWmWRjpOxEy6eY813RNDI6qS97M0g=="],
"@aws-sdk/client-sesv2/@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3/@smithy/util-stream": ["@smithy/util-stream@4.5.8", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.8", "@smithy/node-http-handler": "^4.4.7", "@smithy/types": "^4.11.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-ZnnBhTapjM0YPGUSmOs0Mcg/Gg87k503qG4zU2v/+Js2Gu+daKOJMeqcQns8ajepY8tgzzfYxl6kQyZKml6O2w=="],
"@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3/@aws-sdk/core/@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.972.0", "", { "dependencies": { "@smithy/types": "^4.12.0", "fast-xml-parser": "5.2.5", "tslib": "^2.6.2" } }, "sha512-POaGMcXnozzqBUyJM3HLUZ9GR6OKJWPGJEmhtTnxZXt8B6JcJ/6K3xRJ5H/j8oovVLz8Wg6vFxAHv8lvuASxMg=="],
"@aws-sdk/client-sesv2/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.958.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.957.0", "@aws-sdk/middleware-host-header": "3.957.0", "@aws-sdk/middleware-logger": "3.957.0", "@aws-sdk/middleware-recursion-detection": "3.957.0", "@aws-sdk/middleware-user-agent": "3.957.0", "@aws-sdk/region-config-resolver": "3.957.0", "@aws-sdk/types": "3.957.0", "@aws-sdk/util-endpoints": "3.957.0", "@aws-sdk/util-user-agent-browser": "3.957.0", "@aws-sdk/util-user-agent-node": "3.957.0", "@smithy/config-resolver": "^4.4.5", "@smithy/core": "^3.20.0", "@smithy/fetch-http-handler": "^5.3.8", "@smithy/hash-node": "^4.2.7", "@smithy/invalid-dependency": "^4.2.7", "@smithy/middleware-content-length": "^4.2.7", "@smithy/middleware-endpoint": "^4.4.1", "@smithy/middleware-retry": "^4.4.17", "@smithy/middleware-serde": "^4.2.8", "@smithy/middleware-stack": "^4.2.7", "@smithy/node-config-provider": "^4.3.7", "@smithy/node-http-handler": "^4.4.7", "@smithy/protocol-http": "^5.3.7", "@smithy/smithy-client": "^4.10.2", "@smithy/types": "^4.11.0", "@smithy/url-parser": "^4.2.7", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.16", "@smithy/util-defaults-mode-node": "^4.2.19", "@smithy/util-endpoints": "^3.2.7", "@smithy/util-middleware": "^4.2.7", "@smithy/util-retry": "^4.2.7", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-/KuCcS8b5TpQXkYOrPLYytrgxBhv81+5pChkOlhegbeHttjM69pyUpQVJqyfDM/A7wPLnDrzCAnk4zaAOkY0Nw=="],
} }
} }

8882
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,17 +1,19 @@
{ {
"name": "admin.gaertan.art", "name": "admin.gaertan.art",
"version": "0.1.0", "version": "1.1.0",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "PORT=3001 NODE_OPTIONS='--max-old-space-size=4096' next dev", "dev": "PORT=3001 NODE_OPTIONS='--max-old-space-size=4096' next dev",
"dev:version": "NEXT_PUBLIC_GIT_SHA=$(git rev-parse --short HEAD) NEXT_PUBLIC_DEPLOY_ENV=dev PORT=3001 NODE_OPTIONS='--max-old-space-size=4096' next dev",
"build": "next build", "build": "next build",
"build:version": "NEXT_PUBLIC_GIT_SHA=$(git rev-parse --short HEAD) NEXT_PUBLIC_DEPLOY_ENV=prod bun run build",
"start": "next start", "start": "next start",
"lint": "biome check", "lint": "biome check",
"format": "biome format --write" "format": "biome format --write"
}, },
"dependencies": { "dependencies": {
"@aws-sdk/client-s3": "^3.974.0", "@aws-sdk/client-s3": "^3.980.0",
"@aws-sdk/s3-request-presigner": "^3.974.0", "@aws-sdk/s3-request-presigner": "^3.980.0",
"@dnd-kit/core": "^6.3.1", "@dnd-kit/core": "^6.3.1",
"@dnd-kit/modifiers": "^9.0.0", "@dnd-kit/modifiers": "^9.0.0",
"@dnd-kit/sortable": "^10.0.0", "@dnd-kit/sortable": "^10.0.0",
@ -44,7 +46,7 @@
"@radix-ui/react-tooltip": "^1.2.8", "@radix-ui/react-tooltip": "^1.2.8",
"@tanstack/react-table": "^8.21.3", "@tanstack/react-table": "^8.21.3",
"archiver": "^7.0.1", "archiver": "^7.0.1",
"better-auth": "^1.4.17", "better-auth": "^1.4.18",
"class-variance-authority": "^0.7.1", "class-variance-authority": "^0.7.1",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"cmdk": "^1.1.1", "cmdk": "^1.1.1",
@ -53,15 +55,15 @@
"dotenv": "^17.2.3", "dotenv": "^17.2.3",
"lowlight": "^3.3.0", "lowlight": "^3.3.0",
"lucide-react": "^0.561.0", "lucide-react": "^0.561.0",
"next": "^16.1.4", "next": "16.1.6",
"next-themes": "^0.4.6", "next-themes": "^0.4.6",
"node-vibrant": "^4.0.3", "node-vibrant": "^4.0.4",
"nodemailer": "^7.0.12", "nodemailer": "^7.0.13",
"pg": "^8.17.2", "pg": "^8.18.0",
"platejs": "^52.0.17", "platejs": "^52.0.17",
"react": "19.2.1", "react": "19.2.4",
"react-day-picker": "^9.13.0", "react-day-picker": "^9.13.0",
"react-dom": "19.2.1", "react-dom": "19.2.4",
"react-hook-form": "^7.71.1", "react-hook-form": "^7.71.1",
"remark-gfm": "^4.0.1", "remark-gfm": "^4.0.1",
"remark-math": "^6.0.0", "remark-math": "^6.0.0",
@ -78,14 +80,18 @@
"@types/culori": "^4.0.1", "@types/culori": "^4.0.1",
"@types/date-fns": "^2.6.3", "@types/date-fns": "^2.6.3",
"@types/node": "^20.19.30", "@types/node": "^20.19.30",
"@types/nodemailer": "^7.0.5", "@types/nodemailer": "^7.0.9",
"@types/pg": "^8.16.0", "@types/pg": "^8.16.0",
"@types/react": "^19.2.9", "@types/react": "19.2.10",
"@types/react-dom": "^19.2.3", "@types/react-dom": "19.2.3",
"@types/uuid": "^11.0.0", "@types/uuid": "^11.0.0",
"prisma": "^7.3.0", "prisma": "^7.3.0",
"tailwindcss": "^4.1.18", "tailwindcss": "^4.1.18",
"tw-animate-css": "^1.4.0", "tw-animate-css": "^1.4.0",
"typescript": "^5.9.3" "typescript": "^5.9.3"
},
"overrides": {
"@types/react": "19.2.10",
"@types/react-dom": "19.2.3"
} }
} }

View File

@ -0,0 +1,140 @@
-- CreateTable
CREATE TABLE "Tag" (
"id" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"sortIndex" INTEGER NOT NULL DEFAULT 0,
"name" TEXT NOT NULL,
"slug" TEXT NOT NULL,
"isVisible" BOOLEAN NOT NULL DEFAULT true,
"description" TEXT,
CONSTRAINT "Tag_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "TagAlias" (
"id" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"alias" TEXT NOT NULL,
"tagId" TEXT NOT NULL,
CONSTRAINT "TagAlias_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "TagCategory" (
"id" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"tagId" TEXT NOT NULL,
"categoryId" TEXT NOT NULL,
"isParent" BOOLEAN NOT NULL DEFAULT false,
"showOnAnimalPage" BOOLEAN NOT NULL DEFAULT false,
"parentTagId" TEXT,
CONSTRAINT "TagCategory_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Miniature" (
"id" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "Miniature_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "_ArtworkTagsV2" (
"A" TEXT NOT NULL,
"B" TEXT NOT NULL,
CONSTRAINT "_ArtworkTagsV2_AB_pkey" PRIMARY KEY ("A","B")
);
-- CreateTable
CREATE TABLE "_MiniatureTags" (
"A" TEXT NOT NULL,
"B" TEXT NOT NULL,
CONSTRAINT "_MiniatureTags_AB_pkey" PRIMARY KEY ("A","B")
);
-- CreateTable
CREATE TABLE "_CommissionTypeTags" (
"A" TEXT NOT NULL,
"B" TEXT NOT NULL,
CONSTRAINT "_CommissionTypeTags_AB_pkey" PRIMARY KEY ("A","B")
);
-- CreateIndex
CREATE UNIQUE INDEX "Tag_name_key" ON "Tag"("name");
-- CreateIndex
CREATE UNIQUE INDEX "Tag_slug_key" ON "Tag"("slug");
-- CreateIndex
CREATE UNIQUE INDEX "TagAlias_alias_key" ON "TagAlias"("alias");
-- CreateIndex
CREATE INDEX "TagAlias_alias_idx" ON "TagAlias"("alias");
-- CreateIndex
CREATE UNIQUE INDEX "TagAlias_tagId_alias_key" ON "TagAlias"("tagId", "alias");
-- CreateIndex
CREATE INDEX "TagCategory_categoryId_idx" ON "TagCategory"("categoryId");
-- CreateIndex
CREATE INDEX "TagCategory_tagId_idx" ON "TagCategory"("tagId");
-- CreateIndex
CREATE INDEX "TagCategory_parentTagId_idx" ON "TagCategory"("parentTagId");
-- CreateIndex
CREATE INDEX "TagCategory_categoryId_parentTagId_idx" ON "TagCategory"("categoryId", "parentTagId");
-- CreateIndex
CREATE UNIQUE INDEX "TagCategory_tagId_categoryId_key" ON "TagCategory"("tagId", "categoryId");
-- CreateIndex
CREATE INDEX "_ArtworkTagsV2_B_index" ON "_ArtworkTagsV2"("B");
-- CreateIndex
CREATE INDEX "_MiniatureTags_B_index" ON "_MiniatureTags"("B");
-- CreateIndex
CREATE INDEX "_CommissionTypeTags_B_index" ON "_CommissionTypeTags"("B");
-- AddForeignKey
ALTER TABLE "TagAlias" ADD CONSTRAINT "TagAlias_tagId_fkey" FOREIGN KEY ("tagId") REFERENCES "Tag"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "TagCategory" ADD CONSTRAINT "TagCategory_tagId_fkey" FOREIGN KEY ("tagId") REFERENCES "Tag"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "TagCategory" ADD CONSTRAINT "TagCategory_categoryId_fkey" FOREIGN KEY ("categoryId") REFERENCES "ArtCategory"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "TagCategory" ADD CONSTRAINT "TagCategory_parentTagId_fkey" FOREIGN KEY ("parentTagId") REFERENCES "Tag"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "_ArtworkTagsV2" ADD CONSTRAINT "_ArtworkTagsV2_A_fkey" FOREIGN KEY ("A") REFERENCES "Artwork"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "_ArtworkTagsV2" ADD CONSTRAINT "_ArtworkTagsV2_B_fkey" FOREIGN KEY ("B") REFERENCES "Tag"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "_MiniatureTags" ADD CONSTRAINT "_MiniatureTags_A_fkey" FOREIGN KEY ("A") REFERENCES "Miniature"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "_MiniatureTags" ADD CONSTRAINT "_MiniatureTags_B_fkey" FOREIGN KEY ("B") REFERENCES "Tag"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "_CommissionTypeTags" ADD CONSTRAINT "_CommissionTypeTags_A_fkey" FOREIGN KEY ("A") REFERENCES "CommissionType"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "_CommissionTypeTags" ADD CONSTRAINT "_CommissionTypeTags_B_fkey" FOREIGN KEY ("B") REFERENCES "Tag"("id") ON DELETE CASCADE ON UPDATE CASCADE;

View File

@ -0,0 +1,65 @@
/*
Warnings:
- You are about to drop the `ArtTag` table. If the table is not empty, all the data it contains will be lost.
- You are about to drop the `ArtTagAlias` table. If the table is not empty, all the data it contains will be lost.
- You are about to drop the `_ArtCategoryToArtTag` table. If the table is not empty, all the data it contains will be lost.
- You are about to drop the `_ArtTagToArtwork` table. If the table is not empty, all the data it contains will be lost.
- You are about to drop the `_ArtworkTagsV2` table. If the table is not empty, all the data it contains will be lost.
*/
-- DropForeignKey
ALTER TABLE "ArtTag" DROP CONSTRAINT "ArtTag_parentId_fkey";
-- DropForeignKey
ALTER TABLE "ArtTagAlias" DROP CONSTRAINT "ArtTagAlias_tagId_fkey";
-- DropForeignKey
ALTER TABLE "_ArtCategoryToArtTag" DROP CONSTRAINT "_ArtCategoryToArtTag_A_fkey";
-- DropForeignKey
ALTER TABLE "_ArtCategoryToArtTag" DROP CONSTRAINT "_ArtCategoryToArtTag_B_fkey";
-- DropForeignKey
ALTER TABLE "_ArtTagToArtwork" DROP CONSTRAINT "_ArtTagToArtwork_A_fkey";
-- DropForeignKey
ALTER TABLE "_ArtTagToArtwork" DROP CONSTRAINT "_ArtTagToArtwork_B_fkey";
-- DropForeignKey
ALTER TABLE "_ArtworkTagsV2" DROP CONSTRAINT "_ArtworkTagsV2_A_fkey";
-- DropForeignKey
ALTER TABLE "_ArtworkTagsV2" DROP CONSTRAINT "_ArtworkTagsV2_B_fkey";
-- DropTable
DROP TABLE "ArtTag";
-- DropTable
DROP TABLE "ArtTagAlias";
-- DropTable
DROP TABLE "_ArtCategoryToArtTag";
-- DropTable
DROP TABLE "_ArtTagToArtwork";
-- DropTable
DROP TABLE "_ArtworkTagsV2";
-- CreateTable
CREATE TABLE "_ArtworkTags" (
"A" TEXT NOT NULL,
"B" TEXT NOT NULL,
CONSTRAINT "_ArtworkTags_AB_pkey" PRIMARY KEY ("A","B")
);
-- CreateIndex
CREATE INDEX "_ArtworkTags_B_index" ON "_ArtworkTags"("B");
-- AddForeignKey
ALTER TABLE "_ArtworkTags" ADD CONSTRAINT "_ArtworkTags_A_fkey" FOREIGN KEY ("A") REFERENCES "Artwork"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "_ArtworkTags" ADD CONSTRAINT "_ArtworkTags_B_fkey" FOREIGN KEY ("B") REFERENCES "Tag"("id") ON DELETE CASCADE ON UPDATE CASCADE;

View File

@ -0,0 +1,16 @@
-- CreateTable
CREATE TABLE "_CommissionCustomCardTags" (
"A" TEXT NOT NULL,
"B" TEXT NOT NULL,
CONSTRAINT "_CommissionCustomCardTags_AB_pkey" PRIMARY KEY ("A","B")
);
-- CreateIndex
CREATE INDEX "_CommissionCustomCardTags_B_index" ON "_CommissionCustomCardTags"("B");
-- AddForeignKey
ALTER TABLE "_CommissionCustomCardTags" ADD CONSTRAINT "_CommissionCustomCardTags_A_fkey" FOREIGN KEY ("A") REFERENCES "CommissionCustomCard"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "_CommissionCustomCardTags" ADD CONSTRAINT "_CommissionCustomCardTags_B_fkey" FOREIGN KEY ("B") REFERENCES "Tag"("id") ON DELETE CASCADE ON UPDATE CASCADE;

View File

@ -47,13 +47,13 @@ model Artwork {
galleryId String? galleryId String?
gallery Gallery? @relation(fields: [galleryId], references: [id]) gallery Gallery? @relation(fields: [galleryId], references: [id])
metadata ArtworkMetadata? metadata ArtworkMetadata?
timelapse ArtworkTimelapse? timelapse ArtworkTimelapse?
albums Album[] albums Album[]
categories ArtCategory[] categories ArtCategory[]
colors ArtworkColor[] colors ArtworkColor[]
tags ArtTag[] tags Tag[] @relation("ArtworkTags")
variants FileVariant[] variants FileVariant[]
@@index([colorStatus]) @@index([colorStatus])
@ -101,43 +101,7 @@ model ArtCategory {
description String? description String?
artworks Artwork[] artworks Artwork[]
tags ArtTag[] tagLinks TagCategory[]
}
model ArtTag {
id String @id @default(cuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
sortIndex Int @default(0)
name String @unique
slug String @unique
isParent Boolean @default(false)
showOnAnimalPage Boolean @default(false)
description String?
aliases ArtTagAlias[]
artworks Artwork[]
categories ArtCategory[]
parentId String?
parent ArtTag? @relation("TagHierarchy", fields: [parentId], references: [id], onDelete: SetNull)
children ArtTag[] @relation("TagHierarchy")
}
model ArtTagAlias {
id String @id @default(cuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
alias String @unique
tagId String
tag ArtTag @relation(fields: [tagId], references: [id], onDelete: Cascade)
@@unique([tagId, alias])
@@index([alias])
} }
model Color { model Color {
@ -201,12 +165,12 @@ model ArtworkTimelapse {
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @updatedAt updatedAt DateTime @updatedAt
artworkId String @unique artworkId String @unique
artwork Artwork @relation(fields: [artworkId], references: [id], onDelete: Cascade) artwork Artwork @relation(fields: [artworkId], references: [id], onDelete: Cascade)
enabled Boolean @default(false) enabled Boolean @default(false)
s3Key String @unique s3Key String @unique
fileName String? fileName String?
mimeType String? mimeType String?
sizeBytes Int? sizeBytes Int?
@ -248,6 +212,72 @@ model FileVariant {
@@unique([artworkId, type]) @@unique([artworkId, type])
} }
model Tag {
id String @id @default(cuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
sortIndex Int @default(0)
name String @unique
slug String @unique
isVisible Boolean @default(true)
description String?
aliases TagAlias[]
categoryLinks TagCategory[]
categoryParents TagCategory[] @relation("TagCategoryParent")
artworks Artwork[] @relation("ArtworkTags")
commissionTypes CommissionType[] @relation("CommissionTypeTags")
commissionCustomCards CommissionCustomCard[] @relation("CommissionCustomCardTags")
miniatures Miniature[] @relation("MiniatureTags")
}
model TagAlias {
id String @id @default(cuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
alias String @unique
tagId String
tag Tag @relation(fields: [tagId], references: [id], onDelete: Cascade)
@@unique([tagId, alias])
@@index([alias])
}
model TagCategory {
id String @id @default(cuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tagId String
categoryId String
isParent Boolean @default(false)
showOnAnimalPage Boolean @default(false)
parentTagId String?
tag Tag @relation(fields: [tagId], references: [id], onDelete: Cascade)
category ArtCategory @relation(fields: [categoryId], references: [id], onDelete: Cascade)
parentTag Tag? @relation("TagCategoryParent", fields: [parentTagId], references: [id], onDelete: SetNull)
@@unique([tagId, categoryId])
@@index([categoryId])
@@index([tagId])
@@index([parentTagId])
@@index([categoryId, parentTagId])
}
model Miniature {
id String @id @default(cuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tags Tag[] @relation("MiniatureTags")
}
model Commission { model Commission {
id String @id @default(cuid()) id String @id @default(cuid())
createdAt DateTime @default(now()) createdAt DateTime @default(now())
@ -265,6 +295,8 @@ model CommissionType {
description String? description String?
tags Tag[] @relation("CommissionTypeTags")
options CommissionTypeOption[] options CommissionTypeOption[]
extras CommissionTypeExtra[] extras CommissionTypeExtra[]
customInputs CommissionTypeCustomInput[] customInputs CommissionTypeCustomInput[]
@ -279,13 +311,14 @@ model CommissionCustomCard {
name String name String
description String? description String?
referenceImageUrl String? referenceImageUrl String?
isVisible Boolean @default(true) isVisible Boolean @default(true)
isSpecialOffer Boolean @default(false) isSpecialOffer Boolean @default(false)
options CommissionCustomCardOption[] tags Tag[] @relation("CommissionCustomCardTags")
extras CommissionCustomCardExtra[] options CommissionCustomCardOption[]
extras CommissionCustomCardExtra[]
requests CommissionRequest[] requests CommissionRequest[]
@@index([isVisible, sortIndex]) @@index([isVisible, sortIndex])
@ -301,9 +334,9 @@ model CommissionOption {
description String? description String?
types CommissionTypeOption[] types CommissionTypeOption[]
customCards CommissionCustomCardOption[] customCards CommissionCustomCardOption[]
requests CommissionRequest[] requests CommissionRequest[]
} }
model CommissionTypeOption { model CommissionTypeOption {
@ -335,8 +368,8 @@ model CommissionExtra {
description String? description String?
requests CommissionRequest[] requests CommissionRequest[]
types CommissionTypeExtra[] types CommissionTypeExtra[]
customCards CommissionCustomCardExtra[] customCards CommissionCustomCardExtra[]
} }
@ -444,12 +477,12 @@ model CommissionRequest {
userAgent String? userAgent String?
customFields Json? customFields Json?
optionId String? optionId String?
typeId String? typeId String?
customCardId String? customCardId String?
option CommissionOption? @relation(fields: [optionId], references: [id]) option CommissionOption? @relation(fields: [optionId], references: [id])
type CommissionType? @relation(fields: [typeId], references: [id]) type CommissionType? @relation(fields: [typeId], references: [id])
customCard CommissionCustomCard? @relation(fields: [customCardId], references: [id]) customCard CommissionCustomCard? @relation(fields: [customCardId], references: [id])
extras CommissionExtra[] extras CommissionExtra[]
files CommissionRequestFile[] files CommissionRequestFile[]
@ -460,9 +493,9 @@ model CommissionGuidelines {
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @updatedAt updatedAt DateTime @updatedAt
markdown String markdown String
exampleImageUrl String? exampleImageUrl String?
isActive Boolean @default(true) isActive Boolean @default(true)
@@index([isActive]) @@index([isActive])
} }

View File

@ -4,6 +4,7 @@ import { prisma } from "@/lib/prisma";
import { s3 } from "@/lib/s3"; import { s3 } from "@/lib/s3";
import { DeleteObjectCommand } from "@aws-sdk/client-s3"; import { DeleteObjectCommand } from "@aws-sdk/client-s3";
// Deletes an artwork and all related assets and records.
export async function deleteArtwork(artworkId: string) { export async function deleteArtwork(artworkId: string) {
const artwork = await prisma.artwork.findUnique({ const artwork = await prisma.artwork.findUnique({
where: { id: artworkId }, where: { id: artworkId },
@ -82,4 +83,4 @@ export async function deleteArtwork(artworkId: string) {
await prisma.artwork.delete({ where: { id: artworkId } }); await prisma.artwork.delete({ where: { id: artworkId } });
return { success: true }; return { success: true };
} }

View File

@ -1,36 +1,49 @@
"use server"; "use server";
import { prisma } from "@/lib/prisma"; import { prisma } from "@/lib/prisma";
import { VibrantSwatch } from "@/types/VibrantSwatch"; import type { VibrantSwatch } from "@/types/VibrantSwatch";
import { getImageBufferFromS3Key } from "@/utils/getImageBufferFromS3"; import { getImageBufferFromS3Key } from "@/utils/getImageBufferFromS3";
import { generateColorName, rgbToHex } from "@/utils/uploadHelper"; import { generateColorName, rgbToHex } from "@/utils/uploadHelper";
import { converter, parse } from "culori"; import { converter, parse } from "culori";
import { Vibrant } from "node-vibrant/node"; import { Vibrant } from "node-vibrant/node";
const toOklab = converter("oklab"); const toOklab = converter("oklab");
const A_MIN = -0.5, A_MAX = 0.5; const A_MIN = -0.5,
const B_MIN = -0.5, B_MAX = 0.5; A_MAX = 0.5;
const B_MIN = -0.5,
B_MAX = 0.5;
function clamp01(x: number) { return Math.max(0, Math.min(1, x)); } function clamp01(x: number) {
function norm(x: number, lo: number, hi: number) { return clamp01((x - lo) / (hi - lo)); } return Math.max(0, Math.min(1, x));
}
function norm(x: number, lo: number, hi: number) {
return clamp01((x - lo) / (hi - lo));
}
function hilbertIndex15(x01: number, y01: number): number { function hilbertIndex15(x01: number, y01: number): number {
let x = Math.floor(clamp01(x01) * 32767); let x = Math.floor(clamp01(x01) * 32767);
let y = Math.floor(clamp01(y01) * 32767); let y = Math.floor(clamp01(y01) * 32767);
let index = 0; let index = 0;
for (let s = 1 << 14; s > 0; s >>= 1) { for (let s = 1 << 14; s > 0; s >>= 1) {
const rx = (x & s) ? 1 : 0; const rx = x & s ? 1 : 0;
const ry = (y & s) ? 1 : 0; const ry = y & s ? 1 : 0;
index += s * s * ((3 * rx) ^ ry); index += s * s * ((3 * rx) ^ ry);
if (ry === 0) { if (ry === 0) {
if (rx === 1) { x = 32767 - x; y = 32767 - y; } if (rx === 1) {
const t = x; x = y; y = t; x = 32767 - x;
y = 32767 - y;
}
const t = x;
x = y;
y = t;
} }
} }
return index >>> 0; return index >>> 0;
} }
function centroidFromPaletteHexes(hexByType: Record<string, string | undefined>) { function centroidFromPaletteHexes(
hexByType: Record<string, string | undefined>,
) {
const weights: Record<string, number> = { const weights: Record<string, number> = {
Vibrant: 0.7, Vibrant: 0.7,
Muted: 0.15, Muted: 0.15,
@ -41,21 +54,27 @@ function centroidFromPaletteHexes(hexByType: Record<string, string | undefined>)
}; };
const fallbackHex = const fallbackHex =
hexByType["Vibrant"] || hexByType.Vibrant ||
hexByType["Muted"] || hexByType.Muted ||
hexByType["DarkVibrant"] || hexByType.DarkVibrant ||
hexByType["DarkMuted"] || hexByType.DarkMuted ||
hexByType["LightVibrant"] || hexByType.LightVibrant ||
hexByType["LightMuted"]; hexByType.LightMuted;
let L = 0, A = 0, B = 0, W = 0; let L = 0,
A = 0,
B = 0,
W = 0;
for (const [type, w] of Object.entries(weights)) { for (const [type, w] of Object.entries(weights)) {
const hex = hexByType[type] ?? fallbackHex; const hex = hexByType[type] ?? fallbackHex;
if (!hex || w <= 0) continue; if (!hex || w <= 0) continue;
const c = toOklab(parse(hex)); const c = toOklab(parse(hex));
if (!c) continue; if (!c) continue;
L += c.l * w; A += c.a * w; B += c.b * w; W += w; L += c.l * w;
A += c.a * w;
B += c.b * w;
W += w;
} }
if (W === 0) return { l: 0.5, a: 0, b: 0 }; if (W === 0) return { l: 0.5, a: 0, b: 0 };
@ -78,7 +97,11 @@ export async function generateArtworkColorsForArtwork(artworkId: string) {
where: { id: artworkId }, where: { id: artworkId },
select: { select: {
file: { select: { fileKey: true } }, file: { select: { fileKey: true } },
variants: { where: { type: "original" }, select: { s3Key: true }, take: 1 }, variants: {
where: { type: "original" },
select: { s3Key: true },
take: 1,
},
}, },
}); });
if (!artwork) throw new Error("Artwork not found"); if (!artwork) throw new Error("Artwork not found");
@ -100,7 +123,9 @@ export async function generateArtworkColorsForArtwork(artworkId: string) {
for (const { type, hex } of vibrantHexes) { for (const { type, hex } of vibrantHexes) {
if (!hex) continue; if (!hex) continue;
const [r, g, b] = hex.match(/\w\w/g)!.map((h) => parseInt(h, 16)); const match = hex.match(/\w\w/g);
if (!match) continue;
const [r, g, b] = match.map((h) => parseInt(h, 16));
const name = generateColorName(hex); const name = generateColorName(hex);
const color = await prisma.color.upsert({ const color = await prisma.color.upsert({
@ -117,11 +142,14 @@ export async function generateArtworkColorsForArtwork(artworkId: string) {
} }
const hexByType: Record<string, string | undefined> = Object.fromEntries( const hexByType: Record<string, string | undefined> = Object.fromEntries(
vibrantHexes.map(({ type, hex }) => [type, hex]) vibrantHexes.map(({ type, hex }) => [type, hex]),
); );
const { l, a, b } = centroidFromPaletteHexes(hexByType); const { l, a, b } = centroidFromPaletteHexes(hexByType);
const sortKey = hilbertIndex15(norm(a, A_MIN, A_MAX), norm(b, B_MIN, B_MAX)); const sortKey = hilbertIndex15(
norm(a, A_MIN, A_MAX),
norm(b, B_MIN, B_MAX),
);
await prisma.artwork.update({ await prisma.artwork.update({
where: { id: artworkId }, where: { id: artworkId },
@ -144,6 +172,9 @@ export async function generateArtworkColorsForArtwork(artworkId: string) {
colorError: e instanceof Error ? e.message : "Color generation failed", colorError: e instanceof Error ? e.message : "Color generation failed",
}, },
}); });
return { ok: false as const, error: e instanceof Error ? e.message : "Color generation failed" }; return {
ok: false as const,
error: e instanceof Error ? e.message : "Color generation failed",
};
} }
} }

View File

@ -8,6 +8,7 @@ import sharp from "sharp";
const GALLERY_TARGET_SIZE = 300; const GALLERY_TARGET_SIZE = 300;
// Generates a gallery-sized variant for a single artwork.
export async function generateGalleryVariant( export async function generateGalleryVariant(
artworkId: string, artworkId: string,
opts?: { force?: boolean }, opts?: { force?: boolean },

View File

@ -2,10 +2,11 @@
import { prisma } from "@/lib/prisma"; import { prisma } from "@/lib/prisma";
// Returns color swatches for a given artwork.
export async function getArtworkColors(artworkId: string) { export async function getArtworkColors(artworkId: string) {
return prisma.artworkColor.findMany({ return prisma.artworkColor.findMany({
where: { artworkId }, where: { artworkId },
include: { color: true }, include: { color: true },
orderBy: [{ type: "asc" }], orderBy: [{ type: "asc" }],
}); });
} }

View File

@ -2,6 +2,7 @@
import { prisma } from "@/lib/prisma"; import { prisma } from "@/lib/prisma";
// Returns album/category options for artwork filters.
export async function getArtworkFilterOptions() { export async function getArtworkFilterOptions() {
const [albums, categories] = await Promise.all([ const [albums, categories] = await Promise.all([
prisma.album.findMany({ select: { id: true, name: true }, orderBy: { name: "asc" } }), prisma.album.findMany({ select: { id: true, name: true }, orderBy: { name: "asc" } }),

View File

@ -1,9 +1,10 @@
"use server" "use server";
import { prisma } from "@/lib/prisma" import { prisma } from "@/lib/prisma";
// Loads a single artwork with relations for the edit page.
export async function getSingleArtwork(id: string) { export async function getSingleArtwork(id: string) {
return await prisma.artwork.findUnique({ return prisma.artwork.findUnique({
where: { id }, where: { id },
include: { include: {
// album: true, // album: true,
@ -17,7 +18,7 @@ export async function getSingleArtwork(id: string) {
// sortContexts: true, // sortContexts: true,
tags: true, tags: true,
variants: true, variants: true,
timelapse: true timelapse: true,
} },
}) });
} }

View File

@ -1,15 +1,19 @@
"use server"; "use server";
import type { Prisma } from "@/generated/prisma/client";
import { prisma } from "@/lib/prisma"; import { prisma } from "@/lib/prisma";
import { ArtworkTableInput, artworkTableInputSchema, artworkTableOutputSchema } from "@/schemas/artworks/tableSchema"; import { type ArtworkTableInput, artworkTableInputSchema, artworkTableOutputSchema } from "@/schemas/artworks/tableSchema";
// Builds the admin artworks table page with filters, sorting, and pagination.
function triToBool(tri: "any" | "true" | "false"): boolean | undefined { function triToBool(tri: "any" | "true" | "false"): boolean | undefined {
if (tri === "any") return undefined; if (tri === "any") return undefined;
return tri === "true"; return tri === "true";
} }
function mapSortingToOrderBy(sorting: ArtworkTableInput["sorting"]) { function mapSortingToOrderBy(
const allowed: Record<string, (desc: boolean) => any> = { sorting: ArtworkTableInput["sorting"]
): Prisma.ArtworkOrderByWithRelationInput[] {
const allowed: Record<string, (desc: boolean) => Prisma.ArtworkOrderByWithRelationInput> = {
createdAt: (desc) => ({ createdAt: desc ? "desc" : "asc" }), createdAt: (desc) => ({ createdAt: desc ? "desc" : "asc" }),
updatedAt: (desc) => ({ updatedAt: desc ? "desc" : "asc" }), updatedAt: (desc) => ({ updatedAt: desc ? "desc" : "asc" }),
sortIndex: (desc) => ({ sortIndex: desc ? "desc" : "asc" }), sortIndex: (desc) => ({ sortIndex: desc ? "desc" : "asc" }),
@ -25,9 +29,10 @@ function mapSortingToOrderBy(sorting: ArtworkTableInput["sorting"]) {
tagsCount: (desc) => ({ tags: { _count: desc ? "desc" : "asc" } }), tagsCount: (desc) => ({ tags: { _count: desc ? "desc" : "asc" } }),
}; };
const orderBy = sorting const orderBy = sorting.flatMap((s) => {
.map((s) => allowed[s.id]?.(s.desc)) const mapper = allowed[s.id];
.filter(Boolean); return mapper ? [mapper(s.desc)] : [];
});
orderBy.push({ id: "desc" }); orderBy.push({ id: "desc" });
return orderBy; return orderBy;
@ -44,7 +49,7 @@ export async function getArtworksTablePage(input: unknown) {
const nsfw = triToBool(filters.nsfw); const nsfw = triToBool(filters.nsfw);
const needsWork = triToBool(filters.needsWork); const needsWork = triToBool(filters.needsWork);
const where: any = { const where: Prisma.ArtworkWhereInput = {
...(typeof published === "boolean" ? { published } : {}), ...(typeof published === "boolean" ? { published } : {}),
...(typeof nsfw === "boolean" ? { nsfw } : {}), ...(typeof nsfw === "boolean" ? { nsfw } : {}),
...(typeof needsWork === "boolean" ? { needsWork } : {}), ...(typeof needsWork === "boolean" ? { needsWork } : {}),

View File

@ -1,13 +1,9 @@
"use server"; "use server";
import { prisma } from "@/lib/prisma"; import { prisma } from "@/lib/prisma";
import type { GalleryVariantStats } from "@/types/Artwork";
export type GalleryVariantStats = { // Returns counts for gallery variant presence.
total: number;
withGallery: number;
missing: number;
};
export async function getGalleryVariantStats(): Promise<GalleryVariantStats> { export async function getGalleryVariantStats(): Promise<GalleryVariantStats> {
const [total, withGallery] = await Promise.all([ const [total, withGallery] = await Promise.all([
prisma.artwork.count(), prisma.artwork.count(),

View File

@ -2,25 +2,30 @@
import { prisma } from "@/lib/prisma"; import { prisma } from "@/lib/prisma";
import { s3 } from "@/lib/s3"; import { s3 } from "@/lib/s3";
import {
confirmArtworkTimelapseUploadSchema,
createArtworkTimelapseUploadSchema,
deleteArtworkTimelapseSchema,
setArtworkTimelapseEnabledSchema,
} from "@/schemas/artworks/timelapse";
import type {
ConfirmArtworkTimelapseUploadInput,
CreateArtworkTimelapseUploadInput,
DeleteArtworkTimelapseInput,
SetArtworkTimelapseEnabledInput,
} from "@/schemas/artworks/timelapse";
import { PutObjectCommand, DeleteObjectCommand } from "@aws-sdk/client-s3"; import { PutObjectCommand, DeleteObjectCommand } from "@aws-sdk/client-s3";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner"; import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
import { revalidatePath } from "next/cache"; import { revalidatePath } from "next/cache";
import { z } from "zod/v4";
import { v4 as uuidv4 } from "uuid"; import { v4 as uuidv4 } from "uuid";
const createUploadSchema = z.object({
artworkId: z.string().min(1),
fileName: z.string().min(1),
mimeType: z.string().min(1),
sizeBytes: z.number().int().positive(),
});
/** /**
* Creates a presigned PUT url so the client can upload large timelapse videos directly to S3 * Creates a presigned PUT url so the client can upload large timelapse videos directly to S3
* (avoids Next.js body-size/proxy limits). * (avoids Next.js body-size/proxy limits).
*/ */
export async function createArtworkTimelapseUpload(input: z.infer<typeof createUploadSchema>) { export async function createArtworkTimelapseUpload(input: CreateArtworkTimelapseUploadInput) {
const { artworkId, fileName, mimeType, sizeBytes } = createUploadSchema.parse(input); const { artworkId, fileName, mimeType, sizeBytes } =
createArtworkTimelapseUploadSchema.parse(input);
const ext = fileName.includes(".") ? fileName.split(".").pop() : undefined; const ext = fileName.includes(".") ? fileName.split(".").pop() : undefined;
const suffix = ext ? `.${ext}` : ""; const suffix = ext ? `.${ext}` : "";
@ -41,17 +46,10 @@ export async function createArtworkTimelapseUpload(input: z.infer<typeof createU
return { uploadUrl, s3Key, fileName, mimeType, sizeBytes }; return { uploadUrl, s3Key, fileName, mimeType, sizeBytes };
} }
const confirmSchema = z.object({
artworkId: z.string().min(1),
s3Key: z.string().min(1),
fileName: z.string().min(1),
mimeType: z.string().min(1),
sizeBytes: z.number().int().positive(),
});
/** Persist uploaded timelapse metadata in DB (upsert by artworkId). */ /** Persist uploaded timelapse metadata in DB (upsert by artworkId). */
export async function confirmArtworkTimelapseUpload(input: z.infer<typeof confirmSchema>) { export async function confirmArtworkTimelapseUpload(input: ConfirmArtworkTimelapseUploadInput) {
const { artworkId, s3Key, fileName, mimeType, sizeBytes } = confirmSchema.parse(input); const { artworkId, s3Key, fileName, mimeType, sizeBytes } =
confirmArtworkTimelapseUploadSchema.parse(input);
// If an old timelapse exists, delete the old object so you don't leak storage. // If an old timelapse exists, delete the old object so you don't leak storage.
const existing = await prisma.artworkTimelapse.findUnique({ where: { artworkId } }); const existing = await prisma.artworkTimelapse.findUnique({ where: { artworkId } });
@ -91,13 +89,8 @@ export async function confirmArtworkTimelapseUpload(input: z.infer<typeof confir
return { ok: true }; return { ok: true };
} }
const enabledSchema = z.object({ export async function setArtworkTimelapseEnabled(input: SetArtworkTimelapseEnabledInput) {
artworkId: z.string().min(1), const { artworkId, enabled } = setArtworkTimelapseEnabledSchema.parse(input);
enabled: z.boolean(),
});
export async function setArtworkTimelapseEnabled(input: z.infer<typeof enabledSchema>) {
const { artworkId, enabled } = enabledSchema.parse(input);
await prisma.artworkTimelapse.update({ await prisma.artworkTimelapse.update({
where: { artworkId }, where: { artworkId },
@ -108,12 +101,8 @@ export async function setArtworkTimelapseEnabled(input: z.infer<typeof enabledSc
return { ok: true }; return { ok: true };
} }
const deleteSchema = z.object({ export async function deleteArtworkTimelapse(input: DeleteArtworkTimelapseInput) {
artworkId: z.string().min(1), const { artworkId } = deleteArtworkTimelapseSchema.parse(input);
});
export async function deleteArtworkTimelapse(input: z.infer<typeof deleteSchema>) {
const { artworkId } = deleteSchema.parse(input);
const existing = await prisma.artworkTimelapse.findUnique({ where: { artworkId } }); const existing = await prisma.artworkTimelapse.findUnique({ where: { artworkId } });
if (!existing) return { ok: true }; if (!existing) return { ok: true };

View File

@ -1,12 +1,13 @@
"use server" "use server";
import { prisma } from "@/lib/prisma"; import { prisma } from "@/lib/prisma";
import { artworkSchema } from "@/schemas/artworks/imageSchema"; import { artworkSchema } from "@/schemas/artworks/imageSchema";
import { normalizeNames, slugify } from "@/utils/artworkHelpers"; import { normalizeNames, slugify } from "@/utils/artworkHelpers";
import { z } from "zod/v4"; import type { z } from "zod/v4";
// Updates an artwork and its tag/category relationships.
export async function updateArtwork( export async function updateArtwork(
values: z.infer<typeof artworkSchema>, values: z.infer<typeof artworkSchema>,
id: string id: string
) { ) {
const validated = artworkSchema.safeParse(values); const validated = artworkSchema.safeParse(values);
@ -36,12 +37,11 @@ export async function updateArtwork(
const categoriesToCreate = normalizeNames(newCategoryNames); const categoriesToCreate = normalizeNames(newCategoryNames);
const updatedArtwork = await prisma.$transaction(async (tx) => { const updatedArtwork = await prisma.$transaction(async (tx) => {
if (setAsHeader) {
if(setAsHeader) {
await tx.artwork.updateMany({ await tx.artwork.updateMany({
where: { setAsHeader: true }, where: { setAsHeader: true },
data: { setAsHeader: false }, data: { setAsHeader: false },
}) });
} }
const tagsRelation = const tagsRelation =
@ -73,7 +73,7 @@ export async function updateArtwork(
: {}; : {};
return tx.artwork.update({ return tx.artwork.update({
where: { id: id }, where: { id },
data: { data: {
name, name,
slug: slugify(name), slug: slugify(name),
@ -94,4 +94,4 @@ export async function updateArtwork(
}); });
return updatedArtwork; return updatedArtwork;
} }

View File

@ -2,28 +2,25 @@
import { auth } from "@/lib/auth"; import { auth } from "@/lib/auth";
import { prisma } from "@/lib/prisma"; import { prisma } from "@/lib/prisma";
import { z } from "zod/v4"; import type { RegisterFirstUserInput } from "@/schemas/auth";
import { registerFirstUserSchema } from "@/schemas/auth";
import type { SignUpResponse } from "@/types/auth";
const schema = z.object({ // Registers the very first user and upgrades them to admin.
name: z.string().min(1).max(200), export async function registerFirstUser(input: RegisterFirstUserInput) {
email: z.string().email().max(320),
password: z.string().min(8).max(128),
});
export async function registerFirstUser(input: z.infer<typeof schema>) {
const count = await prisma.user.count(); const count = await prisma.user.count();
if (count !== 0) throw new Error("Registration is disabled."); if (count !== 0) throw new Error("Registration is disabled.");
const { name, email, password } = schema.parse(input); const { name, email, password } = registerFirstUserSchema.parse(input);
const res = await auth.api.signUpEmail({ const res = (await auth.api.signUpEmail({
body: { name, email, password }, body: { name, email, password },
}); })) as SignUpResponse;
const userId = const userId =
(res as any)?.user?.id ?? res.user?.id ??
(res as any)?.data?.user?.id ?? res.data?.user?.id ??
(res as any)?.data?.id; res.data?.id;
if (!userId) throw new Error("Signup failed: no user id returned."); if (!userId) throw new Error("Signup failed: no user id returned.");

View File

@ -1,25 +1,27 @@
"use server" "use server";
import { prisma } from "@/lib/prisma" import { prisma } from "@/lib/prisma";
import { categorySchema } from "@/schemas/artworks/categorySchema" import { categorySchema } from "@/schemas/artworks/categorySchema";
import type * as z from "zod/v4";
export async function createCategory(formData: categorySchema) { // Creates a new artwork category.
const parsed = categorySchema.safeParse(formData) export async function createCategory(formData: z.infer<typeof categorySchema>) {
const parsed = categorySchema.safeParse(formData);
if (!parsed.success) { if (!parsed.success) {
console.error("Validation failed", parsed.error) console.error("Validation failed", parsed.error);
throw new Error("Invalid input") throw new Error("Invalid input");
} }
const data = parsed.data const data = parsed.data;
const created = await prisma.artCategory.create({ const created = await prisma.artCategory.create({
data: { data: {
name: data.name, name: data.name,
slug: data.slug, slug: data.slug,
description: data.description description: data.description,
}, },
}) });
return created return created;
} }

View File

@ -3,6 +3,7 @@
import { prisma } from "@/lib/prisma"; import { prisma } from "@/lib/prisma";
import { revalidatePath } from "next/cache"; import { revalidatePath } from "next/cache";
// Deletes a category if it is not referenced by artworks or tags.
export async function deleteCategory(catId: string) { export async function deleteCategory(catId: string) {
const cat = await prisma.artCategory.findUnique({ const cat = await prisma.artCategory.findUnique({
where: { id: catId }, where: { id: catId },
@ -10,8 +11,8 @@ export async function deleteCategory(catId: string) {
id: true, id: true,
_count: { _count: {
select: { select: {
tags: true, tagLinks: true,
artworks: true artworks: true,
}, },
}, },
}, },
@ -25,13 +26,13 @@ export async function deleteCategory(catId: string) {
throw new Error("Cannot delete category: it is used by artworks."); throw new Error("Cannot delete category: it is used by artworks.");
} }
if (cat._count.tags > 0) { if (cat._count.tagLinks > 0) {
throw new Error("Cannot delete category: it is used by tags."); throw new Error("Cannot delete category: it is used by tags.");
} }
await prisma.artCategory.delete({ where: { id: catId } }); await prisma.artCategory.delete({ where: { id: catId } });
revalidatePath("/categories"); revalidatePath("/categories");
return { success: true }; return { success: true };
} }

View File

@ -1,16 +1,20 @@
"use server" "use server";
import { prisma } from "@/lib/prisma" import { prisma } from "@/lib/prisma";
// Category data fetchers for admin pages.
export async function getCategoriesWithTags() { export async function getCategoriesWithTags() {
return await prisma.artCategory.findMany({ include: { tags: true }, orderBy: { sortIndex: "asc" } }) return prisma.artCategory.findMany({
include: { tagLinks: { include: { tag: true } } },
orderBy: { sortIndex: "asc" },
});
} }
export async function getCategoriesWithCount() { export async function getCategoriesWithCount() {
return await prisma.artCategory.findMany({ return prisma.artCategory.findMany({
include: { include: {
_count: { select: { artworks: true, tags: true } }, _count: { select: { artworks: true, tagLinks: true } },
}, },
orderBy: [{ sortIndex: "asc" }, { name: "asc" }], orderBy: [{ sortIndex: "asc" }, { name: "asc" }],
}) });
} }

View File

@ -1,27 +1,28 @@
"use server" "use server";
import { prisma } from '@/lib/prisma'; import { prisma } from "@/lib/prisma";
import { categorySchema } from '@/schemas/artworks/categorySchema'; import { categorySchema } from "@/schemas/artworks/categorySchema";
import { z } from 'zod/v4'; import type * as z from "zod/v4";
// Updates an artwork category by id.
export async function updateCategory(id: string, rawData: z.infer<typeof categorySchema>) { export async function updateCategory(id: string, rawData: z.infer<typeof categorySchema>) {
const parsed = categorySchema.safeParse(rawData) const parsed = categorySchema.safeParse(rawData);
if (!parsed.success) { if (!parsed.success) {
console.error("Validation failed", parsed.error) console.error("Validation failed", parsed.error);
throw new Error("Invalid input") throw new Error("Invalid input");
} }
const data = parsed.data const data = parsed.data;
const updated = await prisma.artCategory.update({ const updated = await prisma.artCategory.update({
where: { id }, where: { id },
data: { data: {
name: data.name, name: data.name,
slug: data.slug, slug: data.slug,
description: data.description description: data.description,
}, },
}) });
return updated return updated;
} }

View File

@ -1,16 +1,9 @@
"use server"; "use server";
import { prisma } from "@/lib/prisma"; import { prisma } from "@/lib/prisma";
import type { ArtworkColorStats } from "@/types/colors";
export type ArtworkColorStats = { // Aggregates color-processing status counts for artworks.
total: number;
ready: number;
pending: number;
processing: number;
failed: number;
missingSortKey: number;
};
export async function getArtworkColorStats(): Promise<ArtworkColorStats> { export async function getArtworkColorStats(): Promise<ArtworkColorStats> {
const [ const [
total, total,

View File

@ -1,17 +1,11 @@
"use server"; "use server";
import { Prisma } from "@/generated/prisma/client"; import type { Prisma } from "@/generated/prisma/client";
import { prisma } from "@/lib/prisma"; import { prisma } from "@/lib/prisma";
import { generateArtworkColorsForArtwork } from "../artworks/generateArtworkColors"; import { generateArtworkColorsForArtwork } from "../artworks/generateArtworkColors";
import type { ProcessColorsResult } from "@/types/colors";
export type ProcessColorsResult = { // Processes pending/failed artwork colors with a configurable batch size.
picked: number;
processed: number;
ok: number;
failed: number;
results: Array<{ artworkId: string; ok: boolean; error?: string }>;
};
export async function processPendingArtworkColors(args?: { export async function processPendingArtworkColors(args?: {
limit?: number; limit?: number;
includeFailed?: boolean; includeFailed?: boolean;

View File

@ -2,6 +2,7 @@
import { prisma } from "@/lib/prisma"; import { prisma } from "@/lib/prisma";
// Deletes a custom commission card by id.
export async function deleteCommissionCustomCard(id: string) { export async function deleteCommissionCustomCard(id: string) {
await prisma.commissionCustomCard.delete({ await prisma.commissionCustomCard.delete({
where: { id }, where: { id },

View File

@ -1,6 +1,7 @@
"use server"; "use server";
import { s3 } from "@/lib/s3"; import { s3 } from "@/lib/s3";
import type { CommissionCustomCardImageItem } from "@/types/commissions";
import { import {
DeleteObjectCommand, DeleteObjectCommand,
ListObjectsV2Command, ListObjectsV2Command,
@ -9,13 +10,6 @@ import {
const PREFIX = "commissions/custom-cards/"; const PREFIX = "commissions/custom-cards/";
export type CommissionCustomCardImageItem = {
key: string;
url: string;
size: number | null;
lastModified: string | null;
};
function buildImageUrl(key: string) { function buildImageUrl(key: string) {
return `/api/image/${encodeURI(key)}`; return `/api/image/${encodeURI(key)}`;
} }

View File

@ -6,6 +6,7 @@ import {
type CommissionCustomCardValues, type CommissionCustomCardValues,
} from "@/schemas/commissionCustomCard"; } from "@/schemas/commissionCustomCard";
// Creates a new custom commission card with options/extras.
export async function createCommissionCustomCard( export async function createCommissionCustomCard(
formData: CommissionCustomCardValues formData: CommissionCustomCardValues
) { ) {
@ -25,6 +26,9 @@ export async function createCommissionCustomCard(
referenceImageUrl: data.referenceImageUrl ?? null, referenceImageUrl: data.referenceImageUrl ?? null,
isVisible: data.isVisible ?? true, isVisible: data.isVisible ?? true,
isSpecialOffer: data.isSpecialOffer ?? false, isSpecialOffer: data.isSpecialOffer ?? false,
tags: data.tagIds?.length
? { connect: data.tagIds.map((id) => ({ id })) }
: undefined,
options: { options: {
create: create:
data.options?.map((opt, index) => ({ data.options?.map((opt, index) => ({

View File

@ -6,6 +6,7 @@ import {
type CommissionCustomCardValues, type CommissionCustomCardValues,
} from "@/schemas/commissionCustomCard"; } from "@/schemas/commissionCustomCard";
// Updates a custom commission card and resets related options/extras.
export async function updateCommissionCustomCard( export async function updateCommissionCustomCard(
id: string, id: string,
rawData: CommissionCustomCardValues rawData: CommissionCustomCardValues
@ -20,6 +21,12 @@ export async function updateCommissionCustomCard(
referenceImageUrl: data.referenceImageUrl ?? null, referenceImageUrl: data.referenceImageUrl ?? null,
isVisible: data.isVisible ?? true, isVisible: data.isVisible ?? true,
isSpecialOffer: data.isSpecialOffer ?? false, isSpecialOffer: data.isSpecialOffer ?? false,
tags: data.tagIds
? {
set: [],
connect: data.tagIds.map((id) => ({ id })),
}
: undefined,
options: { options: {
deleteMany: {}, deleteMany: {},
create: data.options?.map((opt, index) => ({ create: data.options?.map((opt, index) => ({

View File

@ -2,6 +2,7 @@
import { prisma } from "@/lib/prisma"; import { prisma } from "@/lib/prisma";
// Updates sort order for custom commission cards.
export async function updateCommissionCustomCardSortOrder( export async function updateCommissionCustomCardSortOrder(
items: { id: string; sortIndex: number }[] items: { id: string; sortIndex: number }[]
) { ) {

View File

@ -1,6 +1,7 @@
"use server"; "use server";
import { s3 } from "@/lib/s3"; import { s3 } from "@/lib/s3";
import type { CommissionExampleItem } from "@/types/commissions";
import { import {
DeleteObjectCommand, DeleteObjectCommand,
ListObjectsV2Command, ListObjectsV2Command,
@ -9,13 +10,6 @@ import {
const PREFIX = "commissions/examples/"; const PREFIX = "commissions/examples/";
export type CommissionExampleItem = {
key: string;
url: string;
size: number | null;
lastModified: string | null;
};
function buildImageUrl(key: string) { function buildImageUrl(key: string) {
return `/api/image/${encodeURI(key)}`; return `/api/image/${encodeURI(key)}`;
} }

View File

@ -1,7 +1,8 @@
'use server'; "use server";
import { prisma } from "@/lib/prisma"; import { prisma } from "@/lib/prisma";
// Returns the latest active commission guidelines (markdown + example image).
export async function getActiveGuidelines(): Promise<{ export async function getActiveGuidelines(): Promise<{
markdown: string | null; markdown: string | null;
exampleImageUrl: string | null; exampleImageUrl: string | null;

View File

@ -1,7 +1,8 @@
'use server'; "use server";
import { prisma } from "@/lib/prisma"; import { prisma } from "@/lib/prisma";
// Deactivates existing guidelines and creates a new active version.
export async function saveGuidelines(markdown: string, exampleImageUrl: string | null) { export async function saveGuidelines(markdown: string, exampleImageUrl: string | null) {
await prisma.commissionGuidelines.updateMany({ await prisma.commissionGuidelines.updateMany({
where: { isActive: true }, where: { isActive: true },

View File

@ -1,8 +1,9 @@
"use server"; "use server";
import { prisma } from "@/lib/prisma"; import { prisma } from "@/lib/prisma";
import { z } from "zod"; import { z } from "zod/v4";
// Deletes a commission request by id.
export async function deleteCommissionRequest(id: string) { export async function deleteCommissionRequest(id: string) {
const parsed = z.string().min(1).parse(id); const parsed = z.string().min(1).parse(id);

View File

@ -1,8 +1,9 @@
"use server"; "use server";
import { prisma } from "@/lib/prisma"; import { prisma } from "@/lib/prisma";
import { calculatePriceRange, PriceSource } from "@/utils/commissionPricing"; import { calculatePriceRange, type PriceSource } from "@/utils/commissionPricing";
// Loads a commission request with related data and computed price estimate.
export async function getCommissionRequestById(id: string) { export async function getCommissionRequestById(id: string) {
const req = await prisma.commissionRequest.findUnique({ const req = await prisma.commissionRequest.findUnique({
where: { id }, where: { id },

View File

@ -1,39 +1,24 @@
"use server"; "use server";
import type { Prisma } from "@/generated/prisma/client";
import { prisma } from "@/lib/prisma"; import { prisma } from "@/lib/prisma";
import { import { commissionRequestTableRowSchema } from "@/schemas/commissions/requests";
commissionRequestTableRowSchema, import type {
commissionStatusSchema, CommissionRequestsTableFilters,
} from "@/schemas/commissions/requests"; CommissionRequestsTableSorting,
import { z } from "zod"; } from "@/schemas/commissions/requestsTable";
import type { CursorPagination } from "@/types/pagination";
export type CursorPagination = { pageIndex: number; pageSize: number }; import { z } from "zod/v4";
export type SortDir = "asc" | "desc";
const triStateSchema = z.enum(["any", "true", "false"]);
const sortingSchema = z.array(
z.object({
id: z.string(),
desc: z.boolean(),
})
);
const filtersSchema = z.object({
q: z.string().optional(),
email: z.string().optional(),
status: z.union([z.literal("any"), commissionStatusSchema]).default("any"),
hasFiles: triStateSchema.default("any"),
});
// Builds a paginated, filtered, and sorted commission-requests table payload for the admin UI.
export async function getCommissionRequestsTablePage(input: { export async function getCommissionRequestsTablePage(input: {
pagination: CursorPagination; pagination: CursorPagination;
sorting: z.infer<typeof sortingSchema>; sorting: CommissionRequestsTableSorting;
filters: z.infer<typeof filtersSchema>; filters: CommissionRequestsTableFilters;
}) { }) {
const { pagination, sorting, filters } = input; const { pagination, sorting, filters } = input;
const where: any = {}; const where: Prisma.CommissionRequestWhereInput = {};
if (filters.q) { if (filters.q) {
const q = filters.q.trim(); const q = filters.q.trim();
@ -60,7 +45,7 @@ export async function getCommissionRequestsTablePage(input: {
// sorting // sorting
const sort = sorting?.[0] ?? { id: "createdAt", desc: true }; const sort = sorting?.[0] ?? { id: "createdAt", desc: true };
const orderBy: any = const orderBy: Prisma.CommissionRequestOrderByWithRelationInput =
sort.id === "createdAt" sort.id === "createdAt"
? { createdAt: sort.desc ? "desc" : "asc" } ? { createdAt: sort.desc ? "desc" : "asc" }
: sort.id === "status" : sort.id === "status"
@ -94,7 +79,7 @@ export async function getCommissionRequestsTablePage(input: {
customerName: r.customerName, customerName: r.customerName,
customerEmail: r.customerEmail, customerEmail: r.customerEmail,
customerSocials: r.customerSocials ?? null, customerSocials: r.customerSocials ?? null,
status: r.status as any, status: r.status,
fileCount: r._count.files, fileCount: r._count.files,
})); }));

View File

@ -2,8 +2,9 @@
import { prisma } from "@/lib/prisma"; import { prisma } from "@/lib/prisma";
import { commissionStatusSchema } from "@/schemas/commissions/requests"; import { commissionStatusSchema } from "@/schemas/commissions/requests";
import { z } from "zod"; import { z } from "zod/v4";
// Sets a commission request status (admin action).
export async function setCommissionRequestStatus(input: { export async function setCommissionRequestStatus(input: {
id: string; id: string;
status: z.infer<typeof commissionStatusSchema>; status: z.infer<typeof commissionStatusSchema>;

View File

@ -1,20 +1,14 @@
"use server"; "use server";
import { prisma } from "@/lib/prisma"; import { prisma } from "@/lib/prisma";
import { commissionStatusSchema } from "@/schemas/commissions/requests"; import {
import { z } from "zod/v4"; updateCommissionRequestSchema,
} from "@/schemas/commissions/updateRequest";
import type { UpdateCommissionRequestInput } from "@/schemas/commissions/updateRequest";
const updateSchema = z.object({ // Updates editable fields on a commission request.
id: z.string().min(1), export async function updateCommissionRequest(input: UpdateCommissionRequestInput) {
status: commissionStatusSchema, const data = updateCommissionRequestSchema.parse(input);
customerName: z.string().min(1).max(200),
customerEmail: z.string().email().max(320),
customerSocials: z.string().max(2000).optional().nullable(),
message: z.string().min(1).max(20_000),
});
export async function updateCommissionRequest(input: z.infer<typeof updateSchema>) {
const data = updateSchema.parse(input);
await prisma.commissionRequest.update({ await prisma.commissionRequest.update({
where: { id: data.id }, where: { id: data.id },

View File

@ -1,21 +1,18 @@
"use server"; "use server";
import { revalidatePath } from "next/cache"; import { revalidatePath } from "next/cache";
import { z } from "zod";
import { COMMISSION_STATUSES } from "@/lib/commissions/kanban"; import { prisma } from "@/lib/prisma";
import { prisma } from "@/lib/prisma"; // adjust to your prisma import import {
// import { requireAdmin } from "@/lib/auth/requireAdmin"; // recommended if you have it updateCommissionRequestStatusSchema,
} from "@/schemas/commissions/updateRequestStatus";
import type { UpdateCommissionRequestStatusInput } from "@/schemas/commissions/updateRequestStatus";
const schema = z.object({ // Updates a commission request status and revalidates the kanban page.
id: z.string().min(1), export async function updateCommissionRequestStatus(
status: z.enum(COMMISSION_STATUSES), input: UpdateCommissionRequestStatusInput
}); ) {
const { id, status } = updateCommissionRequestStatusSchema.parse(input);
export async function updateCommissionRequestStatus(input: z.infer<typeof schema>) {
// await requireAdmin(); // enforce auth/role check here
const { id, status } = schema.parse(input);
await prisma.commissionRequest.update({ await prisma.commissionRequest.update({
where: { id }, where: { id },
@ -23,5 +20,5 @@ export async function updateCommissionRequestStatus(input: z.infer<typeof schema
}); });
// revalidate the board page so a refresh always reflects server truth // revalidate the board page so a refresh always reflects server truth
revalidatePath("/commissions/board"); revalidatePath("/commissions/kanban");
} }

View File

@ -1,19 +1,18 @@
"use server" "use server";
import { prisma } from "@/lib/prisma" import { prisma } from "@/lib/prisma";
// Deletes a commission type and its link records.
export async function deleteCommissionType(typeId: string) { export async function deleteCommissionType(typeId: string) {
await prisma.commissionTypeOption.deleteMany({ await prisma.commissionTypeOption.deleteMany({
where: { typeId }, where: { typeId },
}) });
await prisma.commissionTypeExtra.deleteMany({ await prisma.commissionTypeExtra.deleteMany({
where: { typeId }, where: { typeId },
}) });
await prisma.commissionType.delete({ await prisma.commissionType.delete({
where: { id: typeId }, where: { id: typeId },
}) });
}
}

View File

@ -4,8 +4,9 @@ import { prisma } from "@/lib/prisma";
import { commissionExtraSchema } from "@/schemas/commissionType"; import { commissionExtraSchema } from "@/schemas/commissionType";
import { revalidatePath } from "next/cache"; import { revalidatePath } from "next/cache";
const LIST_PATH = "/commissions/extras"; const LIST_PATH = "/commissions/types/extras";
// CRUD helpers for commission extras (admin-only pages).
export async function createCommissionExtra(input: unknown) { export async function createCommissionExtra(input: unknown) {
const data = commissionExtraSchema.parse(input); const data = commissionExtraSchema.parse(input);
const created = await prisma.commissionExtra.create({ data }); const created = await prisma.commissionExtra.create({ data });
@ -24,7 +25,6 @@ export async function deleteCommissionExtra(id: string) {
// Optional safety: // Optional safety:
// const used = await prisma.commissionTypeExtra.count({ where: { extraId: id } }); // const used = await prisma.commissionTypeExtra.count({ where: { extraId: id } });
// if (used > 0) throw new Error("Extra is linked to types."); // if (used > 0) throw new Error("Extra is linked to types.");
console.log("TBD"); await prisma.commissionExtra.delete({ where: { id } });
// await prisma.commissionExtra.delete({ where: { id } }); revalidatePath(LIST_PATH);
// revalidatePath(LIST_PATH);
} }

View File

@ -1,81 +1,94 @@
"use server" "use server";
import { prisma } from "@/lib/prisma" import { prisma } from "@/lib/prisma";
import { commissionTypeSchema } from "@/schemas/commissionType" import { commissionTypeSchema } from "@/schemas/commissionType";
import type * as z from "zod/v4";
// Creates a commission option entry.
export async function createCommissionOption(data: { name: string }) { export async function createCommissionOption(data: { name: string }) {
return await prisma.commissionOption.create({ return prisma.commissionOption.create({
data: { data: {
name: data.name, name: data.name,
description: "", description: "",
}, },
}) });
} }
// Creates a commission extra entry.
export async function createCommissionExtra(data: { name: string }) { export async function createCommissionExtra(data: { name: string }) {
return await prisma.commissionExtra.create({ return prisma.commissionExtra.create({
data: { data: {
name: data.name, name: data.name,
description: "", description: "",
}, },
}) });
} }
// Creates a commission custom input entry.
export async function createCommissionCustomInput(data: { export async function createCommissionCustomInput(data: {
name: string name: string;
fieldId: string fieldId: string;
}) { }) {
return await prisma.commissionCustomInput.create({ return prisma.commissionCustomInput.create({
data: { data: {
name: data.name, name: data.name,
fieldId: data.fieldId, fieldId: data.fieldId,
}, },
}) });
} }
export async function createCommissionType(formData: commissionTypeSchema) { // Creates a commission type with nested options/extras/custom inputs.
const parsed = commissionTypeSchema.safeParse(formData) export async function createCommissionType(
formData: z.infer<typeof commissionTypeSchema>
) {
const parsed = commissionTypeSchema.safeParse(formData);
if (!parsed.success) { if (!parsed.success) {
console.error("Validation failed", parsed.error) console.error("Validation failed", parsed.error);
throw new Error("Invalid input") throw new Error("Invalid input");
} }
const data = parsed.data const data = parsed.data;
const created = await prisma.commissionType.create({ const created = await prisma.commissionType.create({
data: { data: {
name: data.name, name: data.name,
description: data.description, description: data.description,
tags: data.tagIds?.length
? { connect: data.tagIds.map((id) => ({ id })) }
: undefined,
options: { options: {
create: data.options?.map((opt, index) => ({ create:
option: { connect: { id: opt.optionId } }, data.options?.map((opt, index) => ({
price: opt.price, option: { connect: { id: opt.optionId } },
pricePercent: opt.pricePercent, price: opt.price,
priceRange: opt.priceRange, pricePercent: opt.pricePercent,
sortIndex: index, priceRange: opt.priceRange,
})) || [], sortIndex: index,
})) || [],
}, },
extras: { extras: {
create: data.extras?.map((ext, index) => ({ create:
extra: { connect: { id: ext.extraId } }, data.extras?.map((ext, index) => ({
price: ext.price, extra: { connect: { id: ext.extraId } },
pricePercent: ext.pricePercent, price: ext.price,
priceRange: ext.priceRange, pricePercent: ext.pricePercent,
sortIndex: index, priceRange: ext.priceRange,
})) || [], sortIndex: index,
})) || [],
}, },
customInputs: { customInputs: {
create: data.customInputs?.map((c, index) => ({ create:
customInput: { connect: { id: c.customInputId } }, data.customInputs?.map((c, index) => ({
label: c.label, customInput: { connect: { id: c.customInputId } },
inputType: c.inputType, label: c.label,
required: c.required, inputType: c.inputType,
sortIndex: index, required: c.required,
})) || [], sortIndex: index,
})) || [],
}, },
}, },
}) });
return created return created;
} }

View File

@ -4,13 +4,9 @@ import { prisma } from "@/lib/prisma";
import { commissionOptionSchema } from "@/schemas/commissionType"; import { commissionOptionSchema } from "@/schemas/commissionType";
import { revalidatePath } from "next/cache"; import { revalidatePath } from "next/cache";
const LIST_PATH = "/commissions/options"; const LIST_PATH = "/commissions/types/options";
function toInt(v: string) {
const n = Number.parseInt(v, 10);
return Number.isFinite(n) ? n : 0;
}
// CRUD helpers for commission options (admin-only pages).
export async function createCommissionOption(input: unknown) { export async function createCommissionOption(input: unknown) {
const data = commissionOptionSchema.parse(input); const data = commissionOptionSchema.parse(input);
const created = await prisma.commissionOption.create({ const created = await prisma.commissionOption.create({
@ -37,7 +33,6 @@ export async function updateCommissionOption(id: string, input: unknown) {
} }
export async function deleteCommissionOption(id: string) { export async function deleteCommissionOption(id: string) {
console.log("TBD"); await prisma.commissionOption.delete({ where: { id } });
// await prisma.commissionOption.delete({ where: { id } }); revalidatePath(LIST_PATH);
// revalidatePath(LIST_PATH);
} }

View File

@ -1,7 +1,8 @@
"use server" "use server";
import { prisma } from "@/lib/prisma"; import { prisma } from "@/lib/prisma";
// Updates sort order for commission types.
export async function updateCommissionTypeSortOrder( export async function updateCommissionTypeSortOrder(
ordered: { id: string; sortIndex: number }[] ordered: { id: string; sortIndex: number }[]
) { ) {
@ -10,7 +11,7 @@ export async function updateCommissionTypeSortOrder(
where: { id }, where: { id },
data: { sortIndex }, data: { sortIndex },
}) })
) );
await Promise.all(updates) await Promise.all(updates);
} }

View File

@ -1,20 +1,27 @@
"use server" "use server";
import { prisma } from "@/lib/prisma" import { prisma } from "@/lib/prisma";
import { commissionTypeSchema } from "@/schemas/commissionType" import { commissionTypeSchema } from "@/schemas/commissionType";
import * as z from "zod/v4" import type * as z from "zod/v4";
// Updates a commission type and resets related nested records.
export async function updateCommissionType( export async function updateCommissionType(
id: string, id: string,
rawData: z.infer<typeof commissionTypeSchema> rawData: z.infer<typeof commissionTypeSchema>
) { ) {
const data = commissionTypeSchema.parse(rawData) const data = commissionTypeSchema.parse(rawData);
const updated = await prisma.commissionType.update({ const updated = await prisma.commissionType.update({
where: { id }, where: { id },
data: { data: {
name: data.name, name: data.name,
description: data.description, description: data.description,
tags: data.tagIds
? {
set: [],
connect: data.tagIds.map((id) => ({ id })),
}
: undefined,
options: { options: {
deleteMany: {}, deleteMany: {},
create: data.options?.map((opt, index) => ({ create: data.options?.map((opt, index) => ({
@ -37,13 +44,14 @@ export async function updateCommissionType(
}, },
customInputs: { customInputs: {
deleteMany: {}, deleteMany: {},
create: data.customInputs?.map((c, index) => ({ create:
customInput: { connect: { id: c.customInputId } }, data.customInputs?.map((c, index) => ({
label: c.label, customInput: { connect: { id: c.customInputId } },
inputType: c.inputType, label: c.label,
required: c.required, inputType: c.inputType,
sortIndex: index, required: c.required,
})) || [], sortIndex: index,
})) || [],
}, },
}, },
include: { include: {
@ -51,7 +59,7 @@ export async function updateCommissionType(
extras: true, extras: true,
customInputs: true, customInputs: true,
}, },
}) });
return updated return updated;
} }

View File

@ -1,12 +1,10 @@
"use server"; "use server";
import { prisma } from "@/lib/prisma"; import { prisma } from "@/lib/prisma";
import type { CountRow } from "@/types/dashboard";
type CountRow<K extends string> = { // Aggregates dashboard stats for admin overview cards and tables.
[P in K]: string; function toCountMapSafe<K extends string>(rows: Array<CountRow<K>>, key: K) {
} & { _count: { _all: number } };
function toCountMapSafe(rows: any[], key: string) {
const out: Record<string, number> = {}; const out: Record<string, number> = {};
for (const r of rows) out[String(r[key])] = Number(r?._count?._all ?? 0); for (const r of rows) out[String(r[key])] = Number(r?._count?._all ?? 0);
return out; return out;

View File

@ -1,46 +1,48 @@
"use server" "use server";
import { prisma } from "@/lib/prisma" import { prisma } from "@/lib/prisma";
import { TagFormInput, tagSchema } from "@/schemas/artworks/tagSchema" import { tagSchema } from "@/schemas/artworks/tagSchema";
import type { TagFormInput } from "@/schemas/artworks/tagSchema";
// Creates a tag and related category links/aliases.
export async function createTag(formData: TagFormInput) { export async function createTag(formData: TagFormInput) {
const parsed = tagSchema.safeParse(formData) const parsed = tagSchema.safeParse(formData);
if (!parsed.success) { if (!parsed.success) {
console.error("Validation failed", parsed.error) console.error("Validation failed", parsed.error);
throw new Error("Invalid input") throw new Error("Invalid input");
} }
const data = parsed.data const data = parsed.data;
const parentId = data.parentId ?? null; const parentId = data.parentId ?? null;
const tagSlug = data.name.toLowerCase().replace(/\s+/g, "-"); const tagSlug = data.name.toLowerCase().replace(/\s+/g, "-");
const created = await prisma.$transaction(async (tx) => { const created = await prisma.$transaction(async (tx) => {
const tag = await tx.artTag.create({ const tag = await tx.tag.create({
data: { data: {
name: data.name, name: data.name,
slug: tagSlug, slug: tagSlug,
description: data.description, description: data.description,
isParent: data.isParent, isVisible: data.isVisible ?? true,
showOnAnimalPage: data.showOnAnimalPage,
parentId
}, },
}); });
if (data.categoryIds) { if (data.categoryIds) {
await tx.artTag.update({ await tx.tagCategory.createMany({
where: { id: tag.id }, data: data.categoryIds.map((categoryId) => ({
data: { tagId: tag.id,
categories: { categoryId,
set: data.categoryIds.map(id => ({ id })) isParent: data.isParent,
} showOnAnimalPage: data.showOnAnimalPage,
} parentTagId: parentId,
})),
skipDuplicates: true,
}); });
} }
if (data.aliases && data.aliases.length > 0) { if (data.aliases && data.aliases.length > 0) {
await tx.artTagAlias.createMany({ await tx.tagAlias.createMany({
data: data.aliases.map((alias) => ({ data: data.aliases.map((alias) => ({
tagId: tag.id, tagId: tag.id,
alias, alias,
@ -52,5 +54,5 @@ export async function createTag(formData: TagFormInput) {
return tag; return tag;
}); });
return created return created;
} }

View File

@ -3,15 +3,15 @@
import { prisma } from "@/lib/prisma"; import { prisma } from "@/lib/prisma";
import { revalidatePath } from "next/cache"; import { revalidatePath } from "next/cache";
// Deletes a tag if it has no artwork references or child tags.
export async function deleteTag(tagId: string) { export async function deleteTag(tagId: string) {
const tag = await prisma.artTag.findUnique({ const tag = await prisma.tag.findUnique({
where: { id: tagId }, where: { id: tagId },
select: { select: {
id: true, id: true,
_count: { _count: {
select: { select: {
artworks: true, artworks: true,
children: true,
}, },
}, },
}, },
@ -25,16 +25,21 @@ export async function deleteTag(tagId: string) {
throw new Error("Cannot delete tag: it is used by artworks."); throw new Error("Cannot delete tag: it is used by artworks.");
} }
if (tag._count.children > 0) { const parentUsage = await prisma.tagCategory.count({
where: { parentTagId: tagId },
});
if (parentUsage > 0) {
throw new Error("Cannot delete tag: it has child tags."); throw new Error("Cannot delete tag: it has child tags.");
} }
await prisma.$transaction(async (tx) => { await prisma.$transaction(async (tx) => {
await tx.artTagAlias.deleteMany({ where: { tagId } }); await tx.tagAlias.deleteMany({ where: { tagId } });
await tx.artTag.delete({ where: { id: tagId } }); await tx.tagCategory.deleteMany({ where: { tagId } });
await tx.tag.delete({ where: { id: tagId } });
}); });
revalidatePath("/tags"); revalidatePath("/tags");
return { success: true }; return { success: true };
} }

View File

@ -1,7 +1,8 @@
"use server" "use server";
import { prisma } from "@/lib/prisma" import { prisma } from "@/lib/prisma";
// Returns tags ordered by sortIndex.
export async function getTags() { export async function getTags() {
return await prisma.artTag.findMany({ orderBy: { sortIndex: "asc" } }) return prisma.tag.findMany({ orderBy: { sortIndex: "asc" } });
} }

View File

@ -1,22 +1,29 @@
"use server" "use server";
import { prisma } from "@/lib/prisma"; import { prisma } from "@/lib/prisma";
// Returns true if possibleAncestorId is a descendant of tagId (cycle check).
export async function isDescendant(tagId: string, possibleAncestorId: string): Promise<boolean> { export async function isDescendant(tagId: string, possibleAncestorId: string): Promise<boolean> {
// Walk upwards from possibleAncestorId; if we hit tagId, it's a cycle. // Walk upwards across any category hierarchy; if we hit tagId, it's a cycle.
let current: string | null = possibleAncestorId; const visited = new Set<string>();
const queue: string[] = [possibleAncestorId];
while (current) {
while (queue.length > 0) {
const current = queue.shift();
if (!current) continue;
if (current === tagId) return true; if (current === tagId) return true;
if (visited.has(current)) continue;
visited.add(current);
const t: { parentId: string | null } | null = const parents = await prisma.tagCategory.findMany({
await prisma.artTag.findUnique({ where: { tagId: current, parentTagId: { not: null } },
where: { id: current }, select: { parentTagId: true },
select: { parentId: true }, });
});
current = t?.parentId ?? null; for (const p of parents) {
if (p.parentTagId) queue.push(p.parentTagId);
}
} }
return false; return false;
} }

View File

@ -1,18 +1,20 @@
"use server" "use server";
import { prisma } from '@/lib/prisma'; import { prisma } from "@/lib/prisma";
import { TagFormInput, tagSchema } from '@/schemas/artworks/tagSchema'; import { tagSchema } from "@/schemas/artworks/tagSchema";
import { isDescendant } from './isDescendant'; import type { TagFormInput } from "@/schemas/artworks/tagSchema";
import { isDescendant } from "./isDescendant";
// Updates a tag and its category/alias relationships.
export async function updateTag(id: string, rawData: TagFormInput) { export async function updateTag(id: string, rawData: TagFormInput) {
const parsed = tagSchema.safeParse(rawData) const parsed = tagSchema.safeParse(rawData);
if (!parsed.success) { if (!parsed.success) {
console.error("Validation failed", parsed.error) console.error("Validation failed", parsed.error);
throw new Error("Invalid input") throw new Error("Invalid input");
} }
const data = parsed.data const data = parsed.data;
const parentId = data.parentId ?? null; const parentId = data.parentId ?? null;
const tagSlug = data.name.toLowerCase().replace(/\s+/g, "-"); const tagSlug = data.name.toLowerCase().replace(/\s+/g, "-");
@ -27,22 +29,62 @@ export async function updateTag(id: string, rawData: TagFormInput) {
} }
const updated = await prisma.$transaction(async (tx) => { const updated = await prisma.$transaction(async (tx) => {
const tag = await tx.artTag.update({ const tag = await tx.tag.update({
where: { id }, where: { id },
data: { data: {
name: data.name, name: data.name,
slug: tagSlug, slug: tagSlug,
description: data.description, description: data.description,
isParent: data.isParent, isVisible: data.isVisible ?? true,
showOnAnimalPage: data.showOnAnimalPage,
parentId,
categories: data.categoryIds
? { set: data.categoryIds.map((cid) => ({ id: cid })) }
: undefined,
}, },
}); });
const existing = await tx.artTagAlias.findMany({ if (data.categoryIds) {
const existingLinks = await tx.tagCategory.findMany({
where: { tagId: id },
select: { id: true, categoryId: true },
});
const desired = new Set(data.categoryIds);
const existingSet = new Set(existingLinks.map((l) => l.categoryId));
const toCreate = data.categoryIds.filter((cid) => !existingSet.has(cid));
const toDeleteIds = existingLinks
.filter((l) => !desired.has(l.categoryId))
.map((l) => l.id);
if (toDeleteIds.length > 0) {
await tx.tagCategory.deleteMany({
where: { id: { in: toDeleteIds } },
});
}
if (toCreate.length > 0) {
await tx.tagCategory.createMany({
data: toCreate.map((categoryId) => ({
tagId: id,
categoryId,
isParent: data.isParent,
showOnAnimalPage: data.showOnAnimalPage,
parentTagId: parentId,
})),
skipDuplicates: true,
});
}
if (existingLinks.length > 0) {
await tx.tagCategory.updateMany({
where: { tagId: id, categoryId: { in: data.categoryIds } },
data: {
isParent: data.isParent,
showOnAnimalPage: data.showOnAnimalPage,
parentTagId: parentId,
},
});
}
}
const existing = await tx.tagAlias.findMany({
where: { tagId: id }, where: { tagId: id },
select: { id: true, alias: true }, select: { id: true, alias: true },
}); });
@ -56,13 +98,13 @@ export async function updateTag(id: string, rawData: TagFormInput) {
.map((a) => a.id); .map((a) => a.id);
if (toDeleteIds.length > 0) { if (toDeleteIds.length > 0) {
await tx.artTagAlias.deleteMany({ await tx.tagAlias.deleteMany({
where: { id: { in: toDeleteIds } }, where: { id: { in: toDeleteIds } },
}); });
} }
if (toCreate.length > 0) { if (toCreate.length > 0) {
await tx.artTagAlias.createMany({ await tx.tagAlias.createMany({
data: toCreate.map((alias) => ({ tagId: id, alias })), data: toCreate.map((alias) => ({ tagId: id, alias })),
skipDuplicates: true, skipDuplicates: true,
}); });
@ -71,5 +113,5 @@ export async function updateTag(id: string, rawData: TagFormInput) {
return tag; return tag;
}); });
return updated return updated;
} }

View File

@ -1,10 +1,11 @@
'use server'; "use server";
import { prisma } from "@/lib/prisma"; import { prisma } from "@/lib/prisma";
// Returns the most recent Terms of Service markdown.
export async function getLatestTos(): Promise<string | null> { export async function getLatestTos(): Promise<string | null> {
const tos = await prisma.termsOfService.findFirst({ const tos = await prisma.termsOfService.findFirst({
orderBy: { createdAt: 'desc' }, orderBy: { createdAt: "desc" },
}); });
return tos?.markdown ?? null; return tos?.markdown ?? null;
} }

View File

@ -1,11 +1,12 @@
'use server'; "use server";
import { prisma } from "@/lib/prisma"; import { prisma } from "@/lib/prisma";
// Saves a new Terms of Service version.
export async function saveTosAction(markdown: string) { export async function saveTosAction(markdown: string) {
await prisma.termsOfService.create({ await prisma.termsOfService.create({
data: { data: {
markdown, markdown,
}, },
}); });
} }

View File

@ -1,9 +1,9 @@
"use server";
import { createImageFromFile } from "./createImageFromFile"; import { createImageFromFile } from "./createImageFromFile";
import type { BulkResult } from "@/types/uploads";
type BulkResult = // Bulk image upload server action used by the admin UI.
| { ok: true; artworkId: string; name: string }
| { ok: false; name: string; error: string };
export async function createImagesBulk(formData: FormData): Promise<BulkResult[]> { export async function createImagesBulk(formData: FormData): Promise<BulkResult[]> {
const entries = formData.getAll("file"); const entries = formData.getAll("file");
const files = entries.filter((x): x is File => x instanceof File); const files = entries.filter((x): x is File => x instanceof File);

View File

@ -1,203 +1,12 @@
"use server" "use server";
import { fileUploadSchema } from "@/schemas/artworks/imageSchema"; import type { fileUploadSchema } from "@/schemas/artworks/imageSchema";
import "dotenv/config"; import "dotenv/config";
import { z } from "zod/v4"; import type { z } from "zod/v4";
import { createImageFromFile } from "./createImageFromFile"; import { createImageFromFile } from "./createImageFromFile";
// Creates a single artwork image using the shared upload pipeline.
export async function createImage(values: z.infer<typeof fileUploadSchema>) { export async function createImage(values: z.infer<typeof fileUploadSchema>) {
const imageFile = values.file[0]; const imageFile = values.file[0];
return createImageFromFile(imageFile, { colorMode: "inline" }); return createImageFromFile(imageFile, { colorMode: "inline" });
} }
/*
export async function createImage(values: z.infer<typeof fileUploadSchema>) {
const imageFile = values.file[0];
if (!(imageFile instanceof File)) {
console.log("No image or invalid type");
return null;
}
const fileName = imageFile.name;
const fileType = imageFile.type;
const fileSize = imageFile.size;
const lastModified = new Date(imageFile.lastModified);
const fileKey = uuidv4();
const arrayBuffer = await imageFile.arrayBuffer();
const buffer = Buffer.from(arrayBuffer);
const realFileType = fileType.split("/")[1];
const originalKey = `original/${fileKey}.${realFileType}`;
const modifiedKey = `modified/${fileKey}.webp`;
const resizedKey = `resized/${fileKey}.webp`;
const thumbnailKey = `thumbnail/${fileKey}.webp`;
const sharpData = sharp(buffer);
const metadata = await sharpData.metadata();
//--- Original file
await s3.send(
new PutObjectCommand({
Bucket: `${process.env.BUCKET_NAME}`,
Key: originalKey,
Body: buffer,
ContentType: "image/" + metadata.format,
})
);
//--- Modified file
const modifiedBuffer = await sharp(buffer)
.toFormat('webp')
.toBuffer()
const modifiedMetadata = await sharp(modifiedBuffer).metadata();
await s3.send(
new PutObjectCommand({
Bucket: `${process.env.BUCKET_NAME}`,
Key: modifiedKey,
Body: modifiedBuffer,
ContentType: "image/" + modifiedMetadata.format,
})
);
//--- Resized file
const { width, height } = modifiedMetadata;
const targetSize = 400;
let resizeOptions;
if (width && height) {
if (height < width) {
resizeOptions = { height: targetSize };
} else {
resizeOptions = { width: targetSize };
}
} else {
resizeOptions = { height: targetSize };
}
const resizedBuffer = await sharp(modifiedBuffer)
.resize({ ...resizeOptions, withoutEnlargement: true })
.toFormat('webp')
.toBuffer();
const resizedMetadata = await sharp(resizedBuffer).metadata();
await s3.send(
new PutObjectCommand({
Bucket: `${process.env.BUCKET_NAME}`,
Key: resizedKey,
Body: resizedBuffer,
ContentType: "image/" + resizedMetadata.format,
})
);
//--- Thumbnail file
const thumbnailTargetSize = 160;
let thumbnailOptions;
if (width && height) {
if (height < width) {
thumbnailOptions = { height: thumbnailTargetSize };
} else {
thumbnailOptions = { width: thumbnailTargetSize };
}
} else {
thumbnailOptions = { height: thumbnailTargetSize };
}
const thumbnailBuffer = await sharp(modifiedBuffer)
.resize({ ...thumbnailOptions, withoutEnlargement: true })
.toFormat('webp')
.toBuffer();
const thumbnailMetadata = await sharp(thumbnailBuffer).metadata();
await s3.send(
new PutObjectCommand({
Bucket: `${process.env.BUCKET_NAME}`,
Key: thumbnailKey,
Body: thumbnailBuffer,
ContentType: "image/" + thumbnailMetadata.format,
})
);
const fileRecord = await prisma.fileData.create({
data: {
name: fileName,
fileKey,
originalFile: fileName,
uploadDate: lastModified,
fileType: realFileType,
fileSize: fileSize,
},
});
const artworkSlug = fileName.toLowerCase().replace(/\s+/g, "-");
const artworkRecord = await prisma.artwork.create({
data: {
name: fileName,
slug: artworkSlug,
creationDate: lastModified,
fileId: fileRecord.id,
},
});
await prisma.artworkMetadata.create({
data: {
artworkId: artworkRecord.id,
format: metadata.format || "unknown",
width: metadata.width || 0,
height: metadata.height || 0,
space: metadata.space || "unknown",
channels: metadata.channels || 0,
depth: metadata.depth || "unknown",
density: metadata.density ?? undefined,
bitsPerSample: metadata.bitsPerSample ?? undefined,
isProgressive: metadata.isProgressive ?? undefined,
isPalette: metadata.isPalette ?? undefined,
hasProfile: metadata.hasProfile ?? undefined,
hasAlpha: metadata.hasAlpha ?? undefined,
autoOrientW: metadata.autoOrient?.width ?? undefined,
autoOrientH: metadata.autoOrient?.height ?? undefined,
},
});
await prisma.fileVariant.createMany({
data: [
{
s3Key: originalKey,
type: "original",
height: metadata.height,
width: metadata.width,
fileExtension: metadata.format,
mimeType: "image/" + metadata.format,
sizeBytes: metadata.size,
artworkId: artworkRecord.id
},
{
s3Key: modifiedKey,
type: "modified",
height: modifiedMetadata.height,
width: modifiedMetadata.width,
fileExtension: modifiedMetadata.format,
mimeType: "image/" + modifiedMetadata.format,
sizeBytes: modifiedMetadata.size,
artworkId: artworkRecord.id
},
{
s3Key: resizedKey,
type: "resized",
height: resizedMetadata.height,
width: resizedMetadata.width,
fileExtension: resizedMetadata.format,
mimeType: "image/" + resizedMetadata.format,
sizeBytes: resizedMetadata.size,
artworkId: artworkRecord.id
},
{
s3Key: thumbnailKey,
type: "thumbnail",
height: thumbnailMetadata.height,
width: thumbnailMetadata.width,
fileExtension: thumbnailMetadata.format,
mimeType: "image/" + thumbnailMetadata.format,
sizeBytes: thumbnailMetadata.size,
artworkId: artworkRecord.id
}
],
});
return artworkRecord
}
*/

View File

@ -8,12 +8,12 @@ import sharp from "sharp";
import { v4 as uuidv4 } from "uuid"; import { v4 as uuidv4 } from "uuid";
import { generateArtworkColorsForArtwork } from "../artworks/generateArtworkColors"; import { generateArtworkColorsForArtwork } from "../artworks/generateArtworkColors";
// Upload pipeline that generates variants and metadata, then creates artwork records.
export async function createImageFromFile( export async function createImageFromFile(
imageFile: File, imageFile: File,
opts?: { originalName?: string; colorMode?: "inline" | "defer" | "off" }, opts?: { originalName?: string; colorMode?: "inline" | "defer" | "off" },
) { ) {
if (!(imageFile instanceof File)) { if (!(imageFile instanceof File)) {
console.log("No image or invalid type");
return null; return null;
} }
@ -151,7 +151,7 @@ export async function createImageFromFile(
name: fileName, name: fileName,
fileKey, fileKey,
originalFile: fileName, originalFile: fileName,
uploadDate: lastModified, uploadDate: new Date(),
fileType: realFileType, fileType: realFileType,
fileSize, fileSize,
}, },

View File

@ -2,24 +2,20 @@
import { auth } from "@/lib/auth"; import { auth } from "@/lib/auth";
import { headers } from "next/headers"; import { headers } from "next/headers";
import { z } from "zod/v4"; import type { SessionWithRole } from "@/types/auth";
import { createUserSchema } from "@/schemas/users";
import type { CreateUserInput } from "@/schemas/users";
const schema = z.object({ // Creates a new user account (admin-only).
name: z.string().min(1).max(200), export async function createUser(input: CreateUserInput) {
email: z.string().email().max(320),
password: z.string().min(8).max(128),
role: z.enum(["user", "admin"]).default("user"),
});
export async function createUser(input: z.infer<typeof schema>) {
const session = await auth.api.getSession({ headers: await headers() }); const session = await auth.api.getSession({ headers: await headers() });
const role = (session as any)?.user?.role; const role = (session as SessionWithRole)?.user?.role;
if (!session || role !== "admin") { if (!session || role !== "admin") {
throw new Error("Forbidden"); throw new Error("Forbidden");
} }
const data = schema.parse(input); const data = createUserSchema.parse(input);
return auth.api.createUser({ return auth.api.createUser({
body: { body: {

View File

@ -4,13 +4,15 @@ import { auth } from "@/lib/auth";
import { prisma } from "@/lib/prisma"; import { prisma } from "@/lib/prisma";
import { headers } from "next/headers"; import { headers } from "next/headers";
import { z } from "zod/v4"; import { z } from "zod/v4";
import type { SessionWithRole } from "@/types/auth";
// Deletes a user account with safety checks (admin-only, cannot delete self or last admin).
export async function deleteUser(id: string) { export async function deleteUser(id: string) {
const userId = z.string().min(1).parse(id); const userId = z.string().min(1).parse(id);
const session = await auth.api.getSession({ headers: await headers() }); const session = await auth.api.getSession({ headers: await headers() });
const role = (session as any)?.user?.role as string | undefined; const role = (session as SessionWithRole)?.user?.role;
const currentUserId = (session as any)?.user?.id as string | undefined; const currentUserId = (session as SessionWithRole)?.user?.id;
if (!session || role !== "admin") throw new Error("Forbidden"); if (!session || role !== "admin") throw new Error("Forbidden");
if (!currentUserId) throw new Error("Session missing user id"); if (!currentUserId) throw new Error("Session missing user id");
@ -40,5 +42,5 @@ async function await_attachTarget(userId: string) {
select: { id: true, role: true }, select: { id: true, role: true },
}); });
if (!target) throw new Error("User not found."); if (!target) throw new Error("User not found.");
return target as { id: string; role: "admin" | "user" }; return target;
} }

View File

@ -2,21 +2,14 @@
import { auth } from "@/lib/auth"; import { auth } from "@/lib/auth";
import { prisma } from "@/lib/prisma"; import { prisma } from "@/lib/prisma";
import type { SessionWithRole } from "@/types/auth";
import type { UserRole, UsersListRow } from "@/types/users";
import { headers } from "next/headers"; import { headers } from "next/headers";
export type UsersListRow = { // Returns all users for the admin users table.
id: string;
name: string | null;
email: string;
role: "admin" | "user";
emailVerified: boolean;
createdAt: Date;
updatedAt: Date;
};
export async function getUsers(): Promise<UsersListRow[]> { export async function getUsers(): Promise<UsersListRow[]> {
const session = await auth.api.getSession({ headers: await headers() }); const session = await auth.api.getSession({ headers: await headers() });
const role = (session as any)?.user?.role as string | undefined; const role = (session as SessionWithRole)?.user?.role;
if (!session || role !== "admin") { if (!session || role !== "admin") {
throw new Error("Forbidden"); throw new Error("Forbidden");
@ -35,5 +28,16 @@ export async function getUsers(): Promise<UsersListRow[]> {
}, },
}); });
return rows as UsersListRow[]; return rows.map((r) => {
if (r.role !== "admin" && r.role !== "user") {
throw new Error(`Unexpected user role: ${r.role}`);
}
return {
...r,
role: r.role as UserRole,
createdAt: r.createdAt.toISOString(),
updatedAt: r.updatedAt.toISOString(),
};
});
} }

View File

@ -2,21 +2,20 @@
import { auth } from "@/lib/auth"; import { auth } from "@/lib/auth";
import { headers } from "next/headers"; import { headers } from "next/headers";
import { z } from "zod/v4"; import type { SessionWithRole } from "@/types/auth";
import { resendVerificationSchema } from "@/schemas/users";
import type { ResendVerificationInput } from "@/schemas/users";
const schema = z.object({ // Resends a verification email for a user (admin-only).
email: z.string().email(), export async function resendVerification(input: ResendVerificationInput) {
});
export async function resendVerification(input: z.infer<typeof schema>) {
const session = await auth.api.getSession({ headers: await headers() }); const session = await auth.api.getSession({ headers: await headers() });
const role = (session as any)?.user?.role as string | undefined; const role = (session as SessionWithRole)?.user?.role;
if (!session || role !== "admin") throw new Error("Forbidden"); if (!session || role !== "admin") throw new Error("Forbidden");
const { email } = schema.parse(input); const { email } = resendVerificationSchema.parse(input);
// Uses the public auth route (same origin) // Uses the public auth route (same origin)
const res = await fetch("http://localhost/api/auth/send-verification-email", { const res = await fetch(`${process.env.BETTER_AUTH_URL}/api/auth/send-verification-email`, {
// NOTE: In production, you should use an absolute URL from env, or use authClient. // NOTE: In production, you should use an absolute URL from env, or use authClient.
// This is kept minimal; if you want, I'll refactor to authClient to avoid hostname concerns. // This is kept minimal; if you want, I'll refactor to authClient to avoid hostname concerns.
method: "POST", method: "POST",

View File

@ -8,6 +8,7 @@ import ArtworkVariants from "@/components/artworks/single/ArtworkVariants";
import DeleteArtworkButton from "@/components/artworks/single/DeleteArtworkButton"; import DeleteArtworkButton from "@/components/artworks/single/DeleteArtworkButton";
import EditArtworkForm from "@/components/artworks/single/EditArtworkForm"; import EditArtworkForm from "@/components/artworks/single/EditArtworkForm";
// Single artwork edit page.
export default async function ArtworkSinglePage({ params }: { params: Promise<{ id: string }> }) { export default async function ArtworkSinglePage({ params }: { params: Promise<{ id: string }> }) {
const { id } = await params; const { id } = await params;
@ -16,30 +17,30 @@ export default async function ArtworkSinglePage({ params }: { params: Promise<{
const categories = await getCategoriesWithTags(); const categories = await getCategoriesWithTags();
const tags = await getTags(); const tags = await getTags();
if (!item) return <div>Artwork with this id not found</div> if (!item) return <div>Artwork with this id not found</div>;
return ( return (
<div> <div>
<h1 className="text-2xl font-bold mb-4">Edit artwork</h1> <h1 className="text-2xl font-bold mb-4">Edit artwork</h1>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8"> <div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
<div> <div className="space-y-6">
{item ? <EditArtworkForm artwork={item} tags={tags} categories={categories} /> : 'Artwork not found...'} <EditArtworkForm artwork={item} tags={tags} categories={categories} />
<div className="mt-6"> <div>
{item && <DeleteArtworkButton artworkId={item.id} />} <DeleteArtworkButton artworkId={item.id} />
</div> </div>
<div> <div>
{item && <ArtworkVariants artworkId={item.id} variants={item.variants} />} <ArtworkTimelapse artworkId={item.id} timelapse={item.timelapse} />
</div> </div>
</div> </div>
<div className="space-y-6"> <div className="space-y-6">
<div> <div>
{item && <ArtworkTimelapse artworkId={item.id} timelapse={item.timelapse} />} <ArtworkColors colors={item.colors} artworkId={item.id} />
</div> </div>
<div> <div>
{item && <ArtworkColors colors={item.colors} artworkId={item.id} />} <ArtworkDetails artwork={item} />
</div> </div>
<div> <div>
{item && <ArtworkDetails artwork={item} />} <ArtworkVariants artworkId={item.id} variants={item.variants} />
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,94 +1,13 @@
import { ArtworkColorProcessor } from "@/components/artworks/ArtworkColorProcessor"; import { ArtworkColorProcessor } from "@/components/artworks/ArtworkColorProcessor";
import { ArtworkGalleryVariantProcessor } from "@/components/artworks/ArtworkGalleryVariantProcessor";
import { ArtworksTable } from "@/components/artworks/ArtworksTable"; import { ArtworksTable } from "@/components/artworks/ArtworksTable";
import { getArtworksPage } from "@/lib/queryArtworks";
export default async function ArtworksPage({
searchParams
}: {
searchParams?: {
// type?: string;
published?: string;
// groupBy?: string;
// year?: string;
// album?: string;
cursor?: string;
}
}) {
const {
// type = "all",
published = "all",
// groupBy = "year",
// year,
// album,
cursor = undefined
} = await searchParams ?? {};
// const groupMode = groupBy === "album" ? "album" : "year";
// const groupId = groupMode === "album" ? album ?? "all" : year ?? "all";
// Filter by type
// if (type !== "all") {
// where.typeId = type === "none" ? null : type;
// }
// Filter by published status
// if (published === "published") {
// where.published = true;
// } else if (published === "unpublished") {
// where.published = false;
// } else if (published === "needsWork") {
// where.needsWork = true;
// }
// Filter by group (year or album)
// if (groupMode === "year" && groupId !== "all") {
// where.year = parseInt(groupId);
// } else if (groupMode === "album" && groupId !== "all") {
// where.albumId = groupId;
// }
const { items, nextCursor } = await getArtworksPage(
{
published,
cursor,
take: 48
}
)
// Admin artworks list page.
export default async function ArtworksPage() {
return ( return (
<div className="space-y-6"> <div className="space-y-6">
<h1 className="text-2xl font-bold">Artworks</h1> <h1 className="text-2xl font-bold">Artworks</h1>
{/* <ProcessArtworkColorsButton /> */}
<ArtworkColorProcessor /> <ArtworkColorProcessor />
<ArtworkGalleryVariantProcessor />
<ArtworksTable /> <ArtworksTable />
</div> </div>
// <div>
// <div className="flex justify-between pb-4 items-end">
// <h1 className="text-2xl font-bold mb-4">Artworks</h1>
// <Link href="/uploads/single" className="flex gap-2 items-center cursor-pointer bg-primary hover:bg-primary/90 text-primary-foreground px-4 py-2 rounded">
// <PlusCircleIcon className="h-[1.2rem] w-[1.2rem] scale-100 rotate-0 transition-all text-primary-foreground" /> Upload new artwork
// </Link>
// <Link href="/uploads/bulk" className="flex gap-2 items-center cursor-pointer bg-primary hover:bg-primary/90 text-primary-foreground px-4 py-2 rounded">
// <PlusCircleIcon className="h-[1.2rem] w-[1.2rem] scale-100 rotate-0 transition-all text-primary-foreground" /> Upload many artwork
// </Link>
// </div>
// <FilterBar
// // types={types}
// // albums={albums}
// // years={years}
// // currentType={type}
// currentPublished={published}
// // groupBy={groupMode}
// // groupId={groupId}
// />
// <div className="mt-6">
// {items.length > 0 ? (
// <ArtworkGallery initialArtworks={items} initialCursor={nextCursor} />
// ) : (
// <p className="text-muted-foreground italic">No artworks found.</p>
// )}
// </div>
// </div >
); );
} }

View File

@ -1,13 +1,14 @@
import EditCategoryForm from "@/components/categories/EditCategoryForm"; import EditCategoryForm from "@/components/categories/EditCategoryForm";
import { prisma } from "@/lib/prisma"; import { prisma } from "@/lib/prisma";
// Edit category page.
export default async function PortfolioCategoriesEditPage({ params }: { params: { id: string } }) { export default async function PortfolioCategoriesEditPage({ params }: { params: { id: string } }) {
const { id } = await params; const { id } = await params;
const category = await prisma.artCategory.findUnique({ const category = await prisma.artCategory.findUnique({
where: { where: {
id, id,
} },
}) });
return ( return (
<div> <div>
@ -15,4 +16,4 @@ export default async function PortfolioCategoriesEditPage({ params }: { params:
{category && <EditCategoryForm category={category} />} {category && <EditCategoryForm category={category} />}
</div> </div>
); );
} }

View File

@ -1,5 +1,6 @@
import NewCategoryForm from "@/components/categories/NewCategoryForm"; import NewCategoryForm from "@/components/categories/NewCategoryForm";
// Create a new category page.
export default function PortfolioCategoriesNewPage() { export default function PortfolioCategoriesNewPage() {
return ( return (
<div> <div>
@ -7,4 +8,4 @@ export default function PortfolioCategoriesNewPage() {
<NewCategoryForm /> <NewCategoryForm />
</div> </div>
); );
} }

View File

@ -4,6 +4,7 @@ import { PlusCircleIcon } from "lucide-react";
import Link from "next/link"; import Link from "next/link";
import { Suspense } from "react"; import { Suspense } from "react";
// Admin categories management page.
export default async function CategoriesPage() { export default async function CategoriesPage() {
const items = await getCategoriesWithCount(); const items = await getCategoriesWithCount();
@ -11,7 +12,10 @@ export default async function CategoriesPage() {
<div> <div>
<div className="flex gap-4 justify-between pb-8"> <div className="flex gap-4 justify-between pb-8">
<h1 className="text-2xl font-bold mb-4">Art Categories</h1> <h1 className="text-2xl font-bold mb-4">Art Categories</h1>
<Link href="/categories/new" className="flex gap-2 items-center cursor-pointer bg-primary hover:bg-primary/90 text-primary-foreground px-4 py-2 rounded"> <Link
href="/categories/new"
className="flex gap-2 items-center cursor-pointer bg-primary hover:bg-primary/90 text-primary-foreground px-4 py-2 rounded"
>
<PlusCircleIcon className="h-[1.2rem] w-[1.2rem] scale-100 rotate-0 transition-all text-primary-foreground" /> Add new category <PlusCircleIcon className="h-[1.2rem] w-[1.2rem] scale-100 rotate-0 transition-all text-primary-foreground" /> Add new category
</Link> </Link>
</div> </div>
@ -24,4 +28,4 @@ export default async function CategoriesPage() {
</Suspense> </Suspense>
</div> </div>
); );
} }

View File

@ -1,8 +1,8 @@
import { listCommissionCustomCardImages } from "@/actions/commissions/customCards/images";
import EditCustomCardForm from "@/components/commissions/customCards/EditCustomCardForm"; import EditCustomCardForm from "@/components/commissions/customCards/EditCustomCardForm";
import { prisma } from "@/lib/prisma"; import { prisma } from "@/lib/prisma";
import { notFound } from "next/navigation"; import { notFound } from "next/navigation";
// Edit custom commission card page.
export default async function CommissionCustomCardEditPage({ export default async function CommissionCustomCardEditPage({
params, params,
}: { }: {
@ -10,17 +10,18 @@ export default async function CommissionCustomCardEditPage({
}) { }) {
const { id } = await params; const { id } = await params;
const [card, options, extras, images] = await Promise.all([ const [card, options, extras, tags] = await Promise.all([
prisma.commissionCustomCard.findUnique({ prisma.commissionCustomCard.findUnique({
where: { id }, where: { id },
include: { include: {
options: { orderBy: { sortIndex: "asc" } }, options: { orderBy: { sortIndex: "asc" } },
extras: { orderBy: { sortIndex: "asc" } }, extras: { orderBy: { sortIndex: "asc" } },
tags: true,
}, },
}), }),
prisma.commissionOption.findMany({ orderBy: [{ sortIndex: "asc" }, { name: "asc" }] }), prisma.commissionOption.findMany({ orderBy: [{ sortIndex: "asc" }, { name: "asc" }] }),
prisma.commissionExtra.findMany({ orderBy: [{ sortIndex: "asc" }, { name: "asc" }] }), prisma.commissionExtra.findMany({ orderBy: [{ sortIndex: "asc" }, { name: "asc" }] }),
listCommissionCustomCardImages(), prisma.tag.findMany({ orderBy: [{ sortIndex: "asc" }, { name: "asc" }] }),
]); ]);
if (!card) { if (!card) {
@ -36,7 +37,7 @@ export default async function CommissionCustomCardEditPage({
card={card} card={card}
allOptions={options} allOptions={options}
allExtras={extras} allExtras={extras}
images={images} allTags={tags}
/> />
</div> </div>
); );

View File

@ -1,12 +1,12 @@
import { listCommissionCustomCardImages } from "@/actions/commissions/customCards/images";
import NewCustomCardForm from "@/components/commissions/customCards/NewCustomCardForm"; import NewCustomCardForm from "@/components/commissions/customCards/NewCustomCardForm";
import { prisma } from "@/lib/prisma"; import { prisma } from "@/lib/prisma";
// New custom commission card page.
export default async function CommissionCustomCardsNewPage() { export default async function CommissionCustomCardsNewPage() {
const [options, extras, images] = await Promise.all([ const [options, extras, tags] = await Promise.all([
prisma.commissionOption.findMany({ orderBy: [{ sortIndex: "asc" }, { name: "asc" }] }), prisma.commissionOption.findMany({ orderBy: [{ sortIndex: "asc" }, { name: "asc" }] }),
prisma.commissionExtra.findMany({ orderBy: [{ sortIndex: "asc" }, { name: "asc" }] }), prisma.commissionExtra.findMany({ orderBy: [{ sortIndex: "asc" }, { name: "asc" }] }),
listCommissionCustomCardImages(), prisma.tag.findMany({ orderBy: [{ sortIndex: "asc" }, { name: "asc" }] }),
]); ]);
return ( return (
@ -14,7 +14,7 @@ export default async function CommissionCustomCardsNewPage() {
<div className="flex gap-4 justify-between pb-8"> <div className="flex gap-4 justify-between pb-8">
<h1 className="text-2xl font-bold mb-4">New Custom Commission Card</h1> <h1 className="text-2xl font-bold mb-4">New Custom Commission Card</h1>
</div> </div>
<NewCustomCardForm options={options} extras={extras} images={images} /> <NewCustomCardForm options={options} extras={extras} tags={tags} />
</div> </div>
); );
} }

View File

@ -3,6 +3,7 @@ import { prisma } from "@/lib/prisma";
import { PlusCircleIcon } from "lucide-react"; import { PlusCircleIcon } from "lucide-react";
import Link from "next/link"; import Link from "next/link";
// Custom commission cards list page.
export default async function CommissionCustomCardsPage() { export default async function CommissionCustomCardsPage() {
const cards = await prisma.commissionCustomCard.findMany({ const cards = await prisma.commissionCustomCard.findMany({
include: { include: {

View File

@ -2,6 +2,7 @@ import { listCommissionExamples } from "@/actions/commissions/examples";
import { getActiveGuidelines } from "@/actions/commissions/guidelines/getGuidelines"; import { getActiveGuidelines } from "@/actions/commissions/guidelines/getGuidelines";
import GuidelinesEditor from "@/components/commissions/guidelines/Editor"; import GuidelinesEditor from "@/components/commissions/guidelines/Editor";
// Admin page for editing commission guidelines.
export default async function CommissionGuidelinesPage() { export default async function CommissionGuidelinesPage() {
const [{ markdown, exampleImageUrl }, examples] = await Promise.all([ const [{ markdown, exampleImageUrl }, examples] = await Promise.all([
getActiveGuidelines(), getActiveGuidelines(),

View File

@ -4,6 +4,7 @@ import { prisma } from "@/lib/prisma";
import type { BoardItem, ColumnsState } from "@/types/Board"; import type { BoardItem, ColumnsState } from "@/types/Board";
// Admin kanban page for commission requests.
export default async function CommissionsBoardPage() { export default async function CommissionsBoardPage() {
const requests = await prisma.commissionRequest.findMany({ const requests = await prisma.commissionRequest.findMany({
where: { where: {

View File

@ -2,6 +2,7 @@ import { getCommissionRequestById } from "@/actions/commissions/requests/getComm
import { CommissionRequestEditor } from "@/components/commissions/requests/CommissionRequestEditor"; import { CommissionRequestEditor } from "@/components/commissions/requests/CommissionRequestEditor";
import { notFound } from "next/navigation"; import { notFound } from "next/navigation";
// Admin page for editing a single commission request.
export default async function CommissionRequestPage({ export default async function CommissionRequestPage({
params, params,
}: { }: {
@ -20,7 +21,7 @@ export default async function CommissionRequestPage({
Submitted: {new Date(request.createdAt).toLocaleString()} · ID: {request.id} Submitted: {new Date(request.createdAt).toLocaleString()} · ID: {request.id}
</p> </p>
</div> </div>
<CommissionRequestEditor request={request as any} /> <CommissionRequestEditor request={request} />
</div> </div>
</div> </div>
); );

View File

@ -1,6 +1,7 @@
import RequestsTable from "@/components/commissions/requests/RequestsTable"; import RequestsTable from "@/components/commissions/requests/RequestsTable";
import { prisma } from "@/lib/prisma"; import { prisma } from "@/lib/prisma";
// Server-rendered commissions list page.
export default async function CommissionPage() { export default async function CommissionPage() {
const items = await prisma.commissionRequest.findMany({ const items = await prisma.commissionRequest.findMany({
include: { include: {
@ -15,11 +16,11 @@ export default async function CommissionPage() {
<div> <div>
<h1 className="text-2xl font-semibold">Commission Requests</h1> <h1 className="text-2xl font-semibold">Commission Requests</h1>
<p className="text-sm text-muted-foreground"> <p className="text-sm text-muted-foreground">
List of all incomming requests via website. List of all incoming requests via website.
</p> </p>
</div> </div>
<RequestsTable requests={items} /> <RequestsTable requests={items} />
</div> </div>
</div> </div>
); );
} }

View File

@ -1,6 +1,7 @@
import EditTypeForm from "@/components/commissions/types/EditTypeForm"; import EditTypeForm from "@/components/commissions/types/EditTypeForm";
import { prisma } from "@/lib/prisma"; import { prisma } from "@/lib/prisma";
// Edit commission type page.
export default async function CommissionTypesEditPage({ params }: { params: { id: string } }) { export default async function CommissionTypesEditPage({ params }: { params: { id: string } }) {
const { id } = await params; const { id } = await params;
const commissionType = await prisma.commissionType.findUnique({ const commissionType = await prisma.commissionType.findUnique({
@ -11,20 +12,21 @@ export default async function CommissionTypesEditPage({ params }: { params: { id
options: { include: { option: true }, orderBy: { sortIndex: "asc" } }, options: { include: { option: true }, orderBy: { sortIndex: "asc" } },
extras: { include: { extra: true }, orderBy: { sortIndex: "asc" } }, extras: { include: { extra: true }, orderBy: { sortIndex: "asc" } },
customInputs: { include: { customInput: true }, orderBy: { sortIndex: "asc" } }, customInputs: { include: { customInput: true }, orderBy: { sortIndex: "asc" } },
tags: true,
}, },
}) });
const tags = await prisma.tag.findMany({
orderBy: [{ sortIndex: "asc" }, { name: "asc" }],
});
const options = await prisma.commissionOption.findMany({ const options = await prisma.commissionOption.findMany({
orderBy: [{ sortIndex: "asc" }, { name: "asc" }], orderBy: [{ sortIndex: "asc" }, { name: "asc" }],
}); });
const extras = await prisma.commissionExtra.findMany({ const extras = await prisma.commissionExtra.findMany({
orderBy: [{ sortIndex: "asc" }, { name: "asc" }], orderBy: [{ sortIndex: "asc" }, { name: "asc" }],
}) });
// const customInputs = await prisma.commissionCustomInput.findMany({
// orderBy: [{ sortIndex: "asc" }, { name: "asc" }],
// })
if (!commissionType) { if (!commissionType) {
return <div>Type not found</div> return <div>Type not found</div>;
} }
return ( return (
@ -32,7 +34,12 @@ export default async function CommissionTypesEditPage({ params }: { params: { id
<div className="flex gap-4 justify-between pb-8"> <div className="flex gap-4 justify-between pb-8">
<h1 className="text-2xl font-bold mb-4">Edit Commission Type</h1> <h1 className="text-2xl font-bold mb-4">Edit Commission Type</h1>
</div> </div>
<EditTypeForm type={commissionType} allOptions={options} allExtras={extras} /> <EditTypeForm
type={commissionType}
allOptions={options}
allExtras={extras}
allTags={tags}
/>
</div> </div>
); );
} }

View File

@ -1,10 +1,11 @@
import { ExtraListClient } from "@/components/commissions/extras/ExtraListClient"; import { ExtraListClient } from "@/components/commissions/extras/ExtraListClient";
import { prisma } from "@/lib/prisma"; import { prisma } from "@/lib/prisma";
// Admin page for managing commission extras.
export default async function CommissionTypesExtrasPage() { export default async function CommissionTypesExtrasPage() {
const extras = await prisma.commissionExtra.findMany({ const extras = await prisma.commissionExtra.findMany({
orderBy: [{ createdAt: "asc" }, { name: "asc" }], orderBy: [{ createdAt: "asc" }, { name: "asc" }],
}); });
return <ExtraListClient extras={extras} />; return <ExtraListClient extras={extras} />;
} }

View File

@ -1,24 +1,32 @@
import NewTypeForm from "@/components/commissions/types/NewTypeForm"; import NewTypeForm from "@/components/commissions/types/NewTypeForm";
import { prisma } from "@/lib/prisma"; import { prisma } from "@/lib/prisma";
// Create new commission type page.
export default async function CommissionTypesNewPage() { export default async function CommissionTypesNewPage() {
const tags = await prisma.tag.findMany({
orderBy: [{ sortIndex: "asc" }, { name: "asc" }],
});
const options = await prisma.commissionOption.findMany({ const options = await prisma.commissionOption.findMany({
orderBy: [{ sortIndex: "asc" }, { name: "asc" }], orderBy: [{ sortIndex: "asc" }, { name: "asc" }],
}); });
const extras = await prisma.commissionExtra.findMany({ const extras = await prisma.commissionExtra.findMany({
orderBy: [{ sortIndex: "asc" }, { name: "asc" }], orderBy: [{ sortIndex: "asc" }, { name: "asc" }],
}) });
const customInputs = await prisma.commissionCustomInput.findMany({ const customInputs = await prisma.commissionCustomInput.findMany({
orderBy: [{ sortIndex: "asc" }, { name: "asc" }], orderBy: [{ sortIndex: "asc" }, { name: "asc" }],
}) });
return ( return (
<div> <div>
<div className="flex gap-4 justify-between pb-8"> <div className="flex gap-4 justify-between pb-8">
<h1 className="text-2xl font-bold mb-4">New Commission Type</h1> <h1 className="text-2xl font-bold mb-4">New Commission Type</h1>
</div> </div>
<NewTypeForm options={options} extras={extras} customInputs={customInputs} /> <NewTypeForm
options={options}
extras={extras}
customInputs={customInputs}
tags={tags}
/>
</div> </div>
); );
} }

View File

@ -1,10 +1,11 @@
import { OptionsListClient } from "@/components/commissions/options/OptionsListClient"; import { OptionsListClient } from "@/components/commissions/options/OptionsListClient";
import { prisma } from "@/lib/prisma"; import { prisma } from "@/lib/prisma";
// Admin page for managing commission options.
export default async function CommissionTypesOptionsPage() { export default async function CommissionTypesOptionsPage() {
const options = await prisma.commissionOption.findMany({ const options = await prisma.commissionOption.findMany({
orderBy: [{ createdAt: "asc" }, { name: "asc" }], orderBy: [{ createdAt: "asc" }, { name: "asc" }],
}); });
return <OptionsListClient options={options} />; return <OptionsListClient options={options} />;
} }

View File

@ -3,6 +3,7 @@ import { prisma } from "@/lib/prisma";
import { PlusCircleIcon } from "lucide-react"; import { PlusCircleIcon } from "lucide-react";
import Link from "next/link"; import Link from "next/link";
// Commission types list page.
export default async function CommissionTypesPage() { export default async function CommissionTypesPage() {
const types = await prisma.commissionType.findMany({ const types = await prisma.commissionType.findMany({
include: { include: {
@ -17,11 +18,18 @@ export default async function CommissionTypesPage() {
<div> <div>
<div className="flex gap-4 justify-between pb-8"> <div className="flex gap-4 justify-between pb-8">
<h1 className="text-2xl font-bold mb-4">Commission Types</h1> <h1 className="text-2xl font-bold mb-4">Commission Types</h1>
<Link href="/commissions/types/new" className="flex gap-2 items-center cursor-pointer bg-primary hover:bg-primary/90 text-primary-foreground px-4 py-2 rounded"> <Link
href="/commissions/types/new"
className="flex gap-2 items-center cursor-pointer bg-primary hover:bg-primary/90 text-primary-foreground px-4 py-2 rounded"
>
<PlusCircleIcon className="h-[1.2rem] w-[1.2rem] scale-100 rotate-0 transition-all text-primary-foreground" /> Add new Type <PlusCircleIcon className="h-[1.2rem] w-[1.2rem] scale-100 rotate-0 transition-all text-primary-foreground" /> Add new Type
</Link> </Link>
</div> </div>
{types && types.length > 0 ? <ListTypes types={types} /> : <p className="text-muted-foreground italic">No types found.</p>} {types && types.length > 0 ? (
<ListTypes types={types} />
) : (
<p className="text-muted-foreground italic">No types found.</p>
)}
</div> </div>
); );
} }

View File

@ -3,25 +3,15 @@ import Footer from "@/components/global/Footer";
import MobileSidebar from "@/components/global/MobileSidebar"; import MobileSidebar from "@/components/global/MobileSidebar";
import ModeToggle from "@/components/global/ModeToggle"; import ModeToggle from "@/components/global/ModeToggle";
import Sidebar from "@/components/global/Sidebar"; import Sidebar from "@/components/global/Sidebar";
import type { ReactNode } from "react";
// Main admin layout with sidebar, header actions, and footer.
export default function AdminLayout({ export default function AdminLayout({
children, children,
}: Readonly<{ }: Readonly<{
children: React.ReactNode; children: ReactNode;
}>) { }>) {
return ( return (
// <div className="flex flex-col min-h-screen min-w-screen">
// <header className="sticky top-0 z-50 h-14 w-full border-b bg-background/95 backdrop-blur supports-backdrop-filter:bg-background/60 px-4 py-2">
// <Header />
// </header>
// <main className="container mx-auto px-4 py-8">
// {children}
// </main>
// <footer className="mt-auto px-4 py-2 h-14 border-t bg-background/95 backdrop-blur supports-backdrop-filter:bg-background/60">
// <Footer />
// </footer>
// <Toaster />
// </div>
<div className="min-h-screen w-full"> <div className="min-h-screen w-full">
<div className="flex min-h-screen w-full"> <div className="flex min-h-screen w-full">
<aside className="hidden md:flex md:w-64 md:flex-col md:border-r md:bg-background"> <aside className="hidden md:flex md:w-64 md:flex-col md:border-r md:bg-background">

View File

@ -6,14 +6,7 @@ import { StatusPill } from "@/components/home/StatusPill";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
function fmtDate(d: Date) { // Admin dashboard summary page.
return new Intl.DateTimeFormat("de-DE", {
year: "numeric",
month: "2-digit",
day: "2-digit",
}).format(d);
}
export default async function HomePage() { export default async function HomePage() {
const data = await getAdminDashboard(); const data = await getAdminDashboard();
@ -28,9 +21,6 @@ export default async function HomePage() {
</div> </div>
<div className="flex flex-wrap gap-2"> <div className="flex flex-wrap gap-2">
{/* <Button asChild variant="secondary">
<Link href="/artworks/new">Add artwork</Link>
</Button> */}
<Button asChild> <Button asChild>
<Link href="/commissions/requests">Review requests</Link> <Link href="/commissions/requests">Review requests</Link>
</Button> </Button>
@ -80,48 +70,6 @@ export default async function HomePage() {
</section> </section>
<section className="grid gap-4 lg:grid-cols-3"> <section className="grid gap-4 lg:grid-cols-3">
{/* Artwork status */}
{/* <Card>
<CardHeader>
<CardTitle className="text-base">Artwork status</CardTitle>
</CardHeader>
<CardContent className="space-y-2">
<StatusPill label="Published" value={data.artworks.published} />
<StatusPill label="Unpublished" value={data.artworks.unpublished} />
<StatusPill label="NSFW" value={data.artworks.nsfw} />
<StatusPill label="Set as header" value={data.artworks.header} />
</CardContent>
</Card> */}
{/* Color pipeline */}
{/* <Card>
<CardHeader>
<CardTitle className="text-base">Color pipeline</CardTitle>
</CardHeader>
<CardContent className="space-y-2">
<StatusPill
label="Pending"
value={data.artworks.colorStatus.PENDING}
/>
<StatusPill
label="Processing"
value={data.artworks.colorStatus.PROCESSING}
/>
<StatusPill
label="Ready"
value={data.artworks.colorStatus.READY}
/>
<StatusPill
label="Failed"
value={data.artworks.colorStatus.FAILED}
/>
<div className="pt-2 text-sm text-muted-foreground">
Tip: keep “Failed” near zero—those typically need a re-run or file
fix.
</div>
</CardContent>
</Card> */}
{/* Commissions status */} {/* Commissions status */}
<Card> <Card>
<CardHeader> <CardHeader>
@ -153,84 +101,6 @@ export default async function HomePage() {
</CardContent> </CardContent>
</Card> </Card>
</section> </section>
{/* Recent activity */}
{/* <section className="grid gap-4 lg:grid-cols-2">
<Card>
<CardHeader className="flex flex-row items-center justify-between">
<CardTitle className="text-base">Recent artworks</CardTitle>
<Button asChild variant="ghost" size="sm">
<Link href="/artworks">Open</Link>
</Button>
</CardHeader>
<CardContent className="space-y-3">
{data.artworks.recent.length === 0 ? (
<div className="text-sm text-muted-foreground">No artworks yet.</div>
) : (
<ul className="space-y-2">
{data.artworks.recent.map((a) => (
<li
key={a.id}
className="flex items-center justify-between gap-3 rounded-md border px-3 py-2"
>
<div className="min-w-0">
<div className="truncate font-medium">{a.name}</div>
<div className="text-xs text-muted-foreground">
{fmtDate(a.createdAt)} · {a.colorStatus}
{a.published ? " · published" : " · draft"}
{a.needsWork ? " · needs work" : ""}
</div>
</div>
<Button asChild variant="secondary" size="sm">
<Link href={`/artworks/${a.slug}`}>Open</Link>
</Button>
</li>
))}
</ul>
)}
</CardContent>
</Card>
<Card>
<CardHeader className="flex flex-row items-center justify-between">
<CardTitle className="text-base">Recent commission requests</CardTitle>
<Button asChild variant="ghost" size="sm">
<Link href="/commissions/requests">Open</Link>
</Button>
</CardHeader>
<CardContent className="space-y-3">
{data.commissions.recent.length === 0 ? (
<div className="text-sm text-muted-foreground">
No commission requests yet.
</div>
) : (
<ul className="space-y-2">
{data.commissions.recent.map((r) => (
<li
key={r.id}
className="flex items-center justify-between gap-3 rounded-md border px-3 py-2"
>
<div className="min-w-0">
<div className="truncate font-medium">
{r.customerName}{" "}
<span className="text-muted-foreground">
({r.customerEmail})
</span>
</div>
<div className="text-xs text-muted-foreground">
{fmtDate(r.createdAt)} · {r.status}
</div>
</div>
<Button asChild variant="secondary" size="sm">
<Link href={`/commissions/requests/${r.id}`}>Open</Link>
</Button>
</li>
))}
</ul>
)}
</CardContent>
</Card>
</section> */}
</div> </div>
); );
} }

View File

@ -1,25 +1,46 @@
import EditTagForm from "@/components/tags/EditTagForm"; import EditTagForm from "@/components/tags/EditTagForm";
import { prisma } from "@/lib/prisma"; import { prisma } from "@/lib/prisma";
// Edit tag page.
export default async function PortfolioTagsEditPage({ params }: { params: { id: string } }) { export default async function PortfolioTagsEditPage({ params }: { params: { id: string } }) {
const { id } = await params; const { id } = await params;
const tag = await prisma.artTag.findUnique({ const tag = await prisma.tag.findUnique({
where: { where: {
id, id,
}, },
include: { include: {
categories: true, categoryLinks: {
include: {
category: true,
parentTag: { select: { id: true, name: true } },
},
},
aliases: true aliases: true
} },
}) });
const categories = await prisma.artCategory.findMany({ include: { tags: true }, orderBy: { sortIndex: "asc" } }); const categories = await prisma.artCategory.findMany({
const tags = await prisma.artTag.findMany({ where: { isParent: true }, orderBy: { sortIndex: "asc" } }); include: { tagLinks: true },
orderBy: { sortIndex: "asc" },
});
const tags = await prisma.tag.findMany({ orderBy: { sortIndex: "asc" } });
const animalLink =
tag?.categoryLinks.find((link) => link.category.name === "Animal Studies") ??
null;
const tagWithMeta = tag
? {
...tag,
parentId: animalLink?.parentTagId ?? null,
isParent: animalLink?.isParent ?? false,
showOnAnimalPage: animalLink?.showOnAnimalPage ?? false,
}
: null;
return ( return (
<div> <div>
<h1 className="text-2xl font-bold mb-4">Edit Tag</h1> <h1 className="text-2xl font-bold mb-4">Edit Tag</h1>
{tag && <EditTagForm tag={tag} categories={categories} allTags={tags} />} {tagWithMeta && <EditTagForm tag={tagWithMeta} categories={categories} allTags={tags} />}
</div> </div>
); );
} }

View File

@ -1,9 +1,13 @@
import NewTagForm from "@/components/tags/NewTagForm"; import NewTagForm from "@/components/tags/NewTagForm";
import { prisma } from "@/lib/prisma"; import { prisma } from "@/lib/prisma";
// Create a new tag page.
export default async function PortfolioTagsNewPage() { export default async function PortfolioTagsNewPage() {
const categories = await prisma.artCategory.findMany({ include: { tags: true }, orderBy: { sortIndex: "asc" } }); const categories = await prisma.artCategory.findMany({
const tags = await prisma.artTag.findMany({ where: { isParent: true }, orderBy: { sortIndex: "asc" } }); include: { tagLinks: true },
orderBy: { sortIndex: "asc" },
});
const tags = await prisma.tag.findMany({ orderBy: { sortIndex: "asc" } });
return ( return (
<div> <div>
@ -11,4 +15,4 @@ export default async function PortfolioTagsNewPage() {
<NewTagForm categories={categories} allTags={tags} /> <NewTagForm categories={categories} allTags={tags} />
</div> </div>
); );
} }

View File

@ -4,39 +4,63 @@ import { prisma } from "@/lib/prisma";
import { PlusCircleIcon } from "lucide-react"; import { PlusCircleIcon } from "lucide-react";
import Link from "next/link"; import Link from "next/link";
// Admin tags management page.
export default async function ArtTagsPage() { export default async function ArtTagsPage() {
const items = await prisma.artTag.findMany({ const items = await prisma.tag.findMany({
include: { include: {
parent: { select: { id: true, name: true } },
aliases: { select: { alias: true } }, aliases: { select: { alias: true } },
categories: { select: { id: true, name: true } }, categoryLinks: {
include: {
category: { select: { id: true, name: true } },
parentTag: { select: { id: true, name: true } },
},
},
_count: { select: { artworks: true } }, _count: { select: { artworks: true } },
}, },
orderBy: [{ sortIndex: "asc" }, { name: "asc" }], orderBy: [{ sortIndex: "asc" }, { name: "asc" }],
}); });
const rows = items.map((tag) => {
const categories = tag.categoryLinks.map((link) => link.category);
const animalLink = tag.categoryLinks.find(
(link) => link.category.name === "Animal Studies",
);
return {
id: tag.id,
name: tag.name,
slug: tag.slug,
parent: animalLink?.parentTag ?? null,
isParent: animalLink?.isParent ?? false,
showOnAnimalPage: animalLink?.showOnAnimalPage ?? false,
aliases: tag.aliases,
categories,
_count: tag._count,
};
});
return ( return (
<div className="mx-auto w-full max-w-7xl px-4 py-6"> <div className="mx-auto w-full max-w-7xl px-4 py-6">
<header className="mb-6 flex flex-col gap-3 sm:flex-row sm:items-end sm:justify-between"> <header className="mb-6 flex flex-col gap-3 sm:flex-row sm:items-end sm:justify-between">
<div className="space-y-1"> <div className="space-y-1">
<h1 className="text-2xl font-semibold tracking-tight sm:text-3xl"> <h1 className="text-2xl font-semibold tracking-tight sm:text-3xl">
Art Tags Tags
</h1> </h1>
<p className="text-sm text-muted-foreground"> <p className="text-sm text-muted-foreground">Manage tags.</p>
Manage tags, aliases, categories, and usage across artworks.
</p>
</div> </div>
<Button asChild className="h-11 gap-2"> <div className="flex flex-col gap-2 sm:flex-row sm:items-center">
<Link href="/tags/new"> <Button asChild className="h-11 gap-2">
<PlusCircleIcon className="h-4 w-4" /> <Link href="/tags/new">
Add new tag <PlusCircleIcon className="h-4 w-4" />
</Link> Add new tag
</Button> </Link>
</Button>
</div>
</header> </header>
{items.length > 0 ? ( {rows.length > 0 ? (
<TagTabs tags={items} /> <TagTabs tags={rows} />
) : ( ) : (
<p className="text-muted-foreground"> <p className="text-muted-foreground">
There are no tags yet. Consider adding some! There are no tags yet. Consider adding some!
@ -44,4 +68,4 @@ export default async function ArtTagsPage() {
)} )}
</div> </div>
); );
} }

View File

@ -1,6 +1,7 @@
import { getLatestTos } from "@/actions/tos/getTos"; import { getLatestTos } from "@/actions/tos/getTos";
import TosEditor from "@/components/tos/Editor"; import TosEditor from "@/components/tos/Editor";
// Admin page for editing Terms of Service.
export default async function TosPage() { export default async function TosPage() {
const markdown = await getLatestTos(); const markdown = await getLatestTos();
@ -14,4 +15,4 @@ export default async function TosPage() {
</div> </div>
</div> </div>
); );
} }

View File

@ -1,7 +1,10 @@
import UploadBulkImageForm from "@/components/uploads/UploadBulkImageForm"; import UploadBulkImageForm from "@/components/uploads/UploadBulkImageForm";
// Bulk image upload page.
export default function UploadsBulkPage() { export default function UploadsBulkPage() {
return ( return (
<div><UploadBulkImageForm /></div> <div>
<UploadBulkImageForm />
</div>
); );
} }

View File

@ -1,7 +1,10 @@
import UploadImageForm from "@/components/uploads/UploadImageForm"; import UploadImageForm from "@/components/uploads/UploadImageForm";
// Single image upload page.
export default function UploadsSinglePage() { export default function UploadsSinglePage() {
return ( return (
<div><UploadImageForm /></div> <div>
<UploadImageForm />
</div>
); );
} }

View File

@ -1,11 +1,13 @@
import { CreateUserForm } from "@/components/users/CreateUserForm"; import { CreateUserForm } from "@/components/users/CreateUserForm";
import { auth } from "@/lib/auth"; import { auth } from "@/lib/auth";
import type { SessionWithRole } from "@/types/auth";
import { headers } from "next/headers"; import { headers } from "next/headers";
import { redirect } from "next/navigation"; import { redirect } from "next/navigation";
// Admin-only user creation page.
export default async function NewUserPage() { export default async function NewUserPage() {
const session = await auth.api.getSession({ headers: await headers() }); const session = await auth.api.getSession({ headers: await headers() });
const role = (session as any)?.user?.role; const role = (session as SessionWithRole)?.user?.role;
if (!session) redirect("/login"); if (!session) redirect("/login");
if (role !== "admin") redirect("/"); if (role !== "admin") redirect("/");

View File

@ -1,11 +1,13 @@
import { UsersTable } from "@/components/users/UsersTable"; import { UsersTable } from "@/components/users/UsersTable";
import { auth } from "@/lib/auth"; import { auth } from "@/lib/auth";
import type { SessionWithRole } from "@/types/auth";
import { headers } from "next/headers"; import { headers } from "next/headers";
import { redirect } from "next/navigation"; import { redirect } from "next/navigation";
// Admin users list page.
export default async function UsersPage() { export default async function UsersPage() {
const session = await auth.api.getSession({ headers: await headers() }); const session = await auth.api.getSession({ headers: await headers() });
const role = (session as any)?.user?.role as string | undefined; const role = (session as SessionWithRole)?.user?.role;
if (!session) redirect("/login"); if (!session) redirect("/login");
if (role !== "admin") redirect("/"); if (role !== "admin") redirect("/");

View File

@ -1,5 +1,6 @@
import { ForgotPasswordForm } from "@/components/auth/ForgotPasswordForm"; import { ForgotPasswordForm } from "@/components/auth/ForgotPasswordForm";
// Forgot password page.
export default function ForgotPasswordPage() { export default function ForgotPasswordPage() {
return ( return (
<div className="mx-auto max-w-md p-6"> <div className="mx-auto max-w-md p-6">

View File

@ -1,8 +1,10 @@
import type { ReactNode } from "react";
// Layout wrapper for auth routes.
export default function AuthLayout({ export default function AuthLayout({
children, children,
}: Readonly<{ }: Readonly<{
children: React.ReactNode; children: ReactNode;
}>) { }>) {
return ( return (
<div className="flex flex-col min-h-screen min-w-screen"> <div className="flex flex-col min-h-screen min-w-screen">

View File

@ -1,6 +1,7 @@
import LoginForm from "@/components/auth/LoginForm"; import LoginForm from "@/components/auth/LoginForm";
import { Suspense } from "react"; import { Suspense } from "react";
// Admin login page.
export default function LoginPage() { export default function LoginPage() {
return ( return (
<div className="flex min-h-screen items-center justify-center"> <div className="flex min-h-screen items-center justify-center">
@ -20,4 +21,4 @@ export default function LoginPage() {
</div> </div>
</div> </div>
); );
} }

View File

@ -2,6 +2,7 @@ import { RegisterForm } from "@/components/auth/RegisterForm";
import { prisma } from "@/lib/prisma"; import { prisma } from "@/lib/prisma";
import { redirect } from "next/navigation"; import { redirect } from "next/navigation";
// One-time admin registration page (only when no users exist).
export default async function RegisterPage() { export default async function RegisterPage() {
const count = await prisma.user.count(); const count = await prisma.user.count();
if (count !== 0) redirect("/login"); if (count !== 0) redirect("/login");

View File

@ -1,6 +1,7 @@
import { ResetPasswordForm } from "@/components/auth/ResetPasswordForm"; import { ResetPasswordForm } from "@/components/auth/ResetPasswordForm";
import Link from "next/link"; import Link from "next/link";
// Reset password page, expects a token query param.
export default async function ResetPasswordPage({ export default async function ResetPasswordPage({
searchParams, searchParams,
}: { }: {
@ -13,7 +14,7 @@ export default async function ResetPasswordPage({
<div className="mx-auto max-w-md p-6"> <div className="mx-auto max-w-md p-6">
<p>No valid token, please try again or get back to <Link href="/">Home</Link></p> <p>No valid token, please try again or get back to <Link href="/">Home</Link></p>
</div> </div>
) );
} }
return ( return (

View File

@ -1,6 +1,7 @@
import { getArtworksPage } from "@/lib/queryArtworks"; import { getArtworksPage } from "@/lib/queryArtworks";
import { NextResponse, type NextRequest } from "next/server"; import { NextResponse, type NextRequest } from "next/server";
// Public API for paginated artworks listing.
export async function GET(req: NextRequest) { export async function GET(req: NextRequest) {
const publishedParam = req.nextUrl.searchParams.get("published") ?? "all"; const publishedParam = req.nextUrl.searchParams.get("published") ?? "all";

View File

@ -1,4 +1,5 @@
import { auth } from "@/lib/auth"; import { auth } from "@/lib/auth";
import { toNextJsHandler } from "better-auth/next-js"; import { toNextJsHandler } from "better-auth/next-js";
export const { POST, GET } = toNextJsHandler(auth); // Better Auth route handlers.
export const { POST, GET } = toNextJsHandler(auth);

View File

@ -1,10 +1,27 @@
import { s3 } from "@/lib/s3"; import { s3 } from "@/lib/s3";
import type { S3Body } from "@/types/s3";
import { GetObjectCommand } from "@aws-sdk/client-s3"; import { GetObjectCommand } from "@aws-sdk/client-s3";
import type { NextRequest } from "next/server"; import type { NextRequest } from "next/server";
import { Readable } from "node:stream";
function isWebReadableStream(value: unknown): value is ReadableStream<Uint8Array> {
return !!value && typeof (value as ReadableStream<Uint8Array>).getReader === "function";
}
function toBodyInit(body: S3Body): BodyInit {
if (body instanceof Readable) {
return Readable.toWeb(body) as ReadableStream<Uint8Array>;
}
if (isWebReadableStream(body)) {
return body;
}
return body as BodyInit;
}
// Streams images from S3 for the admin app.
export async function GET(_req: NextRequest, context: { params: Promise<{ key: string[] }> }) { export async function GET(_req: NextRequest, context: { params: Promise<{ key: string[] }> }) {
const { key } = await context.params; const { key } = await context.params;
const s3Key = key.join("/"); const s3Key = key.join("/");
try { try {
const command = new GetObjectCommand({ const command = new GetObjectCommand({
@ -20,7 +37,7 @@ export async function GET(_req: NextRequest, context: { params: Promise<{ key: s
const contentType = response.ContentType ?? "application/octet-stream"; const contentType = response.ContentType ?? "application/octet-stream";
return new Response(response.Body as ReadableStream, { return new Response(toBodyInit(response.Body as S3Body), {
headers: { headers: {
"Content-Type": contentType, "Content-Type": contentType,
"Cache-Control": "public, max-age=3600", "Cache-Control": "public, max-age=3600",
@ -28,7 +45,7 @@ export async function GET(_req: NextRequest, context: { params: Promise<{ key: s
}, },
}); });
} catch (err) { } catch (err) {
console.log(err) console.error(err);
return new Response("Image not found", { status: 404 }); return new Response("Image not found", { status: 404 });
} }
} }

View File

@ -1,9 +1,12 @@
import { prisma } from "@/lib/prisma"; import { prisma } from "@/lib/prisma";
import { s3 } from "@/lib/s3"; import { s3 } from "@/lib/s3";
import type { S3Body } from "@/types/s3";
import { GetObjectCommand } from "@aws-sdk/client-s3"; import { GetObjectCommand } from "@aws-sdk/client-s3";
import archiver from "archiver"; import archiver from "archiver";
import { NextRequest } from "next/server"; import type { NextRequest } from "next/server";
import { Readable } from "node:stream";
// Streams commission request files (single or zip) from S3.
type Mode = "display" | "download" | "bulk"; type Mode = "display" | "download" | "bulk";
function contentDisposition(filename: string, mode: Mode) { function contentDisposition(filename: string, mode: Mode) {
@ -14,7 +17,38 @@ function contentDisposition(filename: string, mode: Mode) {
} }
function sanitizeZipEntryName(name: string) { function sanitizeZipEntryName(name: string) {
return name.replace(/[^\w.\- ()\[\]]+/g, "_").slice(0, 180); return name.replace(/[^\w.\- ()[\]]+/g, "_").slice(0, 180);
}
function isWebReadableStream(value: unknown): value is ReadableStream<Uint8Array> {
return !!value && typeof (value as ReadableStream<Uint8Array>).getReader === "function";
}
function webStreamToAsyncIterable(stream: ReadableStream<Uint8Array>) {
const reader = stream.getReader();
return {
async *[Symbol.asyncIterator]() {
try {
while (true) {
const { done, value } = await reader.read();
if (done) break;
if (value) yield value;
}
} finally {
reader.releaseLock();
}
},
};
}
function toBodyInit(body: S3Body): BodyInit {
if (body instanceof Readable) {
return Readable.toWeb(body) as ReadableStream<Uint8Array>;
}
if (isWebReadableStream(body)) {
return body;
}
return body as BodyInit;
} }
export async function GET(req: NextRequest) { export async function GET(req: NextRequest) {
@ -52,7 +86,7 @@ export async function GET(req: NextRequest) {
const contentType = file.fileType || s3Res.ContentType || "application/octet-stream"; const contentType = file.fileType || s3Res.ContentType || "application/octet-stream";
return new Response(s3Res.Body as ReadableStream, { return new Response(toBodyInit(s3Res.Body as S3Body), {
headers: { headers: {
"Content-Type": contentType, "Content-Type": contentType,
// You can tune caching; admin-only content usually should be private. // You can tune caching; admin-only content usually should be private.
@ -117,8 +151,24 @@ export async function GET(req: NextRequest) {
f.originalFile || f.fileKey.split("/").pop() || "file" f.originalFile || f.fileKey.split("/").pop() || "file"
); );
// obj.Body is a Node stream in Node runtime; works with archiver // obj.Body can be a Node Readable, web ReadableStream, or Buffer.
archive.append(obj.Body as any, { name: entryName }); const body = obj.Body;
if (!body) continue;
if (body instanceof Readable) {
archive.append(body, { name: entryName });
} else if (isWebReadableStream(body)) {
archive.append(Readable.from(webStreamToAsyncIterable(body)), { name: entryName });
} else if (body instanceof Blob) {
const stream = body.stream();
archive.append(Readable.from(webStreamToAsyncIterable(stream)), { name: entryName });
} else if (Buffer.isBuffer(body)) {
archive.append(body, { name: entryName });
} else if (body instanceof Uint8Array) {
archive.append(Buffer.from(body), { name: entryName });
} else {
throw new Error("Unsupported S3 body type for zip entry");
}
} }
await archive.finalize(); await archive.finalize();

View File

@ -1,38 +1,11 @@
import { prisma } from "@/lib/prisma"; import { prisma } from "@/lib/prisma";
import { s3 } from "@/lib/s3"; import { s3 } from "@/lib/s3";
import { publicCommissionRequestSchema } from "@/schemas/commissions/publicRequest";
import { DeleteObjectsCommand, PutObjectCommand } from "@aws-sdk/client-s3"; import { DeleteObjectsCommand, PutObjectCommand } from "@aws-sdk/client-s3";
import { NextResponse } from "next/server"; import { NextResponse } from "next/server";
import { v4 as uuidv4 } from "uuid"; import { v4 as uuidv4 } from "uuid";
import { z } from "zod/v4";
const payloadSchema = z.object({ // Public API endpoint for commission submissions (multipart form).
typeId: z.string().min(1).optional().nullable(),
customCardId: z.string().min(1).optional().nullable(),
optionId: z.string().min(1).optional().nullable(),
extraIds: z.array(z.string().min(1)).default([]),
customerName: z.string().min(1).max(200),
customerEmail: z.string().email().max(320),
customerSocials: z.string().max(2000).optional().nullable(),
message: z.string().min(1).max(20_000),
}).superRefine((data, ctx) => {
const hasType = Boolean(data.typeId);
const hasCustom = Boolean(data.customCardId);
if (!hasType && !hasCustom) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
path: ["typeId"],
message: "Missing commission type or custom card",
});
}
if (hasType && hasCustom) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
path: ["typeId"],
message: "Only one of typeId or customCardId is allowed",
});
}
});
function safeJsonParse(input: string) { function safeJsonParse(input: string) {
try { try {
@ -64,7 +37,7 @@ export async function POST(request: Request) {
return NextResponse.json({ error: "Invalid payload JSON" }, { status: 400 }); return NextResponse.json({ error: "Invalid payload JSON" }, { status: 400 });
} }
const payload = payloadSchema.safeParse(parsedJson); const payload = publicCommissionRequestSchema.safeParse(parsedJson);
if (!payload.success) { if (!payload.success) {
return NextResponse.json( return NextResponse.json(
{ error: "Validation error", issues: payload.error.issues }, { error: "Validation error", issues: payload.error.issues },

View File

@ -2,7 +2,8 @@
import { useEffect } from "react"; import { useEffect } from "react";
export default function Error({ // Global error UI for the app router segment.
export default function ErrorPage({
error, error,
reset, reset,
}: { }: {
@ -26,6 +27,7 @@ export default function Error({
<div className="flex justify-center gap-4 pt-2"> <div className="flex justify-center gap-4 pt-2">
<button <button
type="button"
onClick={reset} onClick={reset}
className="rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90 transition" className="rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90 transition"
> >

Some files were not shown because too many files have changed in this diff Show More