mirror of
https://github.com/Anil-matcha/Open-Generative-AI.git
synced 2026-05-07 01:17:18 +00:00
Merge pull request #6 from Ahoum-Dev/feat/local-upload-fallback
feat(batch): local-only upload fallback when MuAPI is unreachable
This commit is contained in:
commit
8fdc8120a7
5 changed files with 172 additions and 83 deletions
|
|
@ -2,7 +2,7 @@ import { NextResponse } from 'next/server';
|
|||
import prisma from '@/lib/prisma';
|
||||
import { getApiKey } from '@/lib/batchAuth';
|
||||
import { uploadFileToMuapi } from '@/lib/muapiUpload';
|
||||
import { saveLocalBackup } from '@/lib/localUploadStore';
|
||||
import { saveLocalBackup, publicUrlFor } from '@/lib/localUploadStore';
|
||||
|
||||
export async function GET() {
|
||||
const studios = await prisma.studio.findMany({
|
||||
|
|
@ -12,14 +12,6 @@ export async function GET() {
|
|||
}
|
||||
|
||||
export async function POST(request) {
|
||||
const apiKey = getApiKey(request);
|
||||
if (!apiKey) {
|
||||
return NextResponse.json(
|
||||
{ error: 'MuAPI API key is required. Set it in /studio or pass x-api-key.' },
|
||||
{ status: 401 },
|
||||
);
|
||||
}
|
||||
|
||||
let form;
|
||||
try {
|
||||
form = await request.formData();
|
||||
|
|
@ -46,25 +38,39 @@ export async function POST(request) {
|
|||
}
|
||||
}
|
||||
|
||||
let muapiUrl;
|
||||
try {
|
||||
muapiUrl = await uploadFileToMuapi(apiKey, file);
|
||||
} catch (err) {
|
||||
return NextResponse.json({ error: err.message }, { status: 502 });
|
||||
}
|
||||
|
||||
const studio = await prisma.studio.create({
|
||||
data: { name, csvLabel, imageUrl: muapiUrl },
|
||||
data: { name, csvLabel, imageUrl: '' },
|
||||
});
|
||||
|
||||
const localPath = await saveLocalBackup('studios', studio.id, file);
|
||||
if (localPath) {
|
||||
await prisma.studio.update({
|
||||
where: { id: studio.id },
|
||||
data: { localPath },
|
||||
});
|
||||
studio.localPath = localPath;
|
||||
const { localPath, fileName } = await saveLocalBackup('studios', studio.id, file);
|
||||
|
||||
let muapiUrl = null;
|
||||
let muapiNote = null;
|
||||
const apiKey = getApiKey(request);
|
||||
if (apiKey) {
|
||||
try {
|
||||
muapiUrl = await uploadFileToMuapi(apiKey, file);
|
||||
} catch (err) {
|
||||
muapiNote = err.message;
|
||||
}
|
||||
} else {
|
||||
muapiNote = 'No MuAPI key — local copy only. Set the key in /studio when credits are available.';
|
||||
}
|
||||
|
||||
return NextResponse.json({ studio }, { status: 201 });
|
||||
const imageUrl = muapiUrl || (fileName ? publicUrlFor('studios', fileName) : '');
|
||||
|
||||
if (!imageUrl) {
|
||||
await prisma.studio.delete({ where: { id: studio.id } });
|
||||
return NextResponse.json(
|
||||
{ error: `Failed to persist image. MuAPI: ${muapiNote || 'n/a'}. Local: write failed.` },
|
||||
{ status: 500 },
|
||||
);
|
||||
}
|
||||
|
||||
const updated = await prisma.studio.update({
|
||||
where: { id: studio.id },
|
||||
data: { imageUrl, localPath },
|
||||
});
|
||||
|
||||
return NextResponse.json({ studio: updated, muapiNote }, { status: 201 });
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { NextResponse } from 'next/server';
|
|||
import prisma from '@/lib/prisma';
|
||||
import { getApiKey } from '@/lib/batchAuth';
|
||||
import { uploadFileToMuapi } from '@/lib/muapiUpload';
|
||||
import { saveLocalBackup } from '@/lib/localUploadStore';
|
||||
import { saveLocalBackup, publicUrlFor } from '@/lib/localUploadStore';
|
||||
|
||||
export async function GET() {
|
||||
const trainers = await prisma.trainer.findMany({
|
||||
|
|
@ -12,18 +12,10 @@ export async function GET() {
|
|||
}
|
||||
|
||||
export async function POST(request) {
|
||||
const apiKey = getApiKey(request);
|
||||
if (!apiKey) {
|
||||
return NextResponse.json(
|
||||
{ error: 'MuAPI API key is required. Set it in /studio or pass x-api-key.' },
|
||||
{ status: 401 },
|
||||
);
|
||||
}
|
||||
|
||||
let form;
|
||||
try {
|
||||
form = await request.formData();
|
||||
} catch (err) {
|
||||
} catch {
|
||||
return NextResponse.json({ error: 'Invalid multipart body' }, { status: 400 });
|
||||
}
|
||||
|
||||
|
|
@ -46,25 +38,45 @@ export async function POST(request) {
|
|||
}
|
||||
}
|
||||
|
||||
let muapiUrl;
|
||||
try {
|
||||
muapiUrl = await uploadFileToMuapi(apiKey, file);
|
||||
} catch (err) {
|
||||
return NextResponse.json({ error: err.message }, { status: 502 });
|
||||
}
|
||||
|
||||
// Create the row first so we have an id for the local filename.
|
||||
const trainer = await prisma.trainer.create({
|
||||
data: { name, csvLabel, imageUrl: muapiUrl },
|
||||
data: { name, csvLabel, imageUrl: '' },
|
||||
});
|
||||
|
||||
const localPath = await saveLocalBackup('trainers', trainer.id, file);
|
||||
if (localPath) {
|
||||
await prisma.trainer.update({
|
||||
where: { id: trainer.id },
|
||||
data: { localPath },
|
||||
});
|
||||
trainer.localPath = localPath;
|
||||
// Always save a local copy.
|
||||
const { localPath, fileName } = await saveLocalBackup('trainers', trainer.id, file);
|
||||
|
||||
// Try MuAPI in the background — non-fatal if it fails (no credits, no key, etc).
|
||||
// The slice-4 worker will re-upload from the local copy when submitting jobs.
|
||||
let muapiUrl = null;
|
||||
let muapiNote = null;
|
||||
const apiKey = getApiKey(request);
|
||||
if (apiKey) {
|
||||
try {
|
||||
muapiUrl = await uploadFileToMuapi(apiKey, file);
|
||||
} catch (err) {
|
||||
muapiNote = err.message;
|
||||
}
|
||||
} else {
|
||||
muapiNote = 'No MuAPI key — local copy only. Set the key in /studio when credits are available.';
|
||||
}
|
||||
|
||||
return NextResponse.json({ trainer }, { status: 201 });
|
||||
// Pick the best display URL we have. Prefer MuAPI (CDN), fall back to local route.
|
||||
const imageUrl = muapiUrl || (fileName ? publicUrlFor('trainers', fileName) : '');
|
||||
|
||||
if (!imageUrl) {
|
||||
// Both MuAPI and local backup failed — abandon the row so we don't leave junk.
|
||||
await prisma.trainer.delete({ where: { id: trainer.id } });
|
||||
return NextResponse.json(
|
||||
{ error: `Failed to persist image. MuAPI: ${muapiNote || 'n/a'}. Local: write failed.` },
|
||||
{ status: 500 },
|
||||
);
|
||||
}
|
||||
|
||||
const updated = await prisma.trainer.update({
|
||||
where: { id: trainer.id },
|
||||
data: { imageUrl, localPath },
|
||||
});
|
||||
|
||||
return NextResponse.json({ trainer: updated, muapiNote }, { status: 201 });
|
||||
}
|
||||
|
|
|
|||
24
app/api/uploads/[kind]/[name]/route.js
Normal file
24
app/api/uploads/[kind]/[name]/route.js
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
import { NextResponse } from 'next/server';
|
||||
import { readLocal } from '@/lib/localUploadStore';
|
||||
|
||||
const ALLOWED = new Set(['trainers', 'studios']);
|
||||
|
||||
export async function GET(_request, { params }) {
|
||||
const { kind, name } = await params;
|
||||
if (!ALLOWED.has(kind)) {
|
||||
return NextResponse.json({ error: 'unknown asset kind' }, { status: 404 });
|
||||
}
|
||||
|
||||
const file = await readLocal(kind, decodeURIComponent(name));
|
||||
if (!file) {
|
||||
return NextResponse.json({ error: 'not found' }, { status: 404 });
|
||||
}
|
||||
|
||||
return new NextResponse(file.buf, {
|
||||
status: 200,
|
||||
headers: {
|
||||
'Content-Type': file.contentType,
|
||||
'Cache-Control': 'private, max-age=3600',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
@ -1,21 +1,41 @@
|
|||
import { mkdir, writeFile } from 'node:fs/promises';
|
||||
import { mkdir, readFile, writeFile, stat } from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
|
||||
const ROOT = process.env.UPLOAD_DIR || '/data/uploads';
|
||||
export const ROOT = process.env.UPLOAD_DIR || '/data/uploads';
|
||||
|
||||
export async function saveLocalBackup(kind, id, file) {
|
||||
try {
|
||||
const dir = path.join(ROOT, kind);
|
||||
await mkdir(dir, { recursive: true });
|
||||
const ext = inferExtension(file);
|
||||
const target = path.join(dir, `${id}${ext}`);
|
||||
const fileName = `${id}${ext}`;
|
||||
const target = path.join(dir, fileName);
|
||||
const buf = Buffer.from(await file.arrayBuffer());
|
||||
await writeFile(target, buf);
|
||||
return target;
|
||||
return { localPath: target, fileName };
|
||||
} catch (err) {
|
||||
console.warn(`[localUploadStore] backup write skipped (${kind}/${id}):`, err.message);
|
||||
return { localPath: null, fileName: null };
|
||||
}
|
||||
}
|
||||
|
||||
export async function readLocal(kind, fileName) {
|
||||
// Reject path traversal — only accept simple names.
|
||||
if (!fileName || fileName.includes('/') || fileName.includes('..') || fileName.includes('\\')) {
|
||||
return null;
|
||||
}
|
||||
const target = path.join(ROOT, kind, fileName);
|
||||
try {
|
||||
await stat(target);
|
||||
const buf = await readFile(target);
|
||||
return { buf, contentType: contentTypeFor(fileName) };
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function publicUrlFor(kind, fileName) {
|
||||
return `/api/uploads/${kind}/${encodeURIComponent(fileName)}`;
|
||||
}
|
||||
|
||||
function inferExtension(file) {
|
||||
|
|
@ -28,3 +48,12 @@ function inferExtension(file) {
|
|||
if (type.includes('jpeg') || type.includes('jpg')) return '.jpg';
|
||||
return '.bin';
|
||||
}
|
||||
|
||||
function contentTypeFor(fileName) {
|
||||
const lower = fileName.toLowerCase();
|
||||
if (lower.endsWith('.jpg') || lower.endsWith('.jpeg')) return 'image/jpeg';
|
||||
if (lower.endsWith('.png')) return 'image/png';
|
||||
if (lower.endsWith('.webp')) return 'image/webp';
|
||||
if (lower.endsWith('.gif')) return 'image/gif';
|
||||
return 'application/octet-stream';
|
||||
}
|
||||
|
|
|
|||
74
package-lock.json
generated
74
package-lock.json
generated
|
|
@ -154,6 +154,7 @@
|
|||
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
}
|
||||
|
|
@ -488,6 +489,7 @@
|
|||
"integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/template": "^7.28.6",
|
||||
"@babel/types": "^7.29.0"
|
||||
|
|
@ -5124,7 +5126,6 @@
|
|||
"integrity": "sha512-k4eNDan0EIMTT/dUKc/g+rsJ6wcHYhNPdY19VoX/EOtaAG8DLtKCykhrUnuHPYvinn5jhAPgD2Qw9hXBwrahsw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "8.57.1",
|
||||
"@typescript-eslint/types": "8.57.1",
|
||||
|
|
@ -5657,7 +5658,6 @@
|
|||
"integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
|
|
@ -5718,7 +5718,6 @@
|
|||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz",
|
||||
"integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
|
|
@ -5883,6 +5882,7 @@
|
|||
"resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.2.tgz",
|
||||
"integrity": "sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"archiver-utils": "^2.1.0",
|
||||
"async": "^3.2.4",
|
||||
|
|
@ -5901,6 +5901,7 @@
|
|||
"resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz",
|
||||
"integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"glob": "^7.1.4",
|
||||
"graceful-fs": "^4.2.0",
|
||||
|
|
@ -5922,6 +5923,7 @@
|
|||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
|
||||
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"core-util-is": "~1.0.0",
|
||||
"inherits": "~2.0.3",
|
||||
|
|
@ -5936,13 +5938,15 @@
|
|||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/archiver-utils/node_modules/string_decoder": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"safe-buffer": "~5.1.0"
|
||||
}
|
||||
|
|
@ -5952,6 +5956,7 @@
|
|||
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
|
||||
"integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"bl": "^4.0.3",
|
||||
"end-of-stream": "^1.4.1",
|
||||
|
|
@ -6508,7 +6513,6 @@
|
|||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"baseline-browser-mapping": "^2.10.12",
|
||||
"caniuse-lite": "^1.0.30001782",
|
||||
|
|
@ -7205,6 +7209,7 @@
|
|||
"resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.2.tgz",
|
||||
"integrity": "sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"buffer-crc32": "^0.2.13",
|
||||
"crc32-stream": "^4.0.2",
|
||||
|
|
@ -7357,6 +7362,7 @@
|
|||
"resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz",
|
||||
"integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"crc32": "bin/crc32.njs"
|
||||
},
|
||||
|
|
@ -7369,6 +7375,7 @@
|
|||
"resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.3.tgz",
|
||||
"integrity": "sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"crc-32": "^1.2.0",
|
||||
"readable-stream": "^3.4.0"
|
||||
|
|
@ -7408,8 +7415,7 @@
|
|||
"version": "3.2.3",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
|
||||
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/d3-color": {
|
||||
"version": "3.1.0",
|
||||
|
|
@ -7468,7 +7474,6 @@
|
|||
"resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
|
||||
"integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
|
||||
"license": "ISC",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
|
|
@ -7829,7 +7834,6 @@
|
|||
"resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-25.1.8.tgz",
|
||||
"integrity": "sha512-NoXo6Liy2heSklTI5OIZbCgXC1RzrDQsZkeEwXhdOro3FT1VBOvbubvscdPnjVuQ4AMwwv61oaH96AbiYg9EnQ==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"app-builder-lib": "25.1.8",
|
||||
"builder-util": "25.1.7",
|
||||
|
|
@ -8037,6 +8041,7 @@
|
|||
"resolved": "https://registry.npmjs.org/electron-builder-squirrel-windows/-/electron-builder-squirrel-windows-25.1.8.tgz",
|
||||
"integrity": "sha512-2ntkJ+9+0GFP6nAISiMabKt6eqBB0kX1QqHNWFWAXgi0VULKGisM46luRFpIBiU3u/TDmhZMM8tzvo2Abn3ayg==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"app-builder-lib": "25.1.8",
|
||||
"archiver": "^5.3.1",
|
||||
|
|
@ -8049,6 +8054,7 @@
|
|||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
|
||||
"integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.2.0",
|
||||
"jsonfile": "^6.0.1",
|
||||
|
|
@ -8063,6 +8069,7 @@
|
|||
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz",
|
||||
"integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"universalify": "^2.0.0"
|
||||
},
|
||||
|
|
@ -8075,6 +8082,7 @@
|
|||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
|
||||
"integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
}
|
||||
|
|
@ -8492,7 +8500,6 @@
|
|||
"integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.8.0",
|
||||
"@eslint-community/regexpp": "^4.12.1",
|
||||
|
|
@ -8666,7 +8673,6 @@
|
|||
"integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@rtsao/scc": "^1.1.0",
|
||||
"array-includes": "^3.1.9",
|
||||
|
|
@ -9416,7 +9422,8 @@
|
|||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
|
||||
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/fs-extra": {
|
||||
"version": "8.1.0",
|
||||
|
|
@ -9547,6 +9554,7 @@
|
|||
"integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
|
|
@ -10754,7 +10762,8 @@
|
|||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/isbinaryfile": {
|
||||
"version": "5.0.7",
|
||||
|
|
@ -10968,6 +10977,7 @@
|
|||
"resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz",
|
||||
"integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"readable-stream": "^2.0.5"
|
||||
},
|
||||
|
|
@ -10980,6 +10990,7 @@
|
|||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
|
||||
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"core-util-is": "~1.0.0",
|
||||
"inherits": "~2.0.3",
|
||||
|
|
@ -10994,13 +11005,15 @@
|
|||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/lazystream/node_modules/string_decoder": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"safe-buffer": "~5.1.0"
|
||||
}
|
||||
|
|
@ -11333,25 +11346,29 @@
|
|||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
|
||||
"integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/lodash.difference": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz",
|
||||
"integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/lodash.flatten": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz",
|
||||
"integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/lodash.isplainobject": {
|
||||
"version": "4.0.6",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
|
||||
"integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/lodash.merge": {
|
||||
"version": "4.6.2",
|
||||
|
|
@ -11364,7 +11381,8 @@
|
|||
"version": "4.6.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz",
|
||||
"integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/log-symbols": {
|
||||
"version": "4.1.0",
|
||||
|
|
@ -13485,7 +13503,6 @@
|
|||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.11",
|
||||
"picocolors": "^1.1.1",
|
||||
|
|
@ -13645,7 +13662,6 @@
|
|||
"devOptional": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@prisma/config": "6.19.3",
|
||||
"@prisma/engines": "6.19.3"
|
||||
|
|
@ -13678,7 +13694,8 @@
|
|||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/progress": {
|
||||
"version": "2.0.3",
|
||||
|
|
@ -13820,7 +13837,6 @@
|
|||
"resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz",
|
||||
"integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
|
|
@ -13830,7 +13846,6 @@
|
|||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz",
|
||||
"integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"scheduler": "^0.27.0"
|
||||
},
|
||||
|
|
@ -13990,6 +14005,7 @@
|
|||
"resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz",
|
||||
"integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"minimatch": "^5.1.0"
|
||||
}
|
||||
|
|
@ -13998,13 +14014,15 @@
|
|||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/readdir-glob/node_modules/brace-expansion": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
|
||||
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0"
|
||||
}
|
||||
|
|
@ -14014,6 +14032,7 @@
|
|||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz",
|
||||
"integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"brace-expansion": "^2.0.1"
|
||||
},
|
||||
|
|
@ -15508,7 +15527,6 @@
|
|||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
|
|
@ -15736,7 +15754,6 @@
|
|||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||
"devOptional": true,
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
|
|
@ -16078,7 +16095,6 @@
|
|||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz",
|
||||
"integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"esbuild": "^0.21.3",
|
||||
"postcss": "^8.4.43",
|
||||
|
|
@ -16395,6 +16411,7 @@
|
|||
"resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.1.tgz",
|
||||
"integrity": "sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"archiver-utils": "^3.0.4",
|
||||
"compress-commons": "^4.1.2",
|
||||
|
|
@ -16409,6 +16426,7 @@
|
|||
"resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-3.0.4.tgz",
|
||||
"integrity": "sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"glob": "^7.2.3",
|
||||
"graceful-fs": "^4.2.0",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue