diff --git a/README.md b/README.md index e215bc4..0b49691 100644 --- a/README.md +++ b/README.md @@ -1,36 +1,48 @@ -This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). +## Cloudflare R2 File Drive -## Getting Started - -First, run the development server: +A simple FTP-style web UI built with Next.js App Router. Users can register/login (email + password), upload files or entire folders, list them, download via signed URLs, and delete. Files are stored in Cloudflare R2 (S3-compatible); metadata and users are stored in SQLite via Prisma. +## Setup +1) Install deps (already checked in `package-lock.json`): ```bash -npm run dev -# or -yarn dev -# or -pnpm dev -# or -bun dev +npm install ``` -Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. +2) Copy `env.example` to `.env.local` (or set env vars another way) and fill: +- `DATABASE_URL="file:./dev.db"` (default SQLite) +- `R2_ENDPOINT="https://.r2.cloudflarestorage.com"` +- `R2_REGION="auto"` +- `R2_ACCESS_KEY_ID`, `R2_SECRET_ACCESS_KEY` +- `R2_BUCKET` +- `JWT_SECRET` (long random string) +- `PORT` / `NEXT_PORT` (defaults set to 4000 to avoid 3000) -You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. +3) Create DB & Prisma client: +```bash +npx prisma db push +npx prisma generate +``` -This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. +4) Run dev server on port 4000: +```bash +npm run dev +# overrides: PORT=4500 npm run dev +``` -## Learn More +5) Production: +```bash +npm run build +PORT=4000 npm run start +``` -To learn more about Next.js, take a look at the following resources: +## Usage +- Register or log in at `/register` or `/login`. +- Dashboard at `/dashboard`: + - Upload files or folders (uses `webkitdirectory`; folder structure is preserved in keys). + - Files are stored under a user-specific prefix in R2; metadata kept in SQLite. + - Download uses short-lived signed URLs; delete removes from R2 and DB. -- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. -- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. - -You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! - -## Deploy on Vercel - -The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. - -Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. +## Notes +- Middleware protects `/dashboard` and `/api/files/*`; auth cookie is HttpOnly JWT. +- To change ports, set `PORT`/`NEXT_PORT` or pass `-p` to scripts; defaults avoid 3000. +- R2 endpoint format: `https://.r2.cloudflarestorage.com`. diff --git a/app/(auth)/login/page.tsx b/app/(auth)/login/page.tsx new file mode 100644 index 0000000..e717660 --- /dev/null +++ b/app/(auth)/login/page.tsx @@ -0,0 +1,141 @@ +"use client"; + +import Link from "next/link"; +import { useRouter } from "next/navigation"; +import { FormEvent, useState } from "react"; + +export default function LoginPage() { + const router = useRouter(); + const [email, setEmail] = useState(""); + const [password, setPassword] = useState(""); + const [error, setError] = useState(null); + const [loading, setLoading] = useState(false); + + const onSubmit = async (e: FormEvent) => { + e.preventDefault(); + setError(null); + setLoading(true); + const res = await fetch("/api/auth/login", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ email, password }), + }); + const data = await res.json(); + setLoading(false); + if (!res.ok) { + setError(data?.error ?? "Login failed"); + return; + } + router.push("/dashboard"); + }; + + return ( +
+ {/* Geometric Decorations */} +
+
+
+
+ + {/* Accent Glow */} +
+ + {/* Login Card */} +
+ {/* Header */} +
+
+
+
+
+
+

+ Welcome back +

+

+ Log in to upload and download files. +

+
+ + {/* Form */} +
+
+ + setEmail(e.target.value)} + required + autoComplete="email" + /> +
+ +
+ + setPassword(e.target.value)} + required + autoComplete="current-password" + /> +
+ + {error && ( +
+ + + +

{error}

+
+ )} + +
+ +
+
+ + {/* Footer */} +
+

+ No account yet?{" "} + + Create one + +

+
+
+
+ ); +} diff --git a/app/(auth)/register/page.tsx b/app/(auth)/register/page.tsx new file mode 100644 index 0000000..08499f7 --- /dev/null +++ b/app/(auth)/register/page.tsx @@ -0,0 +1,158 @@ +"use client"; + +import Link from "next/link"; +import { useRouter } from "next/navigation"; +import { FormEvent, useState } from "react"; + +export default function RegisterPage() { + const router = useRouter(); + const [email, setEmail] = useState(""); + const [password, setPassword] = useState(""); + const [error, setError] = useState(null); + const [loading, setLoading] = useState(false); + + const onSubmit = async (e: FormEvent) => { + e.preventDefault(); + setError(null); + setLoading(true); + const res = await fetch("/api/auth/register", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ email, password }), + }); + const data = await res.json(); + setLoading(false); + if (!res.ok) { + setError(data?.error ?? "Registration failed"); + return; + } + router.push("/dashboard"); + }; + + return ( +
+ {/* Geometric Decorations */} +
+
+
+
+ + {/* Accent Glow - Magenta variant */} +
+ + {/* Register Card */} +
+ {/* Header */} +
+
+
+
+
+
+

+ Create account +

+

+ Register to start uploading your files. +

+
+ + {/* Form */} +
+
+ + setEmail(e.target.value)} + required + autoComplete="email" + style={{ borderBottomColor: "var(--accent-magenta)" }} + /> +
+ +
+ + setPassword(e.target.value)} + required + minLength={6} + autoComplete="new-password" + style={{ borderBottomColor: "var(--accent-magenta)" }} + /> +

+ Use at least 6 characters for security +

+
+ + {error && ( +
+ + + +

{error}

+
+ )} + +
+ +
+
+ + {/* Footer */} +
+

+ Already have an account?{" "} + + Log in + +

+
+
+
+ ); +} diff --git a/app/api/auth/login/route.ts b/app/api/auth/login/route.ts new file mode 100644 index 0000000..d626f56 --- /dev/null +++ b/app/api/auth/login/route.ts @@ -0,0 +1,39 @@ +import { NextResponse } from "next/server"; +import { prisma } from "@/lib/db"; +import { + createSessionToken, + setSessionCookie, + verifyPassword, +} from "@/lib/auth"; + +export async function POST(req: Request) { + const body = await req.json().catch(() => null); + const email = (body?.email as string | undefined)?.toLowerCase()?.trim(); + const password = body?.password as string | undefined; + + if (!email || !password) { + return NextResponse.json( + { error: "Email and password are required." }, + { status: 400 } + ); + } + + const user = await prisma.user.findUnique({ where: { email } }); + if (!user) { + return NextResponse.json({ error: "Invalid credentials." }, { status: 401 }); + } + + const valid = await verifyPassword(password, user.passwordHash); + if (!valid) { + return NextResponse.json({ error: "Invalid credentials." }, { status: 401 }); + } + + const token = await createSessionToken({ userId: user.id, email: user.email }); + await setSessionCookie(token); + + return NextResponse.json({ + ok: true, + user: { id: user.id, email: user.email }, + }); +} + diff --git a/app/api/auth/logout/route.ts b/app/api/auth/logout/route.ts new file mode 100644 index 0000000..00bd72f --- /dev/null +++ b/app/api/auth/logout/route.ts @@ -0,0 +1,8 @@ +import { NextResponse } from "next/server"; +import { clearSessionCookie } from "@/lib/auth"; + +export async function POST() { + await clearSessionCookie(); + return NextResponse.json({ ok: true }); +} + diff --git a/app/api/auth/register/route.ts b/app/api/auth/register/route.ts new file mode 100644 index 0000000..d5704bf --- /dev/null +++ b/app/api/auth/register/route.ts @@ -0,0 +1,42 @@ +import { NextResponse } from "next/server"; +import { prisma } from "@/lib/db"; +import { + createSessionToken, + hashPassword, + setSessionCookie, +} from "@/lib/auth"; + +export async function POST(req: Request) { + const body = await req.json().catch(() => null); + const email = (body?.email as string | undefined)?.toLowerCase()?.trim(); + const password = body?.password as string | undefined; + + if (!email || !password || password.length < 6) { + return NextResponse.json( + { error: "Email and password (min 6 chars) are required." }, + { status: 400 } + ); + } + + const existing = await prisma.user.findUnique({ where: { email } }); + if (existing) { + return NextResponse.json( + { error: "Email is already registered." }, + { status: 400 } + ); + } + + const passwordHash = await hashPassword(password); + const user = await prisma.user.create({ + data: { email, passwordHash }, + }); + + const token = await createSessionToken({ userId: user.id, email: user.email }); + await setSessionCookie(token); + + return NextResponse.json({ + ok: true, + user: { id: user.id, email: user.email }, + }); +} + diff --git a/app/api/files/[id]/delete/route.ts b/app/api/files/[id]/delete/route.ts new file mode 100644 index 0000000..52cc131 --- /dev/null +++ b/app/api/files/[id]/delete/route.ts @@ -0,0 +1,31 @@ +import { NextResponse } from "next/server"; +import { prisma } from "@/lib/db"; +import { getSessionUser } from "@/lib/auth"; +import { deleteFromR2 } from "@/lib/r2"; + +type Props = { + params: Promise<{ id: string }>; +}; + +export async function DELETE(_req: Request, { params }: Props) { + const session = await getSessionUser(); + if (!session) { + return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); + } + + const { id } = await params; + + const file = await prisma.file.findFirst({ + where: { id, userId: session.userId }, + }); + + if (!file) { + return NextResponse.json({ error: "File not found" }, { status: 404 }); + } + + await deleteFromR2(file.key); + await prisma.file.delete({ where: { id: file.id } }); + + return NextResponse.json({ ok: true }); +} + diff --git a/app/api/files/[id]/download/route.ts b/app/api/files/[id]/download/route.ts new file mode 100644 index 0000000..fcbeb55 --- /dev/null +++ b/app/api/files/[id]/download/route.ts @@ -0,0 +1,29 @@ +import { NextResponse } from "next/server"; +import { prisma } from "@/lib/db"; +import { getSessionUser } from "@/lib/auth"; +import { getSignedDownloadUrl } from "@/lib/r2"; + +type Props = { + params: Promise<{ id: string }>; +}; + +export async function GET(_req: Request, { params }: Props) { + const session = await getSessionUser(); + if (!session) { + return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); + } + + const { id } = await params; + + const file = await prisma.file.findFirst({ + where: { id, userId: session.userId }, + }); + + if (!file) { + return NextResponse.json({ error: "File not found" }, { status: 404 }); + } + + const url = await getSignedDownloadUrl(file.key); + return NextResponse.json({ url, name: file.name }); +} + diff --git a/app/api/files/delete-all/route.ts b/app/api/files/delete-all/route.ts new file mode 100644 index 0000000..36dad8b --- /dev/null +++ b/app/api/files/delete-all/route.ts @@ -0,0 +1,33 @@ +import { NextResponse } from "next/server"; +import { prisma } from "@/lib/db"; +import { getSessionUser } from "@/lib/auth"; +import { deleteFromR2 } from "@/lib/r2"; + +export async function DELETE() { + const session = await getSessionUser(); + if (!session) { + return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); + } + + // Get all files for the user + const files = await prisma.file.findMany({ + where: { userId: session.userId }, + select: { id: true, key: true }, + }); + + if (files.length === 0) { + return NextResponse.json({ ok: true, deleted: 0 }); + } + + // Delete all files from R2 + const deletePromises = files.map((file) => deleteFromR2(file.key)); + await Promise.all(deletePromises); + + // Delete all file records from database + await prisma.file.deleteMany({ + where: { userId: session.userId }, + }); + + return NextResponse.json({ ok: true, deleted: files.length }); +} + diff --git a/app/api/files/list/route.ts b/app/api/files/list/route.ts new file mode 100644 index 0000000..cf37b54 --- /dev/null +++ b/app/api/files/list/route.ts @@ -0,0 +1,28 @@ +import { NextResponse } from "next/server"; +import { prisma } from "@/lib/db"; +import { getSessionUser } from "@/lib/auth"; + +export async function GET() { + const session = await getSessionUser(); + if (!session) { + return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); + } + + const files = await prisma.file.findMany({ + where: { userId: session.userId }, + orderBy: { createdAt: "desc" }, + }); + + return NextResponse.json({ + files: files.map((f) => ({ + id: f.id, + key: f.key, + name: f.name, + relativePath: f.relativePath, + contentType: f.contentType, + sizeBytes: Number(f.sizeBytes), + createdAt: f.createdAt, + })), + }); +} + diff --git a/app/api/files/upload/route.ts b/app/api/files/upload/route.ts new file mode 100644 index 0000000..a56407f --- /dev/null +++ b/app/api/files/upload/route.ts @@ -0,0 +1,81 @@ +import { NextResponse } from "next/server"; +import { prisma } from "@/lib/db"; +import { getSessionUser } from "@/lib/auth"; +import { uploadToR2 } from "@/lib/r2"; + +const MAX_UPLOAD_SIZE = 10 * 1024 * 1024 * 1024; // 10GB in bytes + +function serializeFile(file: Awaited>) { + return { + id: file.id, + key: file.key, + name: file.name, + relativePath: file.relativePath, + contentType: file.contentType, + sizeBytes: Number(file.sizeBytes), + createdAt: file.createdAt, + }; +} + +export async function POST(req: Request) { + const session = await getSessionUser(); + if (!session) { + return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); + } + + const formData = await req.formData(); + const files = formData.getAll("files") as File[]; + + if (!files.length) { + return NextResponse.json( + { error: "No files received in 'files' field." }, + { status: 400 } + ); + } + + // Check total upload size + const totalSize = files.reduce((acc, file) => acc + (file instanceof File ? file.size : 0), 0); + if (totalSize > MAX_UPLOAD_SIZE) { + return NextResponse.json( + { error: "Total upload size exceeds 10GB limit." }, + { status: 413 } + ); + } + + const uploaded = []; + for (const file of files) { + if (!(file instanceof File)) continue; + + const arrayBuffer = await file.arrayBuffer(); + const buffer = Buffer.from(arrayBuffer); + const fileWithPath = file as File & { webkitRelativePath?: string }; + const relativePath = + fileWithPath.webkitRelativePath ?? fileWithPath.name ?? "unnamed"; + const normalizedPath = relativePath.replace(/\\/g, "/"); + const name = normalizedPath.split("/").pop() ?? normalizedPath; + const key = `${session.userId}/${Date.now()}-${normalizedPath}`; + const contentType = file.type || "application/octet-stream"; + + await uploadToR2({ + key, + contentType, + body: buffer, + }); + + const record = await prisma.file.create({ + data: { + key, + name, + relativePath: normalizedPath, + contentType, + sizeBytes: BigInt(file.size), + userId: session.userId, + }, + }); + + uploaded.push(serializeFile(record)); + } + + return NextResponse.json({ ok: true, files: uploaded }); +} + diff --git a/app/dashboard/page.tsx b/app/dashboard/page.tsx new file mode 100644 index 0000000..511cc89 --- /dev/null +++ b/app/dashboard/page.tsx @@ -0,0 +1,721 @@ +"use client"; + +import { FormEvent, useEffect, useRef, useState, DragEvent } from "react"; +import { useRouter } from "next/navigation"; + +type FileRow = { + id: string; + name: string; + relativePath: string; + contentType: string; + sizeBytes: number; + createdAt: string | Date; +}; + +const MAX_UPLOAD_SIZE = 10 * 1024 * 1024 * 1024; // 10GB in bytes + +function formatBytes(bytes: number) { + if (!bytes) return "0 B"; + const sizes = ["B", "KB", "MB", "GB", "TB"]; + const i = Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), sizes.length - 1); + const value = bytes / Math.pow(1024, i); + return `${value.toFixed(value >= 10 || value % 1 === 0 ? 0 : 1)} ${sizes[i]}`; +} + +function getFileIcon(contentType: string) { + if (contentType.startsWith("image/")) { + return ( + + + + ); + } + if (contentType.startsWith("video/")) { + return ( + + + + ); + } + if (contentType.includes("pdf")) { + return ( + + + + ); + } + return ( + + + + ); +} + +export default function DashboardPage() { + const router = useRouter(); + const [files, setFiles] = useState([]); + const [loadingList, setLoadingList] = useState(true); + const [uploading, setUploading] = useState(false); + const [error, setError] = useState(null); + const [isDragging, setIsDragging] = useState(false); + const [deletingAll, setDeletingAll] = useState(false); + const [showDeleteAllConfirm, setShowDeleteAllConfirm] = useState(false); + const [deletingFileId, setDeletingFileId] = useState(null); + const [selectedFiles, setSelectedFiles] = useState([]); + const fileInputRef = useRef(null); + const folderInputRef = useRef(null); + + const loadFiles = async () => { + setLoadingList(true); + const res = await fetch("/api/files/list"); + const data = await res.json(); + setLoadingList(false); + if (!res.ok) { + setError(data?.error ?? "Unable to load files"); + return; + } + setFiles(data.files ?? []); + }; + + useEffect(() => { + loadFiles(); + }, []); + + const handleFileSelect = (e: React.ChangeEvent) => { + const files = e.target.files; + if (files && files.length > 0) { + const fileArray = Array.from(files); + setSelectedFiles((prev) => [...prev, ...fileArray]); + } + // Reset input to allow selecting the same file again + e.target.value = ""; + }; + + const getTotalSelectedSize = () => { + return selectedFiles.reduce((acc, f) => acc + f.size, 0); + }; + + const isOverSizeLimit = () => { + return getTotalSelectedSize() > MAX_UPLOAD_SIZE; + }; + + const handleUpload = async (e: FormEvent) => { + e.preventDefault(); + if (selectedFiles.length === 0) { + setError("Please choose at least one file or folder."); + return; + } + + if (isOverSizeLimit()) { + setError("Total upload size exceeds 10GB limit. Please remove some files."); + return; + } + + const formData = new FormData(); + selectedFiles.forEach((file) => { + const fileWithPath = file as File & { webkitRelativePath?: string }; + const filename = fileWithPath.webkitRelativePath || fileWithPath.name; + formData.append("files", fileWithPath, filename); + }); + + setUploading(true); + setError(null); + const res = await fetch("/api/files/upload", { + method: "POST", + body: formData, + }); + const data = await res.json(); + setUploading(false); + if (!res.ok) { + setError(data?.error ?? "Upload failed"); + return; + } + setFiles((prev) => [...data.files, ...prev]); + setSelectedFiles([]); + }; + + const clearSelectedFiles = () => { + setSelectedFiles([]); + }; + + const addFilesToSelection = (fileList: FileList) => { + setSelectedFiles((prev) => [...prev, ...Array.from(fileList)]); + }; + + const handleDragOver = (e: DragEvent) => { + e.preventDefault(); + setIsDragging(true); + }; + + const handleDragLeave = (e: DragEvent) => { + e.preventDefault(); + setIsDragging(false); + }; + + const handleDrop = (e: DragEvent) => { + e.preventDefault(); + setIsDragging(false); + + if (e.dataTransfer.files && e.dataTransfer.files.length > 0) { + addFilesToSelection(e.dataTransfer.files); + } + }; + + const handleDownload = async (id: string) => { + const res = await fetch(`/api/files/${id}/download`); + const data = await res.json(); + if (!res.ok) { + setError(data?.error ?? "Unable to download"); + return; + } + window.location.assign(data.url); + }; + + const handleDelete = async (id: string) => { + setDeletingFileId(id); + setError(null); + const res = await fetch(`/api/files/${id}/delete`, { method: "DELETE" }); + const data = await res.json(); + setDeletingFileId(null); + if (!res.ok) { + setError(data?.error ?? "Unable to delete"); + return; + } + setFiles((prev) => prev.filter((f) => f.id !== id)); + }; + + const handleDeleteAll = async () => { + setDeletingAll(true); + setError(null); + const res = await fetch("/api/files/delete-all", { method: "DELETE" }); + const data = await res.json(); + setDeletingAll(false); + setShowDeleteAllConfirm(false); + if (!res.ok) { + setError(data?.error ?? "Unable to delete all files"); + return; + } + setFiles([]); + }; + + const handleLogout = async () => { + await fetch("/api/auth/logout", { method: "POST" }); + router.push("/login"); + }; + + return ( +
+ {/* Header */} +
+
+
+
+ {/* Logo */} +
+
+ + + +
+
+

+ Asa's FTP +

+

+ Asa's FTP Storage +

+
+
+
+ + +
+
+
+ + {/* Main Content */} +
+ {/* Upload Section */} +
+
+
+

+ Upload Files +

+
+ +
+
+ {/* Drop Zone */} +
+
+
+ + + +
+
+

+ {isDragging ? "Drop files here" : "Drag & drop files here"} +

+

+ or select files/folders from your computer +

+
+ {/* Hidden file inputs */} + + +
+ + +
+
+
+ + {/* Selected Files Preview */} + {selectedFiles.length > 0 && ( +
+
+

+ {selectedFiles.length} {selectedFiles.length === 1 ? "file" : "files"} selected + + ({formatBytes(getTotalSelectedSize())} / 10 GB max) + +

+ +
+ {isOverSizeLimit() && ( +
+ + + + Total size exceeds 10GB limit. Remove some files to continue. +
+ )} +
+ {selectedFiles.slice(0, 10).map((file, idx) => ( + + + + + {file.name.length > 25 ? file.name.slice(0, 22) + "..." : file.name} + + ))} + {selectedFiles.length > 10 && ( + + +{selectedFiles.length - 10} more + + )} +
+
+ )} + + {/* Upload Button */} +
+

+ {selectedFiles.length === 0 + ? "Supports files and entire folders with preserved structure" + : "Ready to upload"} +

+ +
+
+
+ + {/* Error Message */} + {error && ( +
+ + + +

{error}

+ +
+ )} +
+ + {/* Files Section */} +
+
+
+
+

+ Your Files +

+ + {files.length} {files.length === 1 ? "FILE" : "FILES"} + +
+
+ {files.length > 0 && ( + + )} + +
+
+ +
+ {loadingList ? ( +
+
+ + + + + Loading your files... +
+
+ ) : files.length === 0 ? ( +
+
+ + + +
+

No files yet

+

+ Upload your first file or folder using the upload section above. +

+
+ ) : ( + <> + {/* Desktop Table */} +
+ + + + + + + + + + + + {files.map((file, index) => ( + + + + + + + + ))} + +
NamePathSizeActions
+ {getFileIcon(file.contentType)} + {file.name}{file.relativePath}{formatBytes(file.sizeBytes)} +
+ + +
+
+
+ + {/* Mobile Cards */} +
+ {files.map((file, index) => ( +
+
+
+ {getFileIcon(file.contentType)} +
+
+

{file.name}

+

{file.relativePath}

+

{formatBytes(file.sizeBytes)}

+
+
+
+ + +
+
+ ))} +
+ + )} +
+
+
+ + {/* Delete All Confirmation Modal */} + {showDeleteAllConfirm && ( +
+
+
+
+ + + +
+
+

Delete All Files?

+

This action cannot be undone

+
+
+

+ You are about to permanently delete {files.length} {files.length === 1 ? "file" : "files"} from your storage. This action is irreversible. +

+
+ + +
+
+
+ )} + + {/* Footer */} +
+
+
+

FTP

+

asabizanjo.dev

+
+
+
+
+
+
+
+
+
+ ); +} diff --git a/app/globals.css b/app/globals.css index a2dc41e..549c8f9 100644 --- a/app/globals.css +++ b/app/globals.css @@ -1,26 +1,463 @@ @import "tailwindcss"; +/* Satoshi Font Family */ +@font-face { + font-family: 'Satoshi'; + src: url('/Fonts/WEB/fonts/Satoshi-Variable.woff2') format('woff2'), + url('/Fonts/WEB/fonts/Satoshi-Variable.woff') format('woff'), + url('/Fonts/WEB/fonts/Satoshi-Variable.ttf') format('truetype'); + font-weight: 300 900; + font-display: swap; + font-style: normal; +} + +@font-face { + font-family: 'Satoshi'; + src: url('/Fonts/WEB/fonts/Satoshi-VariableItalic.woff2') format('woff2'), + url('/Fonts/WEB/fonts/Satoshi-VariableItalic.woff') format('woff'), + url('/Fonts/WEB/fonts/Satoshi-VariableItalic.ttf') format('truetype'); + font-weight: 300 900; + font-display: swap; + font-style: italic; +} + +/* Neo-Brutalism Dark Theme Variables */ :root { - --background: #ffffff; - --foreground: #171717; + /* Background Colors */ + --bg-dark: #0d0d0d; + --bg-card: #1a1a1a; + --bg-elevated: #252525; + + /* Accent Colors */ + --accent-lime: #BFFF00; + --accent-magenta: #FF00FF; + --accent-cyan: #00FFFF; + --accent-orange: #FF6B00; + + /* Text Colors */ + --text-primary: #FAFAFA; + --text-secondary: #A0A0A0; + --text-muted: #666666; + + /* Danger/Error */ + --danger: #FF3B3B; + --danger-soft: #FF3B3B33; + + /* Border & Shadow */ + --border-width: 3px; + --shadow-offset: 4px; + --shadow-offset-lg: 8px; + + /* Font */ + --font-satoshi: 'Satoshi', system-ui, -apple-system, sans-serif; } @theme inline { - --color-background: var(--background); - --color-foreground: var(--foreground); - --font-sans: var(--font-geist-sans); - --font-mono: var(--font-geist-mono); + --color-bg-dark: var(--bg-dark); + --color-bg-card: var(--bg-card); + --color-bg-elevated: var(--bg-elevated); + --color-accent-lime: var(--accent-lime); + --color-accent-magenta: var(--accent-magenta); + --color-accent-cyan: var(--accent-cyan); + --color-accent-orange: var(--accent-orange); + --color-text-primary: var(--text-primary); + --color-text-secondary: var(--text-secondary); + --color-text-muted: var(--text-muted); + --color-danger: var(--danger); + --font-sans: var(--font-satoshi); } -@media (prefers-color-scheme: dark) { - :root { - --background: #0a0a0a; - --foreground: #ededed; - } +/* Base Styles */ +* { + box-sizing: border-box; +} + +html { + scroll-behavior: smooth; } body { - background: var(--background); - color: var(--foreground); - font-family: Arial, Helvetica, sans-serif; + background: var(--bg-dark); + color: var(--text-primary); + font-family: var(--font-satoshi); + font-weight: 500; + line-height: 1.6; + min-height: 100vh; +} + +/* Grid Background Pattern */ +.bg-grid { + background-image: + linear-gradient(rgba(255, 255, 255, 0.03) 1px, transparent 1px), + linear-gradient(90deg, rgba(255, 255, 255, 0.03) 1px, transparent 1px); + background-size: 40px 40px; +} + +/* Noise Texture Overlay */ +.bg-noise::before { + content: ''; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + opacity: 0.015; + z-index: 100; + pointer-events: none; + background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)'/%3E%3C/svg%3E"); +} + +/* ================================ + BRUTAL COMPONENT UTILITIES + ================================ */ + +/* Brutal Card */ +.brutal-card { + background: var(--bg-card); + border: var(--border-width) solid var(--text-primary); + box-shadow: var(--shadow-offset-lg) var(--shadow-offset-lg) 0 var(--accent-lime); + transition: transform 0.15s ease, box-shadow 0.15s ease; +} + +.brutal-card:hover { + transform: translate(-2px, -2px); + box-shadow: calc(var(--shadow-offset-lg) + 2px) calc(var(--shadow-offset-lg) + 2px) 0 var(--accent-lime); +} + +/* Brutal Card - Magenta variant */ +.brutal-card-magenta { + background: var(--bg-card); + border: var(--border-width) solid var(--text-primary); + box-shadow: var(--shadow-offset-lg) var(--shadow-offset-lg) 0 var(--accent-magenta); +} + +/* Brutal Button - Primary */ +.brutal-btn { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 0.5rem; + padding: 0.75rem 1.5rem; + font-family: var(--font-satoshi); + font-weight: 700; + font-size: 0.875rem; + text-transform: uppercase; + letter-spacing: 0.05em; + color: var(--bg-dark); + background: var(--accent-lime); + border: var(--border-width) solid var(--bg-dark); + box-shadow: var(--shadow-offset) var(--shadow-offset) 0 var(--bg-dark); + cursor: pointer; + transition: transform 0.1s ease, box-shadow 0.1s ease; +} + +.brutal-btn:hover { + transform: translate(-2px, -2px); + box-shadow: calc(var(--shadow-offset) + 2px) calc(var(--shadow-offset) + 2px) 0 var(--bg-dark); +} + +.brutal-btn:active { + transform: translate(2px, 2px); + box-shadow: 0 0 0 var(--bg-dark); +} + +.brutal-btn:disabled { + opacity: 0.5; + cursor: not-allowed; + transform: none; +} + +/* Brutal Button - Outline */ +.brutal-btn-outline { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 0.5rem; + padding: 0.75rem 1.5rem; + font-family: var(--font-satoshi); + font-weight: 700; + font-size: 0.875rem; + text-transform: uppercase; + letter-spacing: 0.05em; + color: var(--text-primary); + background: transparent; + border: var(--border-width) solid var(--accent-lime); + box-shadow: var(--shadow-offset) var(--shadow-offset) 0 var(--accent-lime); + cursor: pointer; + transition: transform 0.1s ease, box-shadow 0.1s ease, background 0.1s ease; +} + +.brutal-btn-outline:hover { + background: var(--accent-lime); + color: var(--bg-dark); + transform: translate(-2px, -2px); + box-shadow: calc(var(--shadow-offset) + 2px) calc(var(--shadow-offset) + 2px) 0 var(--accent-lime); +} + +.brutal-btn-outline:active { + transform: translate(2px, 2px); + box-shadow: 0 0 0 var(--accent-lime); +} + +/* Brutal Button - Danger */ +.brutal-btn-danger { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 0.5rem; + padding: 0.5rem 1rem; + font-family: var(--font-satoshi); + font-weight: 700; + font-size: 0.75rem; + text-transform: uppercase; + letter-spacing: 0.05em; + color: var(--danger); + background: transparent; + border: 2px solid var(--danger); + box-shadow: 3px 3px 0 var(--danger); + cursor: pointer; + transition: transform 0.1s ease, box-shadow 0.1s ease, background 0.1s ease; +} + +.brutal-btn-danger:hover { + background: var(--danger); + color: var(--text-primary); + transform: translate(-1px, -1px); + box-shadow: 4px 4px 0 var(--danger); +} + +/* Brutal Button - Small */ +.brutal-btn-sm { + padding: 0.5rem 1rem; + font-size: 0.75rem; + box-shadow: 3px 3px 0 var(--bg-dark); +} + +/* Brutal Input */ +.brutal-input { + width: 100%; + padding: 0.875rem 1rem; + font-family: var(--font-satoshi); + font-size: 1rem; + font-weight: 500; + color: var(--text-primary); + background: var(--bg-elevated); + border: none; + border-bottom: var(--border-width) solid var(--accent-lime); + outline: none; + transition: border-color 0.15s ease, background 0.15s ease; +} + +.brutal-input::placeholder { + color: var(--text-muted); +} + +.brutal-input:focus { + border-bottom-color: var(--accent-cyan); + background: var(--bg-card); +} + +/* Brutal Label */ +.brutal-label { + display: block; + margin-bottom: 0.5rem; + font-family: var(--font-satoshi); + font-size: 0.75rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.1em; + color: var(--text-secondary); +} + +/* Brutal Link */ +.brutal-link { + color: var(--accent-lime); + font-weight: 700; + text-decoration: none; + border-bottom: 2px solid transparent; + transition: border-color 0.15s ease; +} + +.brutal-link:hover { + border-bottom-color: var(--accent-lime); +} + +/* Brutal Divider */ +.brutal-divider { + height: 3px; + background: linear-gradient(90deg, var(--accent-lime), var(--accent-magenta), var(--accent-cyan)); +} + +/* ================================ + GEOMETRIC DECORATIONS + ================================ */ + +.geo-accent { + position: absolute; + pointer-events: none; +} + +.geo-square { + width: 80px; + height: 80px; + border: 4px solid var(--accent-lime); + transform: rotate(45deg); +} + +.geo-circle { + width: 120px; + height: 120px; + border: 4px solid var(--accent-magenta); + border-radius: 50%; +} + +.geo-line { + width: 200px; + height: 4px; + background: var(--accent-cyan); +} + +/* ================================ + ANIMATIONS + ================================ */ + +@keyframes float { + 0%, 100% { + transform: translateY(0) rotate(45deg); + } + 50% { + transform: translateY(-20px) rotate(45deg); + } +} + +@keyframes pulse-glow { + 0%, 100% { + box-shadow: 0 0 20px var(--accent-lime); + } + 50% { + box-shadow: 0 0 40px var(--accent-lime); + } +} + +@keyframes slide-in-up { + from { + opacity: 0; + transform: translateY(20px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes slide-in-left { + from { + opacity: 0; + transform: translateX(-20px); + } + to { + opacity: 1; + transform: translateX(0); + } +} + +.animate-float { + animation: float 6s ease-in-out infinite; +} + +.animate-slide-up { + animation: slide-in-up 0.4s ease-out forwards; +} + +.animate-slide-left { + animation: slide-in-left 0.4s ease-out forwards; +} + +/* Staggered animation delays */ +.delay-100 { animation-delay: 100ms; } +.delay-200 { animation-delay: 200ms; } +.delay-300 { animation-delay: 300ms; } +.delay-400 { animation-delay: 400ms; } +.delay-500 { animation-delay: 500ms; } + +/* ================================ + TABLE STYLES + ================================ */ + +.brutal-table { + width: 100%; + border-collapse: collapse; +} + +.brutal-table th { + padding: 1rem; + font-size: 0.7rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.1em; + color: var(--text-muted); + text-align: left; + border-bottom: 2px solid var(--text-muted); +} + +.brutal-table td { + padding: 1rem; + font-size: 0.875rem; + border-bottom: 1px solid var(--bg-elevated); +} + +.brutal-table tbody tr { + transition: background 0.15s ease; +} + +.brutal-table tbody tr:hover { + background: var(--bg-elevated); +} + +/* ================================ + SCROLLBAR STYLING + ================================ */ + +::-webkit-scrollbar { + width: 10px; + height: 10px; +} + +::-webkit-scrollbar-track { + background: var(--bg-dark); +} + +::-webkit-scrollbar-thumb { + background: var(--bg-elevated); + border: 2px solid var(--bg-dark); +} + +::-webkit-scrollbar-thumb:hover { + background: var(--text-muted); +} + +/* ================================ + RESPONSIVE UTILITIES + ================================ */ + +@media (max-width: 640px) { + .brutal-card { + box-shadow: var(--shadow-offset) var(--shadow-offset) 0 var(--accent-lime); + } + + .brutal-btn { + width: 100%; + padding: 1rem; + } + + .geo-square, + .geo-circle { + display: none; + } +} + +/* Selection styling */ +::selection { + background: var(--accent-lime); + color: var(--bg-dark); } diff --git a/app/layout.tsx b/app/layout.tsx index f7fa87e..57c548b 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,20 +1,9 @@ import type { Metadata } from "next"; -import { Geist, Geist_Mono } from "next/font/google"; import "./globals.css"; -const geistSans = Geist({ - variable: "--font-geist-sans", - subsets: ["latin"], -}); - -const geistMono = Geist_Mono({ - variable: "--font-geist-mono", - subsets: ["latin"], -}); - export const metadata: Metadata = { - title: "Create Next App", - description: "Generated by create next app", + title: "Asa's FTP", + description: "Upload and download files via Asa's FTP", }; export default function RootLayout({ @@ -24,9 +13,7 @@ export default function RootLayout({ }>) { return ( - + {children} diff --git a/app/page.tsx b/app/page.tsx index 295f8fd..899aa2e 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,65 +1,10 @@ -import Image from "next/image"; +import { redirect } from "next/navigation"; +import { getSessionUser } from "@/lib/auth"; -export default function Home() { - return ( -
-
- Next.js logo -
-

- To get started, edit the page.tsx file. -

-

- Looking for a starting point or more instructions? Head over to{" "} - - Templates - {" "} - or the{" "} - - Learning - {" "} - center. -

-
- -
-
- ); +export default async function Home() { + const session = await getSessionUser(); + if (session) { + redirect("/dashboard"); + } + redirect("/login"); } diff --git a/lib/auth.ts b/lib/auth.ts new file mode 100644 index 0000000..44a4b4a --- /dev/null +++ b/lib/auth.ts @@ -0,0 +1,80 @@ +import { cookies } from "next/headers"; +import { SignJWT, jwtVerify } from "jose"; +import bcrypt from "bcryptjs"; +import { prisma } from "./db"; + +const SESSION_COOKIE_NAME = "ftp_session"; +const SESSION_MAX_AGE = 60 * 60 * 24 * 7; // 7 days + +type SessionPayload = { + userId: string; + email: string; +}; + +function getJwtSecret() { + const secret = process.env.JWT_SECRET; + if (!secret) { + throw new Error("JWT_SECRET is not set"); + } + return new TextEncoder().encode(secret); +} + +export async function hashPassword(password: string) { + return bcrypt.hash(password, 10); +} + +export async function verifyPassword(password: string, hash: string) { + return bcrypt.compare(password, hash); +} + +export async function createSessionToken(payload: SessionPayload) { + const secret = getJwtSecret(); + return new SignJWT(payload) + .setProtectedHeader({ alg: "HS256" }) + .setIssuedAt() + .setExpirationTime(`${SESSION_MAX_AGE}s`) + .sign(secret); +} + +export async function verifySessionToken(token: string) { + try { + const { payload } = await jwtVerify(token, getJwtSecret()); + return payload; + } catch { + return null; + } +} + +export async function setSessionCookie(token: string) { + const store = await cookies(); + store.set(SESSION_COOKIE_NAME, token, { + httpOnly: true, + sameSite: "lax", + secure: process.env.NODE_ENV === "production", + path: "/", + maxAge: SESSION_MAX_AGE, + }); +} + +export async function clearSessionCookie() { + const store = await cookies(); + store.delete(SESSION_COOKIE_NAME); +} + +export async function getSessionUser() { + const store = await cookies(); + const token = store.get(SESSION_COOKIE_NAME)?.value; + if (!token) return null; + const payload = await verifySessionToken(token); + if (!payload) return null; + return payload; +} + +export async function getCurrentUserRecord() { + const session = await getSessionUser(); + if (!session) return null; + return prisma.user.findUnique({ where: { id: session.userId } }); +} + +export { SESSION_COOKIE_NAME, SESSION_MAX_AGE }; + diff --git a/lib/db.ts b/lib/db.ts new file mode 100644 index 0000000..3eeb325 --- /dev/null +++ b/lib/db.ts @@ -0,0 +1,16 @@ +import { PrismaClient } from "@prisma/client"; + +const globalForPrisma = globalThis as unknown as { + prisma?: PrismaClient; +}; + +export const prisma = + globalForPrisma.prisma ?? + new PrismaClient({ + log: process.env.NODE_ENV === "development" ? ["query", "error"] : ["error"], + }); + +if (process.env.NODE_ENV !== "production") { + globalForPrisma.prisma = prisma; +} + diff --git a/lib/r2.ts b/lib/r2.ts new file mode 100644 index 0000000..b779914 --- /dev/null +++ b/lib/r2.ts @@ -0,0 +1,74 @@ +import { + S3Client, + PutObjectCommand, + DeleteObjectCommand, + GetObjectCommand, +} from "@aws-sdk/client-s3"; +import { getSignedUrl } from "@aws-sdk/s3-request-presigner"; + +let cachedClient: S3Client | null = null; +let cachedBucket: string | null = null; + +function getClient() { + if (cachedClient && cachedBucket) { + return { s3: cachedClient, bucket: cachedBucket }; + } + + const { + R2_ENDPOINT, + R2_REGION = "auto", + R2_ACCESS_KEY_ID, + R2_SECRET_ACCESS_KEY, + R2_BUCKET, + } = process.env; + + if (!R2_ENDPOINT || !R2_ACCESS_KEY_ID || !R2_SECRET_ACCESS_KEY || !R2_BUCKET) { + throw new Error("R2 configuration is missing. Check env variables."); + } + + cachedBucket = R2_BUCKET; + cachedClient = new S3Client({ + endpoint: R2_ENDPOINT, + region: R2_REGION, + credentials: { + accessKeyId: R2_ACCESS_KEY_ID, + secretAccessKey: R2_SECRET_ACCESS_KEY, + }, + }); + + return { s3: cachedClient, bucket: cachedBucket }; +} + +export async function uploadToR2(params: { + key: string; + contentType: string; + body: Buffer; +}) { + const { s3, bucket } = getClient(); + const command = new PutObjectCommand({ + Bucket: bucket, + Key: params.key, + Body: params.body, + ContentType: params.contentType, + }); + await s3.send(command); +} + +export async function getSignedDownloadUrl(key: string, expiresInSeconds = 300) { + const { s3, bucket } = getClient(); + const command = new GetObjectCommand({ + Bucket: bucket, + Key: key, + }); + return getSignedUrl(s3, command, { expiresIn: expiresInSeconds }); +} + +export async function deleteFromR2(key: string) { + const { s3, bucket } = getClient(); + const command = new DeleteObjectCommand({ + Bucket: bucket, + Key: key, + }); + await s3.send(command); +} + diff --git a/middleware.ts b/middleware.ts new file mode 100644 index 0000000..981bf42 --- /dev/null +++ b/middleware.ts @@ -0,0 +1,20 @@ +import { NextResponse } from "next/server"; +import type { NextRequest } from "next/server"; +import { SESSION_COOKIE_NAME, verifySessionToken } from "./lib/auth"; + +export async function middleware(req: NextRequest) { + const token = req.cookies.get(SESSION_COOKIE_NAME)?.value; + const session = token ? await verifySessionToken(token) : null; + + if (!session) { + const loginUrl = new URL("/login", req.url); + return NextResponse.redirect(loginUrl); + } + + return NextResponse.next(); +} + +export const config = { + matcher: ["/dashboard/:path*", "/api/files/:path*"], +}; + diff --git a/next.config.ts b/next.config.ts index e9ffa30..3c9fc54 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,7 +1,18 @@ import type { NextConfig } from "next"; const nextConfig: NextConfig = { - /* config options here */ + // Disable server/dev sourcemaps to avoid Windows source map parse warnings + productionBrowserSourceMaps: false, + + webpack: (config, { dev, isServer }) => { + if (dev && isServer) { + config.devtool = false; + } + return config; + }, + + // Acknowledge Turbopack config to silence migration warning + turbopack: {}, }; export default nextConfig; diff --git a/package-lock.json b/package-lock.json index 8eceadb..e4e08ec 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,13 @@ "name": "ftp", "version": "0.1.0", "dependencies": { + "@aws-sdk/client-s3": "^3.948.0", + "@aws-sdk/s3-request-presigner": "^3.948.0", + "@prisma/client": "^5.19.1", + "bcryptjs": "^3.0.3", + "jose": "^6.1.3", "next": "16.0.8", + "prisma": "^5.19.1", "react": "19.2.1", "react-dom": "19.2.1" }, @@ -36,6 +42,924 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@aws-crypto/crc32": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz", + "integrity": "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-crypto/crc32c": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32c/-/crc32c-5.2.0.tgz", + "integrity": "sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha1-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha1-browser/-/sha1-browser-5.2.0.tgz", + "integrity": "sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", + "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-js": "^5.2.0", + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", + "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-crypto/supports-web-crypto": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", + "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", + "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3": { + "version": "3.948.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.948.0.tgz", + "integrity": "sha512-uvEjds8aYA9SzhBS8RKDtsDUhNV9VhqKiHTcmvhM7gJO92q0WTn8/QeFTdNyLc6RxpiDyz+uBxS7PcdNiZzqfA==", + "license": "Apache-2.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.947.0", + "@aws-sdk/credential-provider-node": "3.948.0", + "@aws-sdk/middleware-bucket-endpoint": "3.936.0", + "@aws-sdk/middleware-expect-continue": "3.936.0", + "@aws-sdk/middleware-flexible-checksums": "3.947.0", + "@aws-sdk/middleware-host-header": "3.936.0", + "@aws-sdk/middleware-location-constraint": "3.936.0", + "@aws-sdk/middleware-logger": "3.936.0", + "@aws-sdk/middleware-recursion-detection": "3.948.0", + "@aws-sdk/middleware-sdk-s3": "3.947.0", + "@aws-sdk/middleware-ssec": "3.936.0", + "@aws-sdk/middleware-user-agent": "3.947.0", + "@aws-sdk/region-config-resolver": "3.936.0", + "@aws-sdk/signature-v4-multi-region": "3.947.0", + "@aws-sdk/types": "3.936.0", + "@aws-sdk/util-endpoints": "3.936.0", + "@aws-sdk/util-user-agent-browser": "3.936.0", + "@aws-sdk/util-user-agent-node": "3.947.0", + "@smithy/config-resolver": "^4.4.3", + "@smithy/core": "^3.18.7", + "@smithy/eventstream-serde-browser": "^4.2.5", + "@smithy/eventstream-serde-config-resolver": "^4.3.5", + "@smithy/eventstream-serde-node": "^4.2.5", + "@smithy/fetch-http-handler": "^5.3.6", + "@smithy/hash-blob-browser": "^4.2.6", + "@smithy/hash-node": "^4.2.5", + "@smithy/hash-stream-node": "^4.2.5", + "@smithy/invalid-dependency": "^4.2.5", + "@smithy/md5-js": "^4.2.5", + "@smithy/middleware-content-length": "^4.2.5", + "@smithy/middleware-endpoint": "^4.3.14", + "@smithy/middleware-retry": "^4.4.14", + "@smithy/middleware-serde": "^4.2.6", + "@smithy/middleware-stack": "^4.2.5", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/node-http-handler": "^4.4.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", + "@smithy/url-parser": "^4.2.5", + "@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.13", + "@smithy/util-defaults-mode-node": "^4.2.16", + "@smithy/util-endpoints": "^3.2.5", + "@smithy/util-middleware": "^4.2.5", + "@smithy/util-retry": "^4.2.5", + "@smithy/util-stream": "^4.5.6", + "@smithy/util-utf8": "^4.2.0", + "@smithy/util-waiter": "^4.2.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-sso": { + "version": "3.948.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.948.0.tgz", + "integrity": "sha512-iWjchXy8bIAVBUsKnbfKYXRwhLgRg3EqCQ5FTr3JbR+QR75rZm4ZOYXlvHGztVTmtAZ+PQVA1Y4zO7v7N87C0A==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.947.0", + "@aws-sdk/middleware-host-header": "3.936.0", + "@aws-sdk/middleware-logger": "3.936.0", + "@aws-sdk/middleware-recursion-detection": "3.948.0", + "@aws-sdk/middleware-user-agent": "3.947.0", + "@aws-sdk/region-config-resolver": "3.936.0", + "@aws-sdk/types": "3.936.0", + "@aws-sdk/util-endpoints": "3.936.0", + "@aws-sdk/util-user-agent-browser": "3.936.0", + "@aws-sdk/util-user-agent-node": "3.947.0", + "@smithy/config-resolver": "^4.4.3", + "@smithy/core": "^3.18.7", + "@smithy/fetch-http-handler": "^5.3.6", + "@smithy/hash-node": "^4.2.5", + "@smithy/invalid-dependency": "^4.2.5", + "@smithy/middleware-content-length": "^4.2.5", + "@smithy/middleware-endpoint": "^4.3.14", + "@smithy/middleware-retry": "^4.4.14", + "@smithy/middleware-serde": "^4.2.6", + "@smithy/middleware-stack": "^4.2.5", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/node-http-handler": "^4.4.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", + "@smithy/url-parser": "^4.2.5", + "@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.13", + "@smithy/util-defaults-mode-node": "^4.2.16", + "@smithy/util-endpoints": "^3.2.5", + "@smithy/util-middleware": "^4.2.5", + "@smithy/util-retry": "^4.2.5", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/core": { + "version": "3.947.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.947.0.tgz", + "integrity": "sha512-Khq4zHhuAkvCFuFbgcy3GrZTzfSX7ZIjIcW1zRDxXRLZKRtuhnZdonqTUfaWi5K42/4OmxkYNpsO7X7trQOeHw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@aws-sdk/xml-builder": "3.930.0", + "@smithy/core": "^3.18.7", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/property-provider": "^4.2.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/signature-v4": "^5.3.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-middleware": "^4.2.5", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.947.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.947.0.tgz", + "integrity": "sha512-VR2V6dRELmzwAsCpK4GqxUi6UW5WNhAXS9F9AzWi5jvijwJo3nH92YNJUP4quMpgFZxJHEWyXLWgPjh9u0zYOA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.947.0", + "@aws-sdk/types": "3.936.0", + "@smithy/property-provider": "^4.2.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-http": { + "version": "3.947.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.947.0.tgz", + "integrity": "sha512-inF09lh9SlHj63Vmr5d+LmwPXZc2IbK8lAruhOr3KLsZAIHEgHgGPXWDC2ukTEMzg0pkexQ6FOhXXad6klK4RA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.947.0", + "@aws-sdk/types": "3.936.0", + "@smithy/fetch-http-handler": "^5.3.6", + "@smithy/node-http-handler": "^4.4.5", + "@smithy/property-provider": "^4.2.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", + "@smithy/util-stream": "^4.5.6", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.948.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.948.0.tgz", + "integrity": "sha512-Cl//Qh88e8HBL7yYkJNpF5eq76IO6rq8GsatKcfVBm7RFVxCqYEPSSBtkHdbtNwQdRQqAMXc6E/lEB/CZUDxnA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.947.0", + "@aws-sdk/credential-provider-env": "3.947.0", + "@aws-sdk/credential-provider-http": "3.947.0", + "@aws-sdk/credential-provider-login": "3.948.0", + "@aws-sdk/credential-provider-process": "3.947.0", + "@aws-sdk/credential-provider-sso": "3.948.0", + "@aws-sdk/credential-provider-web-identity": "3.948.0", + "@aws-sdk/nested-clients": "3.948.0", + "@aws-sdk/types": "3.936.0", + "@smithy/credential-provider-imds": "^4.2.5", + "@smithy/property-provider": "^4.2.5", + "@smithy/shared-ini-file-loader": "^4.4.0", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-login": { + "version": "3.948.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.948.0.tgz", + "integrity": "sha512-gcKO2b6eeTuZGp3Vvgr/9OxajMrD3W+FZ2FCyJox363ZgMoYJsyNid1vuZrEuAGkx0jvveLXfwiVS0UXyPkgtw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.947.0", + "@aws-sdk/nested-clients": "3.948.0", + "@aws-sdk/types": "3.936.0", + "@smithy/property-provider": "^4.2.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/shared-ini-file-loader": "^4.4.0", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-node": { + "version": "3.948.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.948.0.tgz", + "integrity": "sha512-ep5vRLnrRdcsP17Ef31sNN4g8Nqk/4JBydcUJuFRbGuyQtrZZrVT81UeH2xhz6d0BK6ejafDB9+ZpBjXuWT5/Q==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.947.0", + "@aws-sdk/credential-provider-http": "3.947.0", + "@aws-sdk/credential-provider-ini": "3.948.0", + "@aws-sdk/credential-provider-process": "3.947.0", + "@aws-sdk/credential-provider-sso": "3.948.0", + "@aws-sdk/credential-provider-web-identity": "3.948.0", + "@aws-sdk/types": "3.936.0", + "@smithy/credential-provider-imds": "^4.2.5", + "@smithy/property-provider": "^4.2.5", + "@smithy/shared-ini-file-loader": "^4.4.0", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-process": { + "version": "3.947.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.947.0.tgz", + "integrity": "sha512-WpanFbHe08SP1hAJNeDdBDVz9SGgMu/gc0XJ9u3uNpW99nKZjDpvPRAdW7WLA4K6essMjxWkguIGNOpij6Do2Q==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.947.0", + "@aws-sdk/types": "3.936.0", + "@smithy/property-provider": "^4.2.5", + "@smithy/shared-ini-file-loader": "^4.4.0", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.948.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.948.0.tgz", + "integrity": "sha512-gqLhX1L+zb/ZDnnYbILQqJ46j735StfWV5PbDjxRzBKS7GzsiYoaf6MyHseEopmWrez5zl5l6aWzig7UpzSeQQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.948.0", + "@aws-sdk/core": "3.947.0", + "@aws-sdk/token-providers": "3.948.0", + "@aws-sdk/types": "3.936.0", + "@smithy/property-provider": "^4.2.5", + "@smithy/shared-ini-file-loader": "^4.4.0", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.948.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.948.0.tgz", + "integrity": "sha512-MvYQlXVoJyfF3/SmnNzOVEtANRAiJIObEUYYyjTqKZTmcRIVVky0tPuG26XnB8LmTYgtESwJIZJj/Eyyc9WURQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.947.0", + "@aws-sdk/nested-clients": "3.948.0", + "@aws-sdk/types": "3.936.0", + "@smithy/property-provider": "^4.2.5", + "@smithy/shared-ini-file-loader": "^4.4.0", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-bucket-endpoint": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.936.0.tgz", + "integrity": "sha512-XLSVVfAorUxZh6dzF+HTOp4R1B5EQcdpGcPliWr0KUj2jukgjZEcqbBmjyMF/p9bmyQsONX80iURF1HLAlW0qg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@aws-sdk/util-arn-parser": "3.893.0", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", + "@smithy/util-config-provider": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-expect-continue": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.936.0.tgz", + "integrity": "sha512-Eb4ELAC23bEQLJmUMYnPWcjD3FZIsmz2svDiXEcxRkQU9r7NRID7pM7C5NPH94wOfiCk0b2Y8rVyFXW0lGQwbA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-flexible-checksums": { + "version": "3.947.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.947.0.tgz", + "integrity": "sha512-kXXxS2raNESNO+zR0L4YInVjhcGGNI2Mx0AE1ThRhDkAt2se3a+rGf9equ9YvOqA1m8Jl/GSI8cXYvSxXmS9Ag==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@aws-crypto/crc32c": "5.2.0", + "@aws-crypto/util": "5.2.0", + "@aws-sdk/core": "3.947.0", + "@aws-sdk/types": "3.936.0", + "@smithy/is-array-buffer": "^4.2.0", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", + "@smithy/util-middleware": "^4.2.5", + "@smithy/util-stream": "^4.5.6", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-host-header": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.936.0.tgz", + "integrity": "sha512-tAaObaAnsP1XnLGndfkGWFuzrJYuk9W0b/nLvol66t8FZExIAf/WdkT2NNAWOYxljVs++oHnyHBCxIlaHrzSiw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-location-constraint": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.936.0.tgz", + "integrity": "sha512-SCMPenDtQMd9o5da9JzkHz838w3327iqXk3cbNnXWqnNRx6unyW8FL0DZ84gIY12kAyVHz5WEqlWuekc15ehfw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-logger": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.936.0.tgz", + "integrity": "sha512-aPSJ12d3a3Ea5nyEnLbijCaaYJT2QjQ9iW+zGh5QcZYXmOGWbKVyPSxmVOboZQG+c1M8t6d2O7tqrwzIq8L8qw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.948.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.948.0.tgz", + "integrity": "sha512-Qa8Zj+EAqA0VlAVvxpRnpBpIWJI9KUwaioY1vkeNVwXPlNaz9y9zCKVM9iU9OZ5HXpoUg6TnhATAHXHAE8+QsQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@aws/lambda-invoke-store": "^0.2.2", + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-s3": { + "version": "3.947.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.947.0.tgz", + "integrity": "sha512-DS2tm5YBKhPW2PthrRBDr6eufChbwXe0NjtTZcYDfUCXf0OR+W6cIqyKguwHMJ+IyYdey30AfVw9/Lb5KB8U8A==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.947.0", + "@aws-sdk/types": "3.936.0", + "@aws-sdk/util-arn-parser": "3.893.0", + "@smithy/core": "^3.18.7", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/signature-v4": "^5.3.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", + "@smithy/util-config-provider": "^4.2.0", + "@smithy/util-middleware": "^4.2.5", + "@smithy/util-stream": "^4.5.6", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-ssec": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.936.0.tgz", + "integrity": "sha512-/GLC9lZdVp05ozRik5KsuODR/N7j+W+2TbfdFL3iS+7un+gnP6hC8RDOZd6WhpZp7drXQ9guKiTAxkZQwzS8DA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.947.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.947.0.tgz", + "integrity": "sha512-7rpKV8YNgCP2R4F9RjWZFcD2R+SO/0R4VHIbY9iZJdH2MzzJ8ZG7h8dZ2m8QkQd1fjx4wrFJGGPJUTYXPV3baA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.947.0", + "@aws-sdk/types": "3.936.0", + "@aws-sdk/util-endpoints": "3.936.0", + "@smithy/core": "^3.18.7", + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients": { + "version": "3.948.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.948.0.tgz", + "integrity": "sha512-zcbJfBsB6h254o3NuoEkf0+UY1GpE9ioiQdENWv7odo69s8iaGBEQ4BDpsIMqcuiiUXw1uKIVNxCB1gUGYz8lw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.947.0", + "@aws-sdk/middleware-host-header": "3.936.0", + "@aws-sdk/middleware-logger": "3.936.0", + "@aws-sdk/middleware-recursion-detection": "3.948.0", + "@aws-sdk/middleware-user-agent": "3.947.0", + "@aws-sdk/region-config-resolver": "3.936.0", + "@aws-sdk/types": "3.936.0", + "@aws-sdk/util-endpoints": "3.936.0", + "@aws-sdk/util-user-agent-browser": "3.936.0", + "@aws-sdk/util-user-agent-node": "3.947.0", + "@smithy/config-resolver": "^4.4.3", + "@smithy/core": "^3.18.7", + "@smithy/fetch-http-handler": "^5.3.6", + "@smithy/hash-node": "^4.2.5", + "@smithy/invalid-dependency": "^4.2.5", + "@smithy/middleware-content-length": "^4.2.5", + "@smithy/middleware-endpoint": "^4.3.14", + "@smithy/middleware-retry": "^4.4.14", + "@smithy/middleware-serde": "^4.2.6", + "@smithy/middleware-stack": "^4.2.5", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/node-http-handler": "^4.4.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", + "@smithy/url-parser": "^4.2.5", + "@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.13", + "@smithy/util-defaults-mode-node": "^4.2.16", + "@smithy/util-endpoints": "^3.2.5", + "@smithy/util-middleware": "^4.2.5", + "@smithy/util-retry": "^4.2.5", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/region-config-resolver": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.936.0.tgz", + "integrity": "sha512-wOKhzzWsshXGduxO4pqSiNyL9oUtk4BEvjWm9aaq6Hmfdoydq6v6t0rAGHWPjFwy9z2haovGRi3C8IxdMB4muw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@smithy/config-resolver": "^4.4.3", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/s3-request-presigner": { + "version": "3.948.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/s3-request-presigner/-/s3-request-presigner-3.948.0.tgz", + "integrity": "sha512-tlQhMDsDWwUDUzzlo8XYGpmMfjGDmXgbysv1+h1v2xJe0A+ogv/nJ6KFVP94uf1j4ePmCN/gDdxEp2PWZMBPOQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/signature-v4-multi-region": "3.947.0", + "@aws-sdk/types": "3.936.0", + "@aws-sdk/util-format-url": "3.936.0", + "@smithy/middleware-endpoint": "^4.3.14", + "@smithy/protocol-http": "^5.3.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4-multi-region": { + "version": "3.947.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.947.0.tgz", + "integrity": "sha512-UaYmzoxf9q3mabIA2hc4T6x5YSFUG2BpNjAZ207EA1bnQMiK+d6vZvb83t7dIWL/U1de1sGV19c1C81Jf14rrA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-sdk-s3": "3.947.0", + "@aws-sdk/types": "3.936.0", + "@smithy/protocol-http": "^5.3.5", + "@smithy/signature-v4": "^5.3.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/token-providers": { + "version": "3.948.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.948.0.tgz", + "integrity": "sha512-V487/kM4Teq5dcr1t5K6eoUKuqlGr9FRWL3MIMukMERJXHZvio6kox60FZ/YtciRHRI75u14YUqm2Dzddcu3+A==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.947.0", + "@aws-sdk/nested-clients": "3.948.0", + "@aws-sdk/types": "3.936.0", + "@smithy/property-provider": "^4.2.5", + "@smithy/shared-ini-file-loader": "^4.4.0", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/types": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.936.0.tgz", + "integrity": "sha512-uz0/VlMd2pP5MepdrHizd+T+OKfyK4r3OA9JI+L/lPKg0YFQosdJNCKisr6o70E3dh8iMpFYxF1UN/4uZsyARg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/util-arn-parser": { + "version": "3.893.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.893.0.tgz", + "integrity": "sha512-u8H4f2Zsi19DGnwj5FSZzDMhytYF/bCh37vAtBsn3cNDL3YG578X5oc+wSX54pM3tOxS+NY7tvOAo52SW7koUA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/util-endpoints": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.936.0.tgz", + "integrity": "sha512-0Zx3Ntdpu+z9Wlm7JKUBOzS9EunwKAb4KdGUQQxDqh5Lc3ta5uBoub+FgmVuzwnmBu9U1Os8UuwVTH0Lgu+P5w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@smithy/types": "^4.9.0", + "@smithy/url-parser": "^4.2.5", + "@smithy/util-endpoints": "^3.2.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/util-format-url": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-format-url/-/util-format-url-3.936.0.tgz", + "integrity": "sha512-MS5eSEtDUFIAMHrJaMERiHAvDPdfxc/T869ZjDNFAIiZhyc037REw0aoTNeimNXDNy2txRNZJaAUn/kE4RwN+g==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@smithy/querystring-builder": "^4.2.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/util-locate-window": { + "version": "3.893.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.893.0.tgz", + "integrity": "sha512-T89pFfgat6c8nMmpI8eKjBcDcgJq36+m9oiXbcUzeU55MP9ZuGgBomGjGnHaEyF36jenW9gmg3NfZDm0AO2XPg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.936.0.tgz", + "integrity": "sha512-eZ/XF6NxMtu+iCma58GRNRxSq4lHo6zHQLOZRIeL/ghqYJirqHdenMOwrzPettj60KWlv827RVebP9oNVrwZbw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@smithy/types": "^4.9.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.947.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.947.0.tgz", + "integrity": "sha512-+vhHoDrdbb+zerV4noQk1DHaUMNzWFWPpPYjVTwW2186k5BEJIecAMChYkghRrBVJ3KPWP1+JnZwOd72F3d4rQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.947.0", + "@aws-sdk/types": "3.936.0", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/xml-builder": { + "version": "3.930.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.930.0.tgz", + "integrity": "sha512-YIfkD17GocxdmlUVc3ia52QhcWuRIUJonbF8A2CYfcWNV3HzvAqpcPeC0bYUhkK+8e8YO1ARnLKZQE0TlwzorA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.9.0", + "fast-xml-parser": "5.2.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws/lambda-invoke-store": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.2.2.tgz", + "integrity": "sha512-C0NBLsIqzDIae8HFw9YIrIBsbc0xTiOtt7fAukGPnqQ/+zZNaq+4jhuccltK0QuWHBnNm/a6kLIRA6GFiM10eg==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@babel/code-frame": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", @@ -1227,6 +2151,69 @@ "node": ">=12.4.0" } }, + "node_modules/@prisma/client": { + "version": "5.19.1", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.19.1.tgz", + "integrity": "sha512-x30GFguInsgt+4z5I4WbkZP2CGpotJMUXy+Gl/aaUjHn2o1DnLYNTA+q9XdYmAQZM8fIIkvUiA2NpgosM3fneg==", + "hasInstallScript": true, + "license": "Apache-2.0", + "engines": { + "node": ">=16.13" + }, + "peerDependencies": { + "prisma": "*" + }, + "peerDependenciesMeta": { + "prisma": { + "optional": true + } + } + }, + "node_modules/@prisma/debug": { + "version": "5.19.1", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.19.1.tgz", + "integrity": "sha512-lAG6A6QnG2AskAukIEucYJZxxcSqKsMK74ZFVfCTOM/7UiyJQi48v6TQ47d6qKG3LbMslqOvnTX25dj/qvclGg==", + "license": "Apache-2.0" + }, + "node_modules/@prisma/engines": { + "version": "5.19.1", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.19.1.tgz", + "integrity": "sha512-kR/PoxZDrfUmbbXqqb8SlBBgCjvGaJYMCOe189PEYzq9rKqitQ2fvT/VJ8PDSe8tTNxhc2KzsCfCAL+Iwm/7Cg==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "5.19.1", + "@prisma/engines-version": "5.19.1-2.69d742ee20b815d88e17e54db4a2a7a3b30324e3", + "@prisma/fetch-engine": "5.19.1", + "@prisma/get-platform": "5.19.1" + } + }, + "node_modules/@prisma/engines-version": { + "version": "5.19.1-2.69d742ee20b815d88e17e54db4a2a7a3b30324e3", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.19.1-2.69d742ee20b815d88e17e54db4a2a7a3b30324e3.tgz", + "integrity": "sha512-xR6rt+z5LnNqTP5BBc+8+ySgf4WNMimOKXRn6xfNRDSpHvbOEmd7+qAOmzCrddEc4Cp8nFC0txU14dstjH7FXA==", + "license": "Apache-2.0" + }, + "node_modules/@prisma/fetch-engine": { + "version": "5.19.1", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-5.19.1.tgz", + "integrity": "sha512-pCq74rtlOVJfn4pLmdJj+eI4P7w2dugOnnTXpRilP/6n5b2aZiA4ulJlE0ddCbTPkfHmOL9BfaRgA8o+1rfdHw==", + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "5.19.1", + "@prisma/engines-version": "5.19.1-2.69d742ee20b815d88e17e54db4a2a7a3b30324e3", + "@prisma/get-platform": "5.19.1" + } + }, + "node_modules/@prisma/get-platform": { + "version": "5.19.1", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-5.19.1.tgz", + "integrity": "sha512-sCeoJ+7yt0UjnR+AXZL7vXlg5eNxaFOwC23h0KvW1YIXUoa7+W2ZcAUhoEQBmJTW4GrFqCuZ8YSP0mkDa4k3Zg==", + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "5.19.1" + } + }, "node_modules/@rtsao/scc": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", @@ -1234,6 +2221,738 @@ "dev": true, "license": "MIT" }, + "node_modules/@smithy/abort-controller": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.2.5.tgz", + "integrity": "sha512-j7HwVkBw68YW8UmFRcjZOmssE77Rvk0GWAIN1oFBhsaovQmZWYCIcGa9/pwRB0ExI8Sk9MWNALTjftjHZea7VA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/chunked-blob-reader": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader/-/chunked-blob-reader-5.2.0.tgz", + "integrity": "sha512-WmU0TnhEAJLWvfSeMxBNe5xtbselEO8+4wG0NtZeL8oR21WgH1xiO37El+/Y+H/Ie4SCwBy3MxYWmOYaGgZueA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/chunked-blob-reader-native": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader-native/-/chunked-blob-reader-native-4.2.1.tgz", + "integrity": "sha512-lX9Ay+6LisTfpLid2zZtIhSEjHMZoAR5hHCR4H7tBz/Zkfr5ea8RcQ7Tk4mi0P76p4cN+Btz16Ffno7YHpKXnQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-base64": "^4.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/config-resolver": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.4.3.tgz", + "integrity": "sha512-ezHLe1tKLUxDJo2LHtDuEDyWXolw8WGOR92qb4bQdWq/zKenO5BvctZGrVJBK08zjezSk7bmbKFOXIVyChvDLw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.3.5", + "@smithy/types": "^4.9.0", + "@smithy/util-config-provider": "^4.2.0", + "@smithy/util-endpoints": "^3.2.5", + "@smithy/util-middleware": "^4.2.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/core": { + "version": "3.18.7", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.18.7.tgz", + "integrity": "sha512-axG9MvKhMWOhFbvf5y2DuyTxQueO0dkedY9QC3mAfndLosRI/9LJv8WaL0mw7ubNhsO4IuXX9/9dYGPFvHrqlw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/middleware-serde": "^4.2.6", + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-middleware": "^4.2.5", + "@smithy/util-stream": "^4.5.6", + "@smithy/util-utf8": "^4.2.0", + "@smithy/uuid": "^1.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/credential-provider-imds": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.5.tgz", + "integrity": "sha512-BZwotjoZWn9+36nimwm/OLIcVe+KYRwzMjfhd4QT7QxPm9WY0HiOV8t/Wlh+HVUif0SBVV7ksq8//hPaBC/okQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.3.5", + "@smithy/property-provider": "^4.2.5", + "@smithy/types": "^4.9.0", + "@smithy/url-parser": "^4.2.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-codec": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-4.2.5.tgz", + "integrity": "sha512-Ogt4Zi9hEbIP17oQMd68qYOHUzmH47UkK7q7Gl55iIm9oKt27MUGrC5JfpMroeHjdkOliOA4Qt3NQ1xMq/nrlA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@smithy/types": "^4.9.0", + "@smithy/util-hex-encoding": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-browser": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.2.5.tgz", + "integrity": "sha512-HohfmCQZjppVnKX2PnXlf47CW3j92Ki6T/vkAT2DhBR47e89pen3s4fIa7otGTtrVxmj7q+IhH0RnC5kpR8wtw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-serde-universal": "^4.2.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-config-resolver": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.3.5.tgz", + "integrity": "sha512-ibjQjM7wEXtECiT6my1xfiMH9IcEczMOS6xiCQXoUIYSj5b1CpBbJ3VYbdwDy8Vcg5JHN7eFpOCGk8nyZAltNQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-node": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.2.5.tgz", + "integrity": "sha512-+elOuaYx6F2H6x1/5BQP5ugv12nfJl66GhxON8+dWVUEDJ9jah/A0tayVdkLRP0AeSac0inYkDz5qBFKfVp2Gg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-serde-universal": "^4.2.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-universal": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-4.2.5.tgz", + "integrity": "sha512-G9WSqbST45bmIFaeNuP/EnC19Rhp54CcVdX9PDL1zyEB514WsDVXhlyihKlGXnRycmHNmVv88Bvvt4EYxWef/Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-codec": "^4.2.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/fetch-http-handler": { + "version": "5.3.6", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.6.tgz", + "integrity": "sha512-3+RG3EA6BBJ/ofZUeTFJA7mHfSYrZtQIrDP9dI8Lf7X6Jbos2jptuLrAAteDiFVrmbEmLSuRG/bUKzfAXk7dhg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.3.5", + "@smithy/querystring-builder": "^4.2.5", + "@smithy/types": "^4.9.0", + "@smithy/util-base64": "^4.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/hash-blob-browser": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-4.2.6.tgz", + "integrity": "sha512-8P//tA8DVPk+3XURk2rwcKgYwFvwGwmJH/wJqQiSKwXZtf/LiZK+hbUZmPj/9KzM+OVSwe4o85KTp5x9DUZTjw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/chunked-blob-reader": "^5.2.0", + "@smithy/chunked-blob-reader-native": "^4.2.1", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/hash-node": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.2.5.tgz", + "integrity": "sha512-DpYX914YOfA3UDT9CN1BM787PcHfWRBB43fFGCYrZFUH0Jv+5t8yYl+Pd5PW4+QzoGEDvn5d5QIO4j2HyYZQSA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.9.0", + "@smithy/util-buffer-from": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/hash-stream-node": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/hash-stream-node/-/hash-stream-node-4.2.5.tgz", + "integrity": "sha512-6+do24VnEyvWcGdHXomlpd0m8bfZePpUKBy7m311n+JuRwug8J4dCanJdTymx//8mi0nlkflZBvJe+dEO/O12Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.9.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/invalid-dependency": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.2.5.tgz", + "integrity": "sha512-2L2erASEro1WC5nV+plwIMxrTXpvpfzl4e+Nre6vBVRR2HKeGGcvpJyyL3/PpiSg+cJG2KpTmZmq934Olb6e5A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/is-array-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.2.0.tgz", + "integrity": "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/md5-js": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-4.2.5.tgz", + "integrity": "sha512-Bt6jpSTMWfjCtC0s79gZ/WZ1w90grfmopVOWqkI2ovhjpD5Q2XRXuecIPB9689L2+cCySMbaXDhBPU56FKNDNg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.9.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-content-length": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.2.5.tgz", + "integrity": "sha512-Y/RabVa5vbl5FuHYV2vUCwvh/dqzrEY/K2yWPSqvhFUwIY0atLqO4TienjBXakoy4zrKAMCZwg+YEqmH7jaN7A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-endpoint": { + "version": "4.3.14", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.3.14.tgz", + "integrity": "sha512-v0q4uTKgBM8dsqGjqsabZQyH85nFaTnFcgpWU1uydKFsdyyMzfvOkNum9G7VK+dOP01vUnoZxIeRiJ6uD0kjIg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.18.7", + "@smithy/middleware-serde": "^4.2.6", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/shared-ini-file-loader": "^4.4.0", + "@smithy/types": "^4.9.0", + "@smithy/url-parser": "^4.2.5", + "@smithy/util-middleware": "^4.2.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-retry": { + "version": "4.4.14", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.14.tgz", + "integrity": "sha512-Z2DG8Ej7FyWG1UA+7HceINtSLzswUgs2np3sZX0YBBxCt+CXG4QUxv88ZDS3+2/1ldW7LqtSY1UO/6VQ1pND8Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.3.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/service-error-classification": "^4.2.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", + "@smithy/util-middleware": "^4.2.5", + "@smithy/util-retry": "^4.2.5", + "@smithy/uuid": "^1.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-serde": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.6.tgz", + "integrity": "sha512-VkLoE/z7e2g8pirwisLz8XJWedUSY8my/qrp81VmAdyrhi94T+riBfwP+AOEEFR9rFTSonC/5D2eWNmFabHyGQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-stack": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.2.5.tgz", + "integrity": "sha512-bYrutc+neOyWxtZdbB2USbQttZN0mXaOyYLIsaTbJhFsfpXyGWUxJpEuO1rJ8IIJm2qH4+xJT0mxUSsEDTYwdQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/node-config-provider": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.3.5.tgz", + "integrity": "sha512-UTurh1C4qkVCtqggI36DGbLB2Kv8UlcFdMXDcWMbqVY2uRg0XmT9Pb4Vj6oSQ34eizO1fvR0RnFV4Axw4IrrAg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^4.2.5", + "@smithy/shared-ini-file-loader": "^4.4.0", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/node-http-handler": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.4.5.tgz", + "integrity": "sha512-CMnzM9R2WqlqXQGtIlsHMEZfXKJVTIrqCNoSd/QpAyp+Dw0a1Vps13l6ma1fH8g7zSPNsA59B/kWgeylFuA/lw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^4.2.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/querystring-builder": "^4.2.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/property-provider": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.2.5.tgz", + "integrity": "sha512-8iLN1XSE1rl4MuxvQ+5OSk/Zb5El7NJZ1td6Tn+8dQQHIjp59Lwl6bd0+nzw6SKm2wSSriH2v/I9LPzUic7EOg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/protocol-http": { + "version": "5.3.5", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.3.5.tgz", + "integrity": "sha512-RlaL+sA0LNMp03bf7XPbFmT5gN+w3besXSWMkA8rcmxLSVfiEXElQi4O2IWwPfxzcHkxqrwBFMbngB8yx/RvaQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/querystring-builder": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.2.5.tgz", + "integrity": "sha512-y98otMI1saoajeik2kLfGyRp11e5U/iJYH/wLCh3aTV/XutbGT9nziKGkgCaMD1ghK7p6htHMm6b6scl9JRUWg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.9.0", + "@smithy/util-uri-escape": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/querystring-parser": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.2.5.tgz", + "integrity": "sha512-031WCTdPYgiQRYNPXznHXof2YM0GwL6SeaSyTH/P72M1Vz73TvCNH2Nq8Iu2IEPq9QP2yx0/nrw5YmSeAi/AjQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/service-error-classification": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.2.5.tgz", + "integrity": "sha512-8fEvK+WPE3wUAcDvqDQG1Vk3ANLR8Px979te96m84CbKAjBVf25rPYSzb4xU4hlTyho7VhOGnh5i62D/JVF0JQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.9.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/shared-ini-file-loader": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.4.0.tgz", + "integrity": "sha512-5WmZ5+kJgJDjwXXIzr1vDTG+RhF9wzSODQBfkrQ2VVkYALKGvZX1lgVSxEkgicSAFnFhPj5rudJV0zoinqS0bA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/signature-v4": { + "version": "5.3.5", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.3.5.tgz", + "integrity": "sha512-xSUfMu1FT7ccfSXkoLl/QRQBi2rOvi3tiBZU2Tdy3I6cgvZ6SEi9QNey+lqps/sJRnogIS+lq+B1gxxbra2a/w==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^4.2.0", + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", + "@smithy/util-hex-encoding": "^4.2.0", + "@smithy/util-middleware": "^4.2.5", + "@smithy/util-uri-escape": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/smithy-client": { + "version": "4.9.10", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.9.10.tgz", + "integrity": "sha512-Jaoz4Jw1QYHc1EFww/E6gVtNjhoDU+gwRKqXP6C3LKYqqH2UQhP8tMP3+t/ePrhaze7fhLE8vS2q6vVxBANFTQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.18.7", + "@smithy/middleware-endpoint": "^4.3.14", + "@smithy/middleware-stack": "^4.2.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", + "@smithy/util-stream": "^4.5.6", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/types": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.9.0.tgz", + "integrity": "sha512-MvUbdnXDTwykR8cB1WZvNNwqoWVaTRA0RLlLmf/cIFNMM2cKWz01X4Ly6SMC4Kks30r8tT3Cty0jmeWfiuyHTA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/url-parser": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.2.5.tgz", + "integrity": "sha512-VaxMGsilqFnK1CeBX+LXnSuaMx4sTL/6znSZh2829txWieazdVxr54HmiyTsIbpOTLcf5nYpq9lpzmwRdxj6rQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/querystring-parser": "^4.2.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-base64": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.3.0.tgz", + "integrity": "sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-body-length-browser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.2.0.tgz", + "integrity": "sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-body-length-node": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.2.1.tgz", + "integrity": "sha512-h53dz/pISVrVrfxV1iqXlx5pRg3V2YWFcSQyPyXZRrZoZj4R4DeWRDo1a7dd3CPTcFi3kE+98tuNyD2axyZReA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-buffer-from": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.2.0.tgz", + "integrity": "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-config-provider": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.2.0.tgz", + "integrity": "sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-browser": { + "version": "4.3.13", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.13.tgz", + "integrity": "sha512-hlVLdAGrVfyNei+pKIgqDTxfu/ZI2NSyqj4IDxKd5bIsIqwR/dSlkxlPaYxFiIaDVrBy0he8orsFy+Cz119XvA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^4.2.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-node": { + "version": "4.2.16", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.16.tgz", + "integrity": "sha512-F1t22IUiJLHrxW9W1CQ6B9PN+skZ9cqSuzB18Eh06HrJPbjsyZ7ZHecAKw80DQtyGTRcVfeukKaCRYebFwclbg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/config-resolver": "^4.4.3", + "@smithy/credential-provider-imds": "^4.2.5", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/property-provider": "^4.2.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-endpoints": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.2.5.tgz", + "integrity": "sha512-3O63AAWu2cSNQZp+ayl9I3NapW1p1rR5mlVHcF6hAB1dPZUQFfRPYtplWX/3xrzWthPGj5FqB12taJJCfH6s8A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.3.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-hex-encoding": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.2.0.tgz", + "integrity": "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-middleware": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.2.5.tgz", + "integrity": "sha512-6Y3+rvBF7+PZOc40ybeZMcGln6xJGVeY60E7jy9Mv5iKpMJpHgRE6dKy9ScsVxvfAYuEX4Q9a65DQX90KaQ3bA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-retry": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.2.5.tgz", + "integrity": "sha512-GBj3+EZBbN4NAqJ/7pAhsXdfzdlznOh8PydUijy6FpNIMnHPSMO2/rP4HKu+UFeikJxShERk528oy7GT79YiJg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/service-error-classification": "^4.2.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-stream": { + "version": "4.5.6", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.6.tgz", + "integrity": "sha512-qWw/UM59TiaFrPevefOZ8CNBKbYEP6wBAIlLqxn3VAIo9rgnTNc4ASbVrqDmhuwI87usnjhdQrxodzAGFFzbRQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/fetch-http-handler": "^5.3.6", + "@smithy/node-http-handler": "^4.4.5", + "@smithy/types": "^4.9.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" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-uri-escape": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.2.0.tgz", + "integrity": "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-utf8": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.2.0.tgz", + "integrity": "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-waiter": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.2.5.tgz", + "integrity": "sha512-Dbun99A3InifQdIrsXZ+QLcC0PGBPAdrl4cj1mTgJvyc9N2zf7QSxg8TBkzsCmGJdE3TLbO9ycwpY0EkWahQ/g==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^4.2.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/uuid": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/uuid/-/uuid-1.1.0.tgz", + "integrity": "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@swc/helpers": { "version": "0.5.15", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", @@ -2420,6 +4139,21 @@ "baseline-browser-mapping": "dist/cli.js" } }, + "node_modules/bcryptjs": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-3.0.3.tgz", + "integrity": "sha512-GlF5wPWnSa/X5LKM1o0wz0suXIINz1iHRLvTS+sLyi7XPbe5ycmYI3DlZqVGZZtDgl4DmasFg7gOB3JYbphV5g==", + "license": "BSD-3-Clause", + "bin": { + "bcrypt": "bin/bcrypt" + } + }, + "node_modules/bowser": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.13.1.tgz", + "integrity": "sha512-OHawaAbjwx6rqICCKgSG0SAnT05bzd7ppyKLVUITZpANBaaMFBAsaNkto3LoQ31tyFP5kNujE8Cdx85G9VzOkw==", + "license": "MIT" + }, "node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", @@ -3503,6 +5237,24 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-xml-parser": { + "version": "5.2.5", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.2.5.tgz", + "integrity": "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "strnum": "^2.1.0" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, "node_modules/fastq": { "version": "1.19.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", @@ -3593,6 +5345,20 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -4407,6 +6173,15 @@ "jiti": "lib/jiti-cli.mjs" } }, + "node_modules/jose": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.1.3.tgz", + "integrity": "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -5342,6 +7117,26 @@ "node": ">= 0.8.0" } }, + "node_modules/prisma": { + "version": "5.19.1", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-5.19.1.tgz", + "integrity": "sha512-c5K9MiDaa+VAAyh1OiYk76PXOme9s3E992D7kvvIOhCrNsBQfy2mP2QAQtX0WNj140IgG++12kwZpYB9iIydNQ==", + "hasInstallScript": true, + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@prisma/engines": "5.19.1" + }, + "bin": { + "prisma": "build/index.js" + }, + "engines": { + "node": ">=16.13" + }, + "optionalDependencies": { + "fsevents": "2.3.3" + } + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -5978,6 +7773,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strnum": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.1.1.tgz", + "integrity": "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, "node_modules/styled-jsx": { "version": "5.1.6", "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", diff --git a/package.json b/package.json index bfc32b1..d4d5026 100644 --- a/package.json +++ b/package.json @@ -3,13 +3,19 @@ "version": "0.1.0", "private": true, "scripts": { - "dev": "next dev", + "dev": "next dev -p 4000 --webpack", "build": "next build", - "start": "next start", + "start": "next start -p 4000", "lint": "eslint" }, "dependencies": { + "@aws-sdk/client-s3": "^3.948.0", + "@aws-sdk/s3-request-presigner": "^3.948.0", + "@prisma/client": "^5.19.1", + "bcryptjs": "^3.0.3", + "jose": "^6.1.3", "next": "16.0.8", + "prisma": "^5.19.1", "react": "19.2.1", "react-dom": "19.2.1" }, diff --git a/prisma/dev.db b/prisma/dev.db new file mode 100644 index 0000000..0390eef Binary files /dev/null and b/prisma/dev.db differ diff --git a/prisma/migrations/20251211003826_init/migration.sql b/prisma/migrations/20251211003826_init/migration.sql new file mode 100644 index 0000000..ae1e0c6 --- /dev/null +++ b/prisma/migrations/20251211003826_init/migration.sql @@ -0,0 +1,28 @@ +-- CreateTable +CREATE TABLE "User" ( + "id" TEXT NOT NULL PRIMARY KEY, + "email" TEXT NOT NULL, + "passwordHash" TEXT NOT NULL, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL +); + +-- CreateTable +CREATE TABLE "File" ( + "id" TEXT NOT NULL PRIMARY KEY, + "key" TEXT NOT NULL, + "name" TEXT NOT NULL, + "contentType" TEXT NOT NULL, + "sizeBytes" BIGINT NOT NULL, + "relativePath" TEXT NOT NULL, + "userId" TEXT NOT NULL, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + CONSTRAINT "File_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); + +-- CreateIndex +CREATE UNIQUE INDEX "User_email_key" ON "User"("email"); + +-- CreateIndex +CREATE UNIQUE INDEX "File_key_key" ON "File"("key"); diff --git a/prisma/migrations/migration_lock.toml b/prisma/migrations/migration_lock.toml new file mode 100644 index 0000000..e5e5c47 --- /dev/null +++ b/prisma/migrations/migration_lock.toml @@ -0,0 +1,3 @@ +# Please do not edit this file manually +# It should be added in your version-control system (i.e. Git) +provider = "sqlite" \ No newline at end of file diff --git a/prisma/schema.prisma b/prisma/schema.prisma new file mode 100644 index 0000000..654ae58 --- /dev/null +++ b/prisma/schema.prisma @@ -0,0 +1,31 @@ +generator client { + provider = "prisma-client-js" +} + +datasource db { + provider = "sqlite" + url = env("DATABASE_URL") +} + +model User { + id String @id @default(cuid()) + email String @unique + passwordHash String + files File[] + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +model File { + id String @id @default(cuid()) + key String @unique + name String + contentType String + sizeBytes BigInt + relativePath String + user User @relation(fields: [userId], references: [id]) + userId String + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + diff --git a/public/Fonts/OTF/Satoshi-Black.otf b/public/Fonts/OTF/Satoshi-Black.otf new file mode 100644 index 0000000..4f5f852 Binary files /dev/null and b/public/Fonts/OTF/Satoshi-Black.otf differ diff --git a/public/Fonts/OTF/Satoshi-BlackItalic.otf b/public/Fonts/OTF/Satoshi-BlackItalic.otf new file mode 100644 index 0000000..b105ea4 Binary files /dev/null and b/public/Fonts/OTF/Satoshi-BlackItalic.otf differ diff --git a/public/Fonts/OTF/Satoshi-Bold.otf b/public/Fonts/OTF/Satoshi-Bold.otf new file mode 100644 index 0000000..677ab5f Binary files /dev/null and b/public/Fonts/OTF/Satoshi-Bold.otf differ diff --git a/public/Fonts/OTF/Satoshi-BoldItalic.otf b/public/Fonts/OTF/Satoshi-BoldItalic.otf new file mode 100644 index 0000000..cd5b85e Binary files /dev/null and b/public/Fonts/OTF/Satoshi-BoldItalic.otf differ diff --git a/public/Fonts/OTF/Satoshi-Italic.otf b/public/Fonts/OTF/Satoshi-Italic.otf new file mode 100644 index 0000000..d8652b3 Binary files /dev/null and b/public/Fonts/OTF/Satoshi-Italic.otf differ diff --git a/public/Fonts/OTF/Satoshi-Light.otf b/public/Fonts/OTF/Satoshi-Light.otf new file mode 100644 index 0000000..22cb8bc Binary files /dev/null and b/public/Fonts/OTF/Satoshi-Light.otf differ diff --git a/public/Fonts/OTF/Satoshi-LightItalic.otf b/public/Fonts/OTF/Satoshi-LightItalic.otf new file mode 100644 index 0000000..1cf1a32 Binary files /dev/null and b/public/Fonts/OTF/Satoshi-LightItalic.otf differ diff --git a/public/Fonts/OTF/Satoshi-Medium.otf b/public/Fonts/OTF/Satoshi-Medium.otf new file mode 100644 index 0000000..3513a83 Binary files /dev/null and b/public/Fonts/OTF/Satoshi-Medium.otf differ diff --git a/public/Fonts/OTF/Satoshi-MediumItalic.otf b/public/Fonts/OTF/Satoshi-MediumItalic.otf new file mode 100644 index 0000000..105838c Binary files /dev/null and b/public/Fonts/OTF/Satoshi-MediumItalic.otf differ diff --git a/public/Fonts/OTF/Satoshi-Regular.otf b/public/Fonts/OTF/Satoshi-Regular.otf new file mode 100644 index 0000000..ddaadc0 Binary files /dev/null and b/public/Fonts/OTF/Satoshi-Regular.otf differ diff --git a/public/Fonts/TTF/Satoshi-Variable.ttf b/public/Fonts/TTF/Satoshi-Variable.ttf new file mode 100644 index 0000000..976e85c Binary files /dev/null and b/public/Fonts/TTF/Satoshi-Variable.ttf differ diff --git a/public/Fonts/TTF/Satoshi-VariableItalic.ttf b/public/Fonts/TTF/Satoshi-VariableItalic.ttf new file mode 100644 index 0000000..4c2677c Binary files /dev/null and b/public/Fonts/TTF/Satoshi-VariableItalic.ttf differ diff --git a/public/Fonts/WEB/README.md b/public/Fonts/WEB/README.md new file mode 100644 index 0000000..d2a7388 --- /dev/null +++ b/public/Fonts/WEB/README.md @@ -0,0 +1,43 @@ +# Installing Webfonts +Follow these simple Steps. + +## 1. +Put `satoshi/` Folder into a Folder called `fonts/`. + +## 2. +Put `satoshi.css` into your `css/` Folder. + +## 3. (Optional) +You may adapt the `url('path')` in `satoshi.css` depends on your Website Filesystem. + +## 4. +Import `satoshi.css` at the top of you main Stylesheet. + +``` +@import url('satoshi.css'); +``` + +## 5. +You are now ready to use the following Rules in your CSS to specify each Font Style: +``` +font-family: Satoshi-Light; +font-family: Satoshi-LightItalic; +font-family: Satoshi-Regular; +font-family: Satoshi-Italic; +font-family: Satoshi-Medium; +font-family: Satoshi-MediumItalic; +font-family: Satoshi-Bold; +font-family: Satoshi-BoldItalic; +font-family: Satoshi-Black; +font-family: Satoshi-BlackItalic; +font-family: Satoshi-Variable; +font-family: Satoshi-VariableItalic; + +``` +## 6. (Optional) +Use `font-variation-settings` rule to controll axes of variable fonts: +wght 900.0 + +Available axes: +'wght' (range from 300.0 to 900.0 + diff --git a/public/Fonts/WEB/css/satoshi.css b/public/Fonts/WEB/css/satoshi.css new file mode 100644 index 0000000..773415b --- /dev/null +++ b/public/Fonts/WEB/css/satoshi.css @@ -0,0 +1,148 @@ +/** +* @license +* +* Font Family: Satoshi +* Designed by: Deni Anggara +* URL: https://www.fontshare.com/fonts/satoshi +* © 2025 Indian Type Foundry +* +* Satoshi Light +* Satoshi LightItalic +* Satoshi Regular +* Satoshi Italic +* Satoshi Medium +* Satoshi MediumItalic +* Satoshi Bold +* Satoshi BoldItalic +* Satoshi Black +* Satoshi BlackItalic +* Satoshi Variable (Variable font) +* Satoshi VariableItalic (Variable font) + +* +*/ +@font-face { + font-family: 'Satoshi-Light'; + src: url('../fonts/Satoshi-Light.woff2') format('woff2'), + url('../fonts/Satoshi-Light.woff') format('woff'), + url('../fonts/Satoshi-Light.ttf') format('truetype'); + font-weight: 300; + font-display: swap; + font-style: normal; +} +@font-face { + font-family: 'Satoshi-LightItalic'; + src: url('../fonts/Satoshi-LightItalic.woff2') format('woff2'), + url('../fonts/Satoshi-LightItalic.woff') format('woff'), + url('../fonts/Satoshi-LightItalic.ttf') format('truetype'); + font-weight: 300; + font-display: swap; + font-style: italic; +} +@font-face { + font-family: 'Satoshi-Regular'; + src: url('../fonts/Satoshi-Regular.woff2') format('woff2'), + url('../fonts/Satoshi-Regular.woff') format('woff'), + url('../fonts/Satoshi-Regular.ttf') format('truetype'); + font-weight: 400; + font-display: swap; + font-style: normal; +} +@font-face { + font-family: 'Satoshi-Italic'; + src: url('../fonts/Satoshi-Italic.woff2') format('woff2'), + url('../fonts/Satoshi-Italic.woff') format('woff'), + url('../fonts/Satoshi-Italic.ttf') format('truetype'); + font-weight: 400; + font-display: swap; + font-style: italic; +} +@font-face { + font-family: 'Satoshi-Medium'; + src: url('../fonts/Satoshi-Medium.woff2') format('woff2'), + url('../fonts/Satoshi-Medium.woff') format('woff'), + url('../fonts/Satoshi-Medium.ttf') format('truetype'); + font-weight: 500; + font-display: swap; + font-style: normal; +} +@font-face { + font-family: 'Satoshi-MediumItalic'; + src: url('../fonts/Satoshi-MediumItalic.woff2') format('woff2'), + url('../fonts/Satoshi-MediumItalic.woff') format('woff'), + url('../fonts/Satoshi-MediumItalic.ttf') format('truetype'); + font-weight: 500; + font-display: swap; + font-style: italic; +} +@font-face { + font-family: 'Satoshi-Bold'; + src: url('../fonts/Satoshi-Bold.woff2') format('woff2'), + url('../fonts/Satoshi-Bold.woff') format('woff'), + url('../fonts/Satoshi-Bold.ttf') format('truetype'); + font-weight: 700; + font-display: swap; + font-style: normal; +} +@font-face { + font-family: 'Satoshi-BoldItalic'; + src: url('../fonts/Satoshi-BoldItalic.woff2') format('woff2'), + url('../fonts/Satoshi-BoldItalic.woff') format('woff'), + url('../fonts/Satoshi-BoldItalic.ttf') format('truetype'); + font-weight: 700; + font-display: swap; + font-style: italic; +} +@font-face { + font-family: 'Satoshi-Black'; + src: url('../fonts/Satoshi-Black.woff2') format('woff2'), + url('../fonts/Satoshi-Black.woff') format('woff'), + url('../fonts/Satoshi-Black.ttf') format('truetype'); + font-weight: 900; + font-display: swap; + font-style: normal; +} +@font-face { + font-family: 'Satoshi-BlackItalic'; + src: url('../fonts/Satoshi-BlackItalic.woff2') format('woff2'), + url('../fonts/Satoshi-BlackItalic.woff') format('woff'), + url('../fonts/Satoshi-BlackItalic.ttf') format('truetype'); + font-weight: 900; + font-display: swap; + font-style: italic; +} +/** +* This is a variable font +* You can control variable axes as shown below: +* font-variation-settings: wght 900.0; +* +* available axes: +'wght' (range from 300.0 to 900.0 +*/ +@font-face { + font-family: 'Satoshi-Variable'; + src: url('../fonts/Satoshi-Variable.woff2') format('woff2'), + url('../fonts/Satoshi-Variable.woff') format('woff'), + url('../fonts/Satoshi-Variable.ttf') format('truetype'); + font-weight: 300 900; + font-display: swap; + font-style: normal; +} +/** +* This is a variable font +* You can control variable axes as shown below: +* font-variation-settings: wght 900.0; +* +* available axes: +'wght' (range from 300.0 to 900.0 +*/ +@font-face { + font-family: 'Satoshi-VariableItalic'; + src: url('../fonts/Satoshi-VariableItalic.woff2') format('woff2'), + url('../fonts/Satoshi-VariableItalic.woff') format('woff'), + url('../fonts/Satoshi-VariableItalic.ttf') format('truetype'); + font-weight: 300 900; + font-display: swap; + font-style: italic; +} + diff --git a/public/Fonts/WEB/fonts/Satoshi-Black.eot b/public/Fonts/WEB/fonts/Satoshi-Black.eot new file mode 100644 index 0000000..11747f3 Binary files /dev/null and b/public/Fonts/WEB/fonts/Satoshi-Black.eot differ diff --git a/public/Fonts/WEB/fonts/Satoshi-Black.ttf b/public/Fonts/WEB/fonts/Satoshi-Black.ttf new file mode 100644 index 0000000..62015ac Binary files /dev/null and b/public/Fonts/WEB/fonts/Satoshi-Black.ttf differ diff --git a/public/Fonts/WEB/fonts/Satoshi-Black.woff b/public/Fonts/WEB/fonts/Satoshi-Black.woff new file mode 100644 index 0000000..a6bee36 Binary files /dev/null and b/public/Fonts/WEB/fonts/Satoshi-Black.woff differ diff --git a/public/Fonts/WEB/fonts/Satoshi-Black.woff2 b/public/Fonts/WEB/fonts/Satoshi-Black.woff2 new file mode 100644 index 0000000..64492d5 Binary files /dev/null and b/public/Fonts/WEB/fonts/Satoshi-Black.woff2 differ diff --git a/public/Fonts/WEB/fonts/Satoshi-BlackItalic.eot b/public/Fonts/WEB/fonts/Satoshi-BlackItalic.eot new file mode 100644 index 0000000..de2edbb Binary files /dev/null and b/public/Fonts/WEB/fonts/Satoshi-BlackItalic.eot differ diff --git a/public/Fonts/WEB/fonts/Satoshi-BlackItalic.ttf b/public/Fonts/WEB/fonts/Satoshi-BlackItalic.ttf new file mode 100644 index 0000000..74410b9 Binary files /dev/null and b/public/Fonts/WEB/fonts/Satoshi-BlackItalic.ttf differ diff --git a/public/Fonts/WEB/fonts/Satoshi-BlackItalic.woff b/public/Fonts/WEB/fonts/Satoshi-BlackItalic.woff new file mode 100644 index 0000000..0e07e1c Binary files /dev/null and b/public/Fonts/WEB/fonts/Satoshi-BlackItalic.woff differ diff --git a/public/Fonts/WEB/fonts/Satoshi-BlackItalic.woff2 b/public/Fonts/WEB/fonts/Satoshi-BlackItalic.woff2 new file mode 100644 index 0000000..9d5c911 Binary files /dev/null and b/public/Fonts/WEB/fonts/Satoshi-BlackItalic.woff2 differ diff --git a/public/Fonts/WEB/fonts/Satoshi-Bold.eot b/public/Fonts/WEB/fonts/Satoshi-Bold.eot new file mode 100644 index 0000000..390ae25 Binary files /dev/null and b/public/Fonts/WEB/fonts/Satoshi-Bold.eot differ diff --git a/public/Fonts/WEB/fonts/Satoshi-Bold.ttf b/public/Fonts/WEB/fonts/Satoshi-Bold.ttf new file mode 100644 index 0000000..00bc985 Binary files /dev/null and b/public/Fonts/WEB/fonts/Satoshi-Bold.ttf differ diff --git a/public/Fonts/WEB/fonts/Satoshi-Bold.woff b/public/Fonts/WEB/fonts/Satoshi-Bold.woff new file mode 100644 index 0000000..bba8257 Binary files /dev/null and b/public/Fonts/WEB/fonts/Satoshi-Bold.woff differ diff --git a/public/Fonts/WEB/fonts/Satoshi-Bold.woff2 b/public/Fonts/WEB/fonts/Satoshi-Bold.woff2 new file mode 100644 index 0000000..0a8db7a Binary files /dev/null and b/public/Fonts/WEB/fonts/Satoshi-Bold.woff2 differ diff --git a/public/Fonts/WEB/fonts/Satoshi-BoldItalic.eot b/public/Fonts/WEB/fonts/Satoshi-BoldItalic.eot new file mode 100644 index 0000000..426be2a Binary files /dev/null and b/public/Fonts/WEB/fonts/Satoshi-BoldItalic.eot differ diff --git a/public/Fonts/WEB/fonts/Satoshi-BoldItalic.ttf b/public/Fonts/WEB/fonts/Satoshi-BoldItalic.ttf new file mode 100644 index 0000000..24f012c Binary files /dev/null and b/public/Fonts/WEB/fonts/Satoshi-BoldItalic.ttf differ diff --git a/public/Fonts/WEB/fonts/Satoshi-BoldItalic.woff b/public/Fonts/WEB/fonts/Satoshi-BoldItalic.woff new file mode 100644 index 0000000..8bcb7a6 Binary files /dev/null and b/public/Fonts/WEB/fonts/Satoshi-BoldItalic.woff differ diff --git a/public/Fonts/WEB/fonts/Satoshi-BoldItalic.woff2 b/public/Fonts/WEB/fonts/Satoshi-BoldItalic.woff2 new file mode 100644 index 0000000..225527f Binary files /dev/null and b/public/Fonts/WEB/fonts/Satoshi-BoldItalic.woff2 differ diff --git a/public/Fonts/WEB/fonts/Satoshi-Italic.eot b/public/Fonts/WEB/fonts/Satoshi-Italic.eot new file mode 100644 index 0000000..64039a8 Binary files /dev/null and b/public/Fonts/WEB/fonts/Satoshi-Italic.eot differ diff --git a/public/Fonts/WEB/fonts/Satoshi-Italic.ttf b/public/Fonts/WEB/fonts/Satoshi-Italic.ttf new file mode 100644 index 0000000..c214f4f Binary files /dev/null and b/public/Fonts/WEB/fonts/Satoshi-Italic.ttf differ diff --git a/public/Fonts/WEB/fonts/Satoshi-Italic.woff b/public/Fonts/WEB/fonts/Satoshi-Italic.woff new file mode 100644 index 0000000..edd4d93 Binary files /dev/null and b/public/Fonts/WEB/fonts/Satoshi-Italic.woff differ diff --git a/public/Fonts/WEB/fonts/Satoshi-Italic.woff2 b/public/Fonts/WEB/fonts/Satoshi-Italic.woff2 new file mode 100644 index 0000000..8b98599 Binary files /dev/null and b/public/Fonts/WEB/fonts/Satoshi-Italic.woff2 differ diff --git a/public/Fonts/WEB/fonts/Satoshi-Light.eot b/public/Fonts/WEB/fonts/Satoshi-Light.eot new file mode 100644 index 0000000..d8fcacc Binary files /dev/null and b/public/Fonts/WEB/fonts/Satoshi-Light.eot differ diff --git a/public/Fonts/WEB/fonts/Satoshi-Light.ttf b/public/Fonts/WEB/fonts/Satoshi-Light.ttf new file mode 100644 index 0000000..b41a2d4 Binary files /dev/null and b/public/Fonts/WEB/fonts/Satoshi-Light.ttf differ diff --git a/public/Fonts/WEB/fonts/Satoshi-Light.woff b/public/Fonts/WEB/fonts/Satoshi-Light.woff new file mode 100644 index 0000000..8f05e4e Binary files /dev/null and b/public/Fonts/WEB/fonts/Satoshi-Light.woff differ diff --git a/public/Fonts/WEB/fonts/Satoshi-Light.woff2 b/public/Fonts/WEB/fonts/Satoshi-Light.woff2 new file mode 100644 index 0000000..cf18cd4 Binary files /dev/null and b/public/Fonts/WEB/fonts/Satoshi-Light.woff2 differ diff --git a/public/Fonts/WEB/fonts/Satoshi-LightItalic.eot b/public/Fonts/WEB/fonts/Satoshi-LightItalic.eot new file mode 100644 index 0000000..e34a0df Binary files /dev/null and b/public/Fonts/WEB/fonts/Satoshi-LightItalic.eot differ diff --git a/public/Fonts/WEB/fonts/Satoshi-LightItalic.ttf b/public/Fonts/WEB/fonts/Satoshi-LightItalic.ttf new file mode 100644 index 0000000..08f5db5 Binary files /dev/null and b/public/Fonts/WEB/fonts/Satoshi-LightItalic.ttf differ diff --git a/public/Fonts/WEB/fonts/Satoshi-LightItalic.woff b/public/Fonts/WEB/fonts/Satoshi-LightItalic.woff new file mode 100644 index 0000000..a03a50d Binary files /dev/null and b/public/Fonts/WEB/fonts/Satoshi-LightItalic.woff differ diff --git a/public/Fonts/WEB/fonts/Satoshi-LightItalic.woff2 b/public/Fonts/WEB/fonts/Satoshi-LightItalic.woff2 new file mode 100644 index 0000000..6bd15ad Binary files /dev/null and b/public/Fonts/WEB/fonts/Satoshi-LightItalic.woff2 differ diff --git a/public/Fonts/WEB/fonts/Satoshi-Medium.eot b/public/Fonts/WEB/fonts/Satoshi-Medium.eot new file mode 100644 index 0000000..83cacec Binary files /dev/null and b/public/Fonts/WEB/fonts/Satoshi-Medium.eot differ diff --git a/public/Fonts/WEB/fonts/Satoshi-Medium.ttf b/public/Fonts/WEB/fonts/Satoshi-Medium.ttf new file mode 100644 index 0000000..ab149b7 Binary files /dev/null and b/public/Fonts/WEB/fonts/Satoshi-Medium.ttf differ diff --git a/public/Fonts/WEB/fonts/Satoshi-Medium.woff b/public/Fonts/WEB/fonts/Satoshi-Medium.woff new file mode 100644 index 0000000..cef3226 Binary files /dev/null and b/public/Fonts/WEB/fonts/Satoshi-Medium.woff differ diff --git a/public/Fonts/WEB/fonts/Satoshi-Medium.woff2 b/public/Fonts/WEB/fonts/Satoshi-Medium.woff2 new file mode 100644 index 0000000..ffd0ac9 Binary files /dev/null and b/public/Fonts/WEB/fonts/Satoshi-Medium.woff2 differ diff --git a/public/Fonts/WEB/fonts/Satoshi-MediumItalic.eot b/public/Fonts/WEB/fonts/Satoshi-MediumItalic.eot new file mode 100644 index 0000000..25d229a Binary files /dev/null and b/public/Fonts/WEB/fonts/Satoshi-MediumItalic.eot differ diff --git a/public/Fonts/WEB/fonts/Satoshi-MediumItalic.ttf b/public/Fonts/WEB/fonts/Satoshi-MediumItalic.ttf new file mode 100644 index 0000000..387f278 Binary files /dev/null and b/public/Fonts/WEB/fonts/Satoshi-MediumItalic.ttf differ diff --git a/public/Fonts/WEB/fonts/Satoshi-MediumItalic.woff b/public/Fonts/WEB/fonts/Satoshi-MediumItalic.woff new file mode 100644 index 0000000..46d8995 Binary files /dev/null and b/public/Fonts/WEB/fonts/Satoshi-MediumItalic.woff differ diff --git a/public/Fonts/WEB/fonts/Satoshi-MediumItalic.woff2 b/public/Fonts/WEB/fonts/Satoshi-MediumItalic.woff2 new file mode 100644 index 0000000..212adc9 Binary files /dev/null and b/public/Fonts/WEB/fonts/Satoshi-MediumItalic.woff2 differ diff --git a/public/Fonts/WEB/fonts/Satoshi-Regular.eot b/public/Fonts/WEB/fonts/Satoshi-Regular.eot new file mode 100644 index 0000000..452666f Binary files /dev/null and b/public/Fonts/WEB/fonts/Satoshi-Regular.eot differ diff --git a/public/Fonts/WEB/fonts/Satoshi-Regular.ttf b/public/Fonts/WEB/fonts/Satoshi-Regular.ttf new file mode 100644 index 0000000..fe85cd6 Binary files /dev/null and b/public/Fonts/WEB/fonts/Satoshi-Regular.ttf differ diff --git a/public/Fonts/WEB/fonts/Satoshi-Regular.woff b/public/Fonts/WEB/fonts/Satoshi-Regular.woff new file mode 100644 index 0000000..03ac195 Binary files /dev/null and b/public/Fonts/WEB/fonts/Satoshi-Regular.woff differ diff --git a/public/Fonts/WEB/fonts/Satoshi-Regular.woff2 b/public/Fonts/WEB/fonts/Satoshi-Regular.woff2 new file mode 100644 index 0000000..81c40ab Binary files /dev/null and b/public/Fonts/WEB/fonts/Satoshi-Regular.woff2 differ diff --git a/public/Fonts/WEB/fonts/Satoshi-Variable.eot b/public/Fonts/WEB/fonts/Satoshi-Variable.eot new file mode 100644 index 0000000..f42624e Binary files /dev/null and b/public/Fonts/WEB/fonts/Satoshi-Variable.eot differ diff --git a/public/Fonts/WEB/fonts/Satoshi-Variable.ttf b/public/Fonts/WEB/fonts/Satoshi-Variable.ttf new file mode 100644 index 0000000..976e85c Binary files /dev/null and b/public/Fonts/WEB/fonts/Satoshi-Variable.ttf differ diff --git a/public/Fonts/WEB/fonts/Satoshi-Variable.woff b/public/Fonts/WEB/fonts/Satoshi-Variable.woff new file mode 100644 index 0000000..f8dcd1d Binary files /dev/null and b/public/Fonts/WEB/fonts/Satoshi-Variable.woff differ diff --git a/public/Fonts/WEB/fonts/Satoshi-Variable.woff2 b/public/Fonts/WEB/fonts/Satoshi-Variable.woff2 new file mode 100644 index 0000000..b00e833 Binary files /dev/null and b/public/Fonts/WEB/fonts/Satoshi-Variable.woff2 differ diff --git a/public/Fonts/WEB/fonts/Satoshi-VariableItalic.eot b/public/Fonts/WEB/fonts/Satoshi-VariableItalic.eot new file mode 100644 index 0000000..5f4554a Binary files /dev/null and b/public/Fonts/WEB/fonts/Satoshi-VariableItalic.eot differ diff --git a/public/Fonts/WEB/fonts/Satoshi-VariableItalic.ttf b/public/Fonts/WEB/fonts/Satoshi-VariableItalic.ttf new file mode 100644 index 0000000..4c2677c Binary files /dev/null and b/public/Fonts/WEB/fonts/Satoshi-VariableItalic.ttf differ diff --git a/public/Fonts/WEB/fonts/Satoshi-VariableItalic.woff b/public/Fonts/WEB/fonts/Satoshi-VariableItalic.woff new file mode 100644 index 0000000..3fe029e Binary files /dev/null and b/public/Fonts/WEB/fonts/Satoshi-VariableItalic.woff differ diff --git a/public/Fonts/WEB/fonts/Satoshi-VariableItalic.woff2 b/public/Fonts/WEB/fonts/Satoshi-VariableItalic.woff2 new file mode 100644 index 0000000..e7ab3a0 Binary files /dev/null and b/public/Fonts/WEB/fonts/Satoshi-VariableItalic.woff2 differ