mirror of
https://github.com/diced/zipline.git
synced 2025-05-10 18:05:54 +02:00
feat: stuffs
This commit is contained in:
parent
8775ae2e29
commit
e4761c3f12
49 changed files with 1302 additions and 866 deletions
|
@ -1,11 +1,6 @@
|
|||
/** @type {import('eslint').Linter.Config} */
|
||||
module.exports = {
|
||||
extends: [
|
||||
'@remix-run/eslint-config',
|
||||
'@remix-run/eslint-config/node',
|
||||
'plugin:prettier/recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
],
|
||||
extends: ['next/core-web-vitals', 'plugin:prettier/recommended', 'plugin:@typescript-eslint/recommended'],
|
||||
root: true,
|
||||
plugins: ['unused-imports', '@typescript-eslint'],
|
||||
parser: '@typescript-eslint/parser',
|
||||
|
|
33
.gitignore
vendored
33
.gitignore
vendored
|
@ -1,18 +1,28 @@
|
|||
node_modules
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
/.cache
|
||||
/build
|
||||
/public/build
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# yarn
|
||||
.yarn/*
|
||||
!.yarn/releases
|
||||
!.yarn/plugins
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
.idea
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
|
@ -20,15 +30,14 @@ yarn-debug.log*
|
|||
yarn-error.log*
|
||||
|
||||
# local env files
|
||||
.env
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env*
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
|
||||
# zipline
|
||||
uploads*/
|
||||
dist/
|
||||
uploads*/
|
6
next.config.js
Normal file
6
next.config.js
Normal file
|
@ -0,0 +1,6 @@
|
|||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
reactStrictMode: true,
|
||||
}
|
||||
|
||||
module.exports = nextConfig
|
21
package.json
21
package.json
|
@ -6,13 +6,12 @@
|
|||
"version": "4.0.0-dev.1",
|
||||
"scripts": {
|
||||
"build": "run-s build:*",
|
||||
"build:remix": "remix build",
|
||||
"build:remix": "next build",
|
||||
"build:server": "tsup",
|
||||
"dev": "run-p dev:remix & (run-s dev:build && run-s dev:server)",
|
||||
"dev": "run-s dev:build dev:server",
|
||||
"dev:build": "cross-env NODE_ENV=development run-s build:server",
|
||||
"dev:remix": "cross-env NODE_ENV=development remix watch",
|
||||
"dev:server": "cross-env NODE_ENV=development DEBUG=zipline node --require ./node_modules/dotenv/config ./build/server.js",
|
||||
"start": "node ./server.mjs",
|
||||
"start": "node ./build/server.mjs",
|
||||
"lint": "eslint --cache --ignore-path .gitignore --fix .",
|
||||
"format": "prettier --write --ignore-path .gitignore .",
|
||||
"validate": "run-p lint format"
|
||||
|
@ -26,23 +25,20 @@
|
|||
"@mantine/form": "^6.0.14",
|
||||
"@mantine/hooks": "^6.0.14",
|
||||
"@mantine/modals": "^6.0.14",
|
||||
"@mantine/next": "^6.0.14",
|
||||
"@mantine/notifications": "^6.0.14",
|
||||
"@mantine/prism": "^6.0.14",
|
||||
"@mantine/remix": "^6.0.14",
|
||||
"@mantine/nprogress": "^6.0.14",
|
||||
"@prisma/client": "4.16.1",
|
||||
"@prisma/internals": "^4.16.1",
|
||||
"@prisma/migrate": "^4.16.1",
|
||||
"@remix-run/express": "^1.17.1",
|
||||
"@remix-run/node": "^1.16.1",
|
||||
"@remix-run/react": "^1.16.1",
|
||||
"@remix-run/v1-route-convention": "^0.1.2",
|
||||
"@types/express": "^4.17.17",
|
||||
"@tabler/icons-react": "^2.22.0",
|
||||
"argon2": "^0.30.3",
|
||||
"bytes": "^3.1.2",
|
||||
"colorette": "^2.0.20",
|
||||
"dayjs": "^1.11.8",
|
||||
"express": "^4.18.2",
|
||||
"isbot": "^3.6.10",
|
||||
"ms": "^2.1.3",
|
||||
"next": "^13.4.7",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"znv": "^0.3.2",
|
||||
|
@ -52,6 +48,7 @@
|
|||
"@remix-run/dev": "^1.16.1",
|
||||
"@remix-run/eslint-config": "^1.16.1",
|
||||
"@types/bytes": "^3.1.1",
|
||||
"@types/express": "^4.17.17",
|
||||
"@types/node": "^20.3.1",
|
||||
"@types/react": "^18.2.7",
|
||||
"@types/react-dom": "^18.2.4",
|
||||
|
|
14
postcss.config.js
Normal file
14
postcss.config.js
Normal file
|
@ -0,0 +1,14 @@
|
|||
module.exports = {
|
||||
plugins: {
|
||||
'postcss-preset-mantine': {},
|
||||
'postcss-simple-vars': {
|
||||
variables: {
|
||||
'mantine-breakpoint-xs': '36em',
|
||||
'mantine-breakpoint-sm': '48em',
|
||||
'mantine-breakpoint-md': '62em',
|
||||
'mantine-breakpoint-lg': '75em',
|
||||
'mantine-breakpoint-xl': '88em',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
|
@ -1,234 +0,0 @@
|
|||
-- CreateEnum
|
||||
CREATE TYPE "OAuthProviderType" AS ENUM ('DISCORD', 'GOOGLE', 'GITHUB');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "LimitType" AS ENUM ('UPLOAD_COUNT', 'UPLOAD_SIZE', 'SHORTEN_COUNT');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "LimitTimeframe" AS ENUM ('SECONDLY', 'MINUTELY', 'HOURLY', 'DAILY', 'WEEKLY', 'MONTHLY', 'YEARLY');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "IncompleteFileStatus" AS ENUM ('PENDING', 'PROCESSING', 'COMPLETE', 'FAILED');
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "zipline_meta" (
|
||||
"id" TEXT NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
"firstSetup" BOOLEAN NOT NULL DEFAULT true,
|
||||
|
||||
CONSTRAINT "zipline_meta_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "User" (
|
||||
"id" TEXT NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
"username" TEXT NOT NULL,
|
||||
"password" TEXT,
|
||||
"avatar" TEXT,
|
||||
"token" TEXT NOT NULL,
|
||||
"administrator" BOOLEAN NOT NULL DEFAULT false,
|
||||
"ziplineId" TEXT NOT NULL,
|
||||
|
||||
CONSTRAINT "User_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "OAuthProvider" (
|
||||
"id" TEXT NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
"userId" TEXT NOT NULL,
|
||||
"provider" "OAuthProviderType" NOT NULL,
|
||||
"accessToken" TEXT NOT NULL,
|
||||
"refreshToken" TEXT NOT NULL,
|
||||
"expiresIn" INTEGER NOT NULL,
|
||||
"scope" TEXT NOT NULL,
|
||||
"tokenType" TEXT NOT NULL,
|
||||
"profile" JSONB NOT NULL,
|
||||
|
||||
CONSTRAINT "OAuthProvider_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "UserLimit" (
|
||||
"id" TEXT NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
"type" "LimitType" NOT NULL,
|
||||
"value" INTEGER NOT NULL,
|
||||
"timeframe" "LimitTimeframe" NOT NULL,
|
||||
"userId" TEXT NOT NULL,
|
||||
|
||||
CONSTRAINT "UserLimit_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "File" (
|
||||
"id" TEXT NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
"deletesAt" TIMESTAMP(3),
|
||||
"name" TEXT NOT NULL,
|
||||
"originalName" TEXT NOT NULL,
|
||||
"path" TEXT NOT NULL,
|
||||
"size" INTEGER NOT NULL,
|
||||
"type" TEXT NOT NULL,
|
||||
"views" INTEGER NOT NULL DEFAULT 0,
|
||||
"favorite" BOOLEAN NOT NULL DEFAULT false,
|
||||
"password" TEXT,
|
||||
"zeroWidthSpace" TEXT,
|
||||
"userId" TEXT,
|
||||
"folderId" TEXT,
|
||||
|
||||
CONSTRAINT "File_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Folder" (
|
||||
"id" TEXT NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"userId" TEXT NOT NULL,
|
||||
|
||||
CONSTRAINT "Folder_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "IncompleteFile" (
|
||||
"id" TEXT NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
"status" "IncompleteFileStatus" NOT NULL,
|
||||
"chunksTotal" INTEGER NOT NULL,
|
||||
"chunksComplete" INTEGER NOT NULL,
|
||||
"metadata" JSONB NOT NULL,
|
||||
"userId" TEXT NOT NULL,
|
||||
|
||||
CONSTRAINT "IncompleteFile_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Tag" (
|
||||
"id" TEXT NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"color" TEXT NOT NULL,
|
||||
|
||||
CONSTRAINT "Tag_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Url" (
|
||||
"id" TEXT NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
"vanity" TEXT,
|
||||
"destination" TEXT NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"zeroWidthSpace" TEXT,
|
||||
"userId" TEXT,
|
||||
|
||||
CONSTRAINT "Url_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Metric" (
|
||||
"id" TEXT NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
"data" JSONB NOT NULL,
|
||||
"ziplineId" TEXT NOT NULL,
|
||||
|
||||
CONSTRAINT "Metric_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Invite" (
|
||||
"id" TEXT NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
"expiresAt" TIMESTAMP(3),
|
||||
"code" TEXT NOT NULL,
|
||||
"used" BOOLEAN NOT NULL DEFAULT false,
|
||||
"inviterId" TEXT NOT NULL,
|
||||
"ziplineId" TEXT NOT NULL,
|
||||
|
||||
CONSTRAINT "Invite_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "_FileToTag" (
|
||||
"A" TEXT NOT NULL,
|
||||
"B" TEXT NOT NULL
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "User_username_key" ON "User"("username");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "User_token_key" ON "User"("token");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "OAuthProvider_userId_provider_key" ON "OAuthProvider"("userId", "provider");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "UserLimit_type_key" ON "UserLimit"("type");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Tag_name_key" ON "Tag"("name");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Url_name_key" ON "Url"("name");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Invite_code_key" ON "Invite"("code");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "_FileToTag_AB_unique" ON "_FileToTag"("A", "B");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "_FileToTag_B_index" ON "_FileToTag"("B");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "User" ADD CONSTRAINT "User_ziplineId_fkey" FOREIGN KEY ("ziplineId") REFERENCES "zipline_meta"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "OAuthProvider" ADD CONSTRAINT "OAuthProvider_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "UserLimit" ADD CONSTRAINT "UserLimit_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "File" ADD CONSTRAINT "File_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "File" ADD CONSTRAINT "File_folderId_fkey" FOREIGN KEY ("folderId") REFERENCES "Folder"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Folder" ADD CONSTRAINT "Folder_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "IncompleteFile" ADD CONSTRAINT "IncompleteFile_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Url" ADD CONSTRAINT "Url_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Metric" ADD CONSTRAINT "Metric_ziplineId_fkey" FOREIGN KEY ("ziplineId") REFERENCES "zipline_meta"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Invite" ADD CONSTRAINT "Invite_inviterId_fkey" FOREIGN KEY ("inviterId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Invite" ADD CONSTRAINT "Invite_ziplineId_fkey" FOREIGN KEY ("ziplineId") REFERENCES "zipline_meta"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "_FileToTag" ADD CONSTRAINT "_FileToTag_A_fkey" FOREIGN KEY ("A") REFERENCES "File"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "_FileToTag" ADD CONSTRAINT "_FileToTag_B_fkey" FOREIGN KEY ("B") REFERENCES "Tag"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
@ -1,3 +0,0 @@
|
|||
# Please do not edit this file manually
|
||||
# It should be added in your version-control system (i.e. Git)
|
||||
provider = "postgresql"
|
|
@ -13,12 +13,6 @@ model Zipline {
|
|||
updatedAt DateTime @updatedAt
|
||||
|
||||
firstSetup Boolean @default(true)
|
||||
|
||||
metrics Metric[]
|
||||
users User[]
|
||||
invite Invite[]
|
||||
|
||||
@@map("zipline_meta")
|
||||
}
|
||||
|
||||
model User {
|
||||
|
@ -39,9 +33,6 @@ model User {
|
|||
invites Invite[]
|
||||
oauthProviders OAuthProvider[]
|
||||
IncompleteFile IncompleteFile[]
|
||||
|
||||
Zipline Zipline @relation(fields: [ziplineId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||||
ziplineId String
|
||||
}
|
||||
|
||||
model OAuthProvider {
|
||||
|
@ -191,9 +182,6 @@ model Metric {
|
|||
updatedAt DateTime @updatedAt
|
||||
|
||||
data Json
|
||||
|
||||
Zipline Zipline @relation(fields: [ziplineId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||||
ziplineId String
|
||||
}
|
||||
|
||||
model Invite {
|
||||
|
@ -207,7 +195,4 @@ model Invite {
|
|||
|
||||
inviter User @relation(fields: [inviterId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||||
inviterId String
|
||||
|
||||
Zipline Zipline @relation(fields: [ziplineId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||||
ziplineId String
|
||||
}
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 1.9 KiB |
|
@ -1,27 +0,0 @@
|
|||
const { createRoutesFromFolders } = require('@remix-run/v1-route-convention');
|
||||
|
||||
/**
|
||||
* @type {import('@remix-run/dev').AppConfig}
|
||||
*/
|
||||
module.exports = {
|
||||
ignoredRouteFiles: ['**/.*'],
|
||||
appDirectory: 'src/app',
|
||||
// assetsBuildDirectory: 'public/build',
|
||||
// serverBuildPath: 'build/index.js',
|
||||
serverModuleFormat: 'cjs',
|
||||
future: {
|
||||
unstable_dev: true,
|
||||
v2_routeConvention: true,
|
||||
v2_errorBoundary: true,
|
||||
v2_meta: true,
|
||||
v2_normalizeFormMethod: true,
|
||||
v2_headers: true,
|
||||
},
|
||||
publicPath: '/modules/',
|
||||
// use directory structure.
|
||||
routes(defineRoutes) {
|
||||
return createRoutesFromFolders(defineRoutes, {
|
||||
appDirectory: 'src/app',
|
||||
});
|
||||
},
|
||||
};
|
2
remix.env.d.ts
vendored
2
remix.env.d.ts
vendored
|
@ -1,2 +0,0 @@
|
|||
/// <reference types="@remix-run/dev" />
|
||||
/// <reference types="@remix-run/node/globals" />
|
|
@ -1,11 +0,0 @@
|
|||
import { RemixBrowser } from '@remix-run/react';
|
||||
import { hydrate } from 'react-dom';
|
||||
import { ClientProvider } from '@mantine/remix';
|
||||
|
||||
hydrate(
|
||||
<ClientProvider>
|
||||
<RemixBrowser />
|
||||
</ClientProvider>,
|
||||
document
|
||||
);
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
import { renderToString } from 'react-dom/server';
|
||||
import { RemixServer } from '@remix-run/react';
|
||||
import type { EntryContext } from '@remix-run/node';
|
||||
import { injectStyles, createStylesServer } from '@mantine/remix';
|
||||
|
||||
const server = createStylesServer();
|
||||
|
||||
export default function handleRequest(
|
||||
request: Request,
|
||||
responseStatusCode: number,
|
||||
responseHeaders: Headers,
|
||||
remixContext: EntryContext
|
||||
) {
|
||||
let markup = renderToString(<RemixServer context={remixContext} url={request.url} />);
|
||||
responseHeaders.set('Content-Type', 'text/html');
|
||||
|
||||
return new Response(`<!DOCTYPE html>${injectStyles(markup, server)}`, {
|
||||
status: responseStatusCode,
|
||||
headers: responseHeaders,
|
||||
});
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
import type { LoaderArgs } from '@remix-run/node';
|
||||
import type { Config } from 'lib/config/Config';
|
||||
|
||||
export type TypedLoaderArgs<T> = {
|
||||
context: T;
|
||||
} & Omit<LoaderArgs, 'context'>;
|
||||
|
||||
export type RouteContext = {
|
||||
config: Config;
|
||||
};
|
|
@ -1,32 +0,0 @@
|
|||
import type { V2_MetaFunction } from '@remix-run/node';
|
||||
import { Links, LiveReload, Meta, Outlet, Scripts, ScrollRestoration } from '@remix-run/react';
|
||||
import { MantineProvider, createEmotionCache } from '@mantine/core';
|
||||
import { StylesPlaceholder } from '@mantine/remix';
|
||||
|
||||
export const meta: V2_MetaFunction = () => [
|
||||
{ charSet: 'utf-8' },
|
||||
{ title: 'Zipline' },
|
||||
{ name: 'viewport', content: 'width=device-width,initial-scale=1' },
|
||||
];
|
||||
|
||||
createEmotionCache({ key: 'mantine' });
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<MantineProvider withGlobalStyles withNormalizeCSS>
|
||||
<html lang='en'>
|
||||
<head>
|
||||
<StylesPlaceholder />
|
||||
<Meta />
|
||||
<Links />
|
||||
</head>
|
||||
<body>
|
||||
<Outlet />
|
||||
<ScrollRestoration />
|
||||
<Scripts />
|
||||
<LiveReload />
|
||||
</body>
|
||||
</html>
|
||||
</MantineProvider>
|
||||
);
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
import { json } from '@remix-run/node';
|
||||
import { isRouteErrorResponse, useRouteError } from '@remix-run/react';
|
||||
import { RouteContext, TypedLoaderArgs } from '~/loader';
|
||||
|
||||
export const loader = async ({ params, context }: TypedLoaderArgs<RouteContext>) => {
|
||||
const slug = params['*']?.split('/').filter(Boolean) ?? [];
|
||||
if (!slug.length) {
|
||||
throw json('Not Found (no slug)', { status: 404 });
|
||||
}
|
||||
|
||||
console.log(slug, context.config);
|
||||
|
||||
if (slug[0] === context.config.files.route) {
|
||||
} else {
|
||||
throw json('Not Found (catchall)', { status: 404 });
|
||||
}
|
||||
|
||||
return json({ slug });
|
||||
};
|
||||
|
||||
export function ErrorBoundary() {
|
||||
const error = useRouteError();
|
||||
|
||||
if (isRouteErrorResponse(error)) {
|
||||
return <pre>{JSON.stringify(error, null, 2)}</pre>;
|
||||
} else if (error instanceof Error) {
|
||||
return <pre>{JSON.stringify(error, null, 2)}</pre>;
|
||||
} else {
|
||||
return <p>Unknown error</p>;
|
||||
}
|
||||
}
|
||||
|
||||
// export default function Index() {
|
||||
// return <p>Index</p>;
|
||||
// }
|
|
@ -1,13 +0,0 @@
|
|||
import { LoaderArgs, json } from '@remix-run/node';
|
||||
import { prisma } from '~/db.server';
|
||||
|
||||
export async function loader({ context, request }: LoaderArgs) {
|
||||
try {
|
||||
// test database connection
|
||||
await prisma.user.count();
|
||||
|
||||
return json({ pong: true }, { status: 200 });
|
||||
} catch (e) {
|
||||
return json({ pong: false }, { status: 500 });
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
import { LoaderArgs, json } from '@remix-run/node';
|
||||
import { useLoaderData } from '@remix-run/react';
|
||||
import { prisma } from '~/db.server';
|
||||
|
||||
export async function loader({}: LoaderArgs) {
|
||||
let zipline = await prisma.zipline.findFirst();
|
||||
if (!zipline) {
|
||||
zipline = await prisma.zipline.create({ data: {} });
|
||||
}
|
||||
|
||||
return json({ zipline });
|
||||
}
|
||||
|
||||
export default function Index() {
|
||||
const { zipline } = useLoaderData<typeof loader>();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<pre>{JSON.stringify(zipline, null, 2)}</pre>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
import { createCookieSessionStorage } from "@remix-run/node";
|
||||
|
||||
let sessionSecret = process.env.SESSION_SECRET;
|
||||
if (!sessionSecret) {
|
||||
throw new Error("SESSION_SECRET must be set");
|
||||
}
|
||||
|
||||
export let sessionStorage = createCookieSessionStorage({
|
||||
cookie: {
|
||||
name: "__session",
|
||||
secrets: [sessionSecret],
|
||||
sameSite: "lax",
|
||||
path: "/",
|
||||
maxAge: 60 * 60 * 24 * 30,
|
||||
httpOnly: true,
|
||||
},
|
||||
});
|
||||
|
||||
const USER_SESSION_KEY = "userId";
|
||||
|
||||
export async function getSession(request: Request) {
|
||||
const cookie = request.headers.get("Cookie");
|
||||
return sessionStorage.getSession(cookie);
|
||||
}
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
export function sleep<T>(ms: number, value: T) {
|
||||
return new Promise<T>((resolve) => setTimeout(() => resolve(value), ms));
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
export interface Config {
|
||||
core: ConfigCore;
|
||||
files: ConfigFiles;
|
||||
}
|
||||
|
||||
export interface ConfigCore {
|
||||
port: number;
|
||||
sessionSecret: string;
|
||||
databaseUrl: string;
|
||||
}
|
||||
|
||||
export interface ConfigFiles {
|
||||
route: string;
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
import type { ValidatedEnv } from './read';
|
||||
|
||||
export function convertEnv(env: ValidatedEnv) {
|
||||
return {
|
||||
core: {
|
||||
port: env.PORT,
|
||||
sessionSecret: env.SESSION_SECRET,
|
||||
databaseUrl: env.DATABASE_URL,
|
||||
},
|
||||
files: {
|
||||
route: env.FILES_ROUTE,
|
||||
},
|
||||
};
|
||||
}
|
16
src/lib/config/index.ts
Normal file
16
src/lib/config/index.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
import { readEnv } from './read';
|
||||
import { validateEnv, Config } from './validate';
|
||||
|
||||
let config: Config;
|
||||
|
||||
declare global {
|
||||
var __config__: Config;
|
||||
}
|
||||
|
||||
if (!global.__config__) {
|
||||
global.__config__ = validateEnv(readEnv());
|
||||
}
|
||||
|
||||
config = global.__config__;
|
||||
|
||||
export { config };
|
|
@ -1,23 +1,120 @@
|
|||
import { log } from 'src/lib/logger';
|
||||
import { parseEnv } from 'znv';
|
||||
import { z } from 'zod';
|
||||
import bytes from 'bytes';
|
||||
import msFn from 'ms';
|
||||
|
||||
const logger = log('config').c('read');
|
||||
type EnvType = 'string' | 'number' | 'boolean' | 'byte' | 'ms';
|
||||
|
||||
export type ParsedEnv = ReturnType<typeof readEnv>;
|
||||
|
||||
export const PROP_TO_ENV: Record<string, string> = {
|
||||
'core.port': 'CORE_PORT',
|
||||
'core.hostname': 'CORE_HOSTNAME',
|
||||
'core.secret': 'CORE_SECRET',
|
||||
'core.databaseUrl': 'CORE_DATABASE_URL',
|
||||
|
||||
'files.route': 'FILES_ROUTE',
|
||||
};
|
||||
|
||||
export function readEnv() {
|
||||
logger.debug('reading env');
|
||||
const envs = [
|
||||
env(PROP_TO_ENV['core.port'], 'core.port', 'number'),
|
||||
env(PROP_TO_ENV['core.hostname'], 'core.hostname', 'string'),
|
||||
env(PROP_TO_ENV['core.secret'], 'core.secret', 'string'),
|
||||
env(PROP_TO_ENV['core.databaseUrl'], 'core.databaseUrl', 'string'),
|
||||
|
||||
const validation = parseEnv(process.env, {
|
||||
PORT: z.number().default(3000),
|
||||
SESSION_SECRET: z.string(),
|
||||
DATABASE_URL: z.string(),
|
||||
env(PROP_TO_ENV['files.route'], 'files.route', 'string'),
|
||||
];
|
||||
|
||||
FILES_ROUTE: z.string().default('u'),
|
||||
});
|
||||
const raw = {
|
||||
core: {
|
||||
port: undefined,
|
||||
hostname: undefined,
|
||||
secret: undefined,
|
||||
databaseUrl: undefined,
|
||||
},
|
||||
files: {
|
||||
route: undefined,
|
||||
},
|
||||
};
|
||||
|
||||
logger.debug('env read', JSON.stringify(validation));
|
||||
for (let i = 0; i !== envs.length; ++i) {
|
||||
const env = envs[i];
|
||||
const value = process.env[env.variable];
|
||||
if (value === undefined) continue;
|
||||
|
||||
return validation;
|
||||
const parsed = parse(value, env.type);
|
||||
if (parsed === undefined) continue;
|
||||
|
||||
setDotProp(raw, env.property, parsed);
|
||||
}
|
||||
|
||||
return raw;
|
||||
}
|
||||
|
||||
export type ValidatedEnv = ReturnType<typeof readEnv>;
|
||||
function env(variable: string, property: string, type: EnvType) {
|
||||
return {
|
||||
variable,
|
||||
property,
|
||||
type,
|
||||
};
|
||||
}
|
||||
|
||||
function setDotProp(obj: Record<string, any>, property: string, value: unknown) {
|
||||
const parts = property.split('.');
|
||||
const last = parts.pop()!;
|
||||
|
||||
for (let i = 0; i !== parts.length; ++i) {
|
||||
const part = parts[i];
|
||||
const next = obj[part];
|
||||
|
||||
if (typeof next === 'object' && next !== null) {
|
||||
obj = next;
|
||||
} else {
|
||||
obj = obj[part] = {};
|
||||
}
|
||||
}
|
||||
|
||||
obj[last] = value;
|
||||
}
|
||||
|
||||
function parse(value: string, type: EnvType) {
|
||||
switch (type) {
|
||||
case 'string':
|
||||
return string(value);
|
||||
case 'number':
|
||||
return number(value);
|
||||
case 'boolean':
|
||||
return boolean(value);
|
||||
case 'byte':
|
||||
return byte(value);
|
||||
case 'ms':
|
||||
return ms(value);
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function string(value: string) {
|
||||
return value;
|
||||
}
|
||||
|
||||
function number(value: string) {
|
||||
const num = Number(value);
|
||||
if (isNaN(num)) return undefined;
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
function boolean(value: string) {
|
||||
if (value === 'true') return true;
|
||||
if (value === 'false') return false;
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function byte(value: string) {
|
||||
return bytes(value);
|
||||
}
|
||||
|
||||
function ms(value: string) {
|
||||
return msFn(value);
|
||||
}
|
||||
|
|
68
src/lib/config/validate.ts
Normal file
68
src/lib/config/validate.ts
Normal file
|
@ -0,0 +1,68 @@
|
|||
import { ZodError, z } from 'zod';
|
||||
import { PROP_TO_ENV, ParsedEnv } from './read';
|
||||
import { log } from '../logger';
|
||||
|
||||
const schema = z.object({
|
||||
core: z.object({
|
||||
port: z.number().default(3000),
|
||||
hostname: z.string().default('localhost'),
|
||||
secret: z.string().superRefine((s, c) => {
|
||||
if (s === 'changethis')
|
||||
return c.addIssue({
|
||||
code: 'custom',
|
||||
message: 'Secret must be changed from the default value',
|
||||
path: ['core', 'secret'],
|
||||
});
|
||||
|
||||
if (s.length <= 16) {
|
||||
return c.addIssue({
|
||||
code: 'too_small',
|
||||
minimum: 16,
|
||||
type: 'string',
|
||||
inclusive: true,
|
||||
message: 'Secret must contain at least 16 characters',
|
||||
path: ['core', 'secret'],
|
||||
exact: false,
|
||||
});
|
||||
}
|
||||
}),
|
||||
databaseUrl: z.string().url(),
|
||||
}),
|
||||
files: z.object({
|
||||
route: z.string().default('u'),
|
||||
}),
|
||||
});
|
||||
|
||||
export type Config = z.infer<typeof schema>;
|
||||
|
||||
export function validateEnv(env: ParsedEnv): Config {
|
||||
const logger = log('config').c('validate');
|
||||
|
||||
try {
|
||||
const validated = schema.parse(env);
|
||||
|
||||
if (!validated) {
|
||||
logger.error('There was an error while validating the environment.');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
return validated;
|
||||
} catch (e) {
|
||||
if (e instanceof ZodError) {
|
||||
logger.error(`There were ${e.errors.length} error(s) while validating the environment.`);
|
||||
|
||||
for (let i = 0; i !== e.errors.length; ++i) {
|
||||
const error = e.errors[i];
|
||||
logger.debug(JSON.stringify(error));
|
||||
|
||||
const path = PROP_TO_ENV[error.path.join('.')];
|
||||
|
||||
logger.error(`${path}: ${error.message}`);
|
||||
}
|
||||
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
throw e;
|
||||
}
|
||||
}
|
19
src/lib/cookie.ts
Normal file
19
src/lib/cookie.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
export function serializeCookie(
|
||||
name: string,
|
||||
value: string,
|
||||
options: {
|
||||
expires?: Date;
|
||||
maxAge?: number;
|
||||
path?: string;
|
||||
sameSite?: 'strict' | 'lax' | 'none';
|
||||
} = {}
|
||||
) {
|
||||
const cookie = [`${name}=${value}`];
|
||||
|
||||
if (options.expires) cookie.push(`Expires=${options.expires.toUTCString()}`);
|
||||
if (options.maxAge) cookie.push(`Max-Age=${options.maxAge}`);
|
||||
if (options.path) cookie.push(`Path=${options.path}`);
|
||||
if (options.sameSite) cookie.push(`SameSite=${options.sameSite}`);
|
||||
|
||||
return cookie.join('; ');
|
||||
}
|
97
src/lib/crypto.ts
Normal file
97
src/lib/crypto.ts
Normal file
|
@ -0,0 +1,97 @@
|
|||
import crypto from 'crypto';
|
||||
import { hash, verify } from 'argon2';
|
||||
|
||||
const ALGORITHM = 'aes-256-cbc';
|
||||
|
||||
export function createKey(secret: string) {
|
||||
const hash = crypto.createHash('sha256');
|
||||
hash.update(secret);
|
||||
|
||||
return hash.digest('hex').slice(0, 32);
|
||||
}
|
||||
|
||||
export function encrypt(value: string, secret: string): string {
|
||||
const key = createKey(secret);
|
||||
const iv = crypto.randomBytes(16);
|
||||
|
||||
const cipher = crypto.createCipheriv(ALGORITHM, Buffer.from(key), iv);
|
||||
|
||||
const encrypted = cipher.update(value);
|
||||
const final = cipher.final();
|
||||
|
||||
const buffer = Buffer.alloc(encrypted.length + final.length);
|
||||
buffer.set(encrypted);
|
||||
buffer.set(final, encrypted.length);
|
||||
|
||||
return iv.toString('hex') + '.' + buffer.toString('hex');
|
||||
}
|
||||
|
||||
export function decrypt(value: string, secret: string): string {
|
||||
const key = createKey(secret);
|
||||
const [iv, encrypted] = value.split('.');
|
||||
|
||||
const decipher = crypto.createDecipheriv(ALGORITHM, Buffer.from(key), Buffer.from(iv, 'hex'));
|
||||
|
||||
const decrypted = decipher.update(Buffer.from(encrypted, 'hex'));
|
||||
const final = decipher.final();
|
||||
|
||||
const buffer = Buffer.alloc(decrypted.length + final.length);
|
||||
buffer.set(decrypted);
|
||||
buffer.set(final, decrypted.length);
|
||||
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
export function randomCharacters(length: number): string {
|
||||
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||
const charactersLength = characters.length;
|
||||
|
||||
let result = '';
|
||||
|
||||
for (let i = 0; i !== length; ++i) {
|
||||
result += characters.charAt(Math.floor(Math.random() * charactersLength));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export function createToken(): string {
|
||||
const date = Date.now();
|
||||
const random = randomCharacters(32);
|
||||
|
||||
const date64 = Buffer.from(date.toString()).toString('base64');
|
||||
const random64 = Buffer.from(random).toString('base64');
|
||||
|
||||
return `${date64}.${random64}`;
|
||||
}
|
||||
|
||||
export function encryptToken(token: string, secret: string): string {
|
||||
const key = createKey(secret);
|
||||
|
||||
const date = Date.now();
|
||||
const date64 = Buffer.from(date.toString()).toString('base64');
|
||||
|
||||
const encrypted = encrypt(token, key);
|
||||
const encrypted64 = Buffer.from(encrypted).toString('base64');
|
||||
|
||||
return `${date64}.${encrypted64}`;
|
||||
}
|
||||
|
||||
export function decryptToken(encryptedToken: string, secret: string): [number, string] {
|
||||
const key = createKey(secret);
|
||||
const [date64, encrypted64] = encryptedToken.split('.');
|
||||
|
||||
const date = parseInt(Buffer.from(date64, 'base64').toString('ascii'), 10);
|
||||
|
||||
const encrypted = Buffer.from(encrypted64, 'base64').toString('ascii');
|
||||
|
||||
return [date, decrypt(encrypted, key)];
|
||||
}
|
||||
|
||||
export async function hashPassword(password: string) {
|
||||
return hash(password);
|
||||
}
|
||||
|
||||
export async function verifyPassword(password: string, hash: string) {
|
||||
return verify(hash, password);
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
import { PrismaClient } from '@prisma/client';
|
||||
import { log } from 'src/lib/logger';
|
||||
import { log } from '@/lib/logger';
|
||||
|
||||
let prisma: PrismaClient;
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import { Migrate } from '@prisma/migrate/dist/Migrate';
|
||||
import { ensureDatabaseExists } from '@prisma/migrate/dist/utils/ensureDatabaseExists';
|
||||
import { log } from 'lib/logger';
|
||||
import { log } from '@/lib/logger';
|
||||
|
||||
export async function runMigrations() {
|
||||
const migrate = new Migrate('./prisma/schema.prisma');
|
45
src/lib/db/queries/user.ts
Normal file
45
src/lib/db/queries/user.ts
Normal file
|
@ -0,0 +1,45 @@
|
|||
import { Prisma } from '@prisma/client';
|
||||
import { prisma } from '..';
|
||||
|
||||
export type User = {
|
||||
id: string;
|
||||
username: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
administrator: boolean;
|
||||
avatar?: string | null;
|
||||
password?: string | null;
|
||||
};
|
||||
|
||||
export async function getUser(
|
||||
where: Prisma.UserWhereInput | Prisma.UserWhereUniqueInput,
|
||||
options?: { password?: boolean; avatar?: boolean }
|
||||
): Promise<User | null> {
|
||||
return prisma.user.findFirst({
|
||||
where,
|
||||
select: {
|
||||
administrator: true,
|
||||
avatar: options?.avatar || false,
|
||||
id: true,
|
||||
createdAt: true,
|
||||
updatedAt: true,
|
||||
password: options?.password || false,
|
||||
username: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export async function getUserTokenRaw(
|
||||
where: Prisma.UserWhereInput | Prisma.UserWhereUniqueInput
|
||||
): Promise<string | null> {
|
||||
const user = await prisma.user.findFirst({
|
||||
where,
|
||||
select: {
|
||||
token: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!user) return null;
|
||||
|
||||
return user.token;
|
||||
}
|
12
src/lib/db/queries/zipline.ts
Normal file
12
src/lib/db/queries/zipline.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
import { prisma } from '..';
|
||||
|
||||
export async function getZipline() {
|
||||
const zipline = await prisma.zipline.findFirst();
|
||||
if (!zipline) {
|
||||
return prisma.zipline.create({
|
||||
data: {},
|
||||
});
|
||||
}
|
||||
|
||||
return zipline;
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
import dayjs from 'dayjs';
|
||||
import { blue, green, red, yellow, gray, white, bold } from 'colorette';
|
||||
import { green, red, yellow, gray, white, bold } from 'colorette';
|
||||
|
||||
export type LoggerLevel = 'info' | 'warn' | 'error' | 'debug' | 'trace';
|
||||
|
||||
|
|
10
src/lib/middleware/combine.ts
Normal file
10
src/lib/middleware/combine.ts
Normal file
|
@ -0,0 +1,10 @@
|
|||
import { NextApiReq, NextApiRes } from '../response';
|
||||
|
||||
export function combine(middleware: Middleware[], handler: Handler) {
|
||||
return middleware.reduceRight((handler, middleware) => {
|
||||
return middleware(handler);
|
||||
}, handler);
|
||||
}
|
||||
|
||||
export type Middleware = (...args: any[]) => Handler;
|
||||
export type Handler = (req: NextApiReq, res: NextApiRes) => Promise<any>;
|
19
src/lib/middleware/cors.ts
Normal file
19
src/lib/middleware/cors.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
import { NextApiReq, NextApiRes } from '../response';
|
||||
import { Handler } from './combine';
|
||||
|
||||
export function cors() {
|
||||
return (handler: Handler) => {
|
||||
return async (req: NextApiReq, res: NextApiRes) => {
|
||||
res.setHeader('Access-Control-Allow-Origin', '*');
|
||||
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE, OPTIONS');
|
||||
res.setHeader('Access-Control-Allow-Headers', 'Authorization, Content-Type, Accept');
|
||||
res.setHeader('Access-Control-Max-Age', '86400');
|
||||
|
||||
if (req.method === 'OPTIONS') {
|
||||
return res.status(200).end();
|
||||
}
|
||||
|
||||
return handler(req, res);
|
||||
};
|
||||
};
|
||||
}
|
19
src/lib/middleware/method.ts
Normal file
19
src/lib/middleware/method.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
import { NextApiReq, NextApiRes, methodNotAllowed } from '../response';
|
||||
import { Handler } from './combine';
|
||||
|
||||
export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS';
|
||||
|
||||
export function method(allowedMethods: HttpMethod[] = []) {
|
||||
return (handler: Handler) => {
|
||||
return async (req: NextApiReq, res: NextApiRes) => {
|
||||
allowedMethods.push('OPTIONS');
|
||||
|
||||
if (!allowedMethods.includes(req.method as HttpMethod)) {
|
||||
res.setHeader('Allow', allowedMethods.join(', '));
|
||||
return methodNotAllowed(res);
|
||||
}
|
||||
|
||||
return handler(req, res);
|
||||
};
|
||||
};
|
||||
}
|
40
src/lib/middleware/ziplineAuth.ts
Normal file
40
src/lib/middleware/ziplineAuth.ts
Normal file
|
@ -0,0 +1,40 @@
|
|||
import { config } from '../config';
|
||||
import { decryptToken } from '../crypto';
|
||||
import { prisma } from '../db';
|
||||
import { NextApiReq, NextApiRes, forbidden, unauthorized } from '../response';
|
||||
import { Handler } from './combine';
|
||||
|
||||
export type ZiplineAuthOptions = {
|
||||
administratorOnly?: boolean;
|
||||
};
|
||||
|
||||
export function ziplineAuth(options: ZiplineAuthOptions) {
|
||||
return (handler: Handler) => {
|
||||
return async (req: NextApiReq, res: NextApiRes) => {
|
||||
let rawToken: string | undefined;
|
||||
|
||||
if (req.cookies.zipline_auth) rawToken = req.cookies.zipline_auth;
|
||||
else if (req.headers.authorization) rawToken = req.headers.authorization;
|
||||
|
||||
if (!rawToken) return unauthorized(res);
|
||||
|
||||
const [date, token] = decryptToken(rawToken, config.core.secret);
|
||||
|
||||
if (isNaN(new Date(date).getTime())) return unauthorized(res);
|
||||
|
||||
const user = await prisma.user.findUnique({
|
||||
where: {
|
||||
token,
|
||||
},
|
||||
});
|
||||
|
||||
if (!user) return unauthorized(res);
|
||||
|
||||
req.user = user;
|
||||
|
||||
if (options.administratorOnly && !user.administrator) return forbidden(res);
|
||||
|
||||
return handler(req, res);
|
||||
};
|
||||
};
|
||||
}
|
96
src/lib/response.ts
Normal file
96
src/lib/response.ts
Normal file
|
@ -0,0 +1,96 @@
|
|||
import { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { User } from './db/queries/user';
|
||||
|
||||
export interface NextApiReq<Body = any, Query = any, Headers = any> extends NextApiRequest {
|
||||
query: Query & { [k: string]: string | string[] };
|
||||
body: Body;
|
||||
headers: Headers & { [k: string]: string };
|
||||
|
||||
user?: User;
|
||||
}
|
||||
|
||||
export type NextApiRes<Data = any> = NextApiResponse<Data>;
|
||||
|
||||
export function ok(res: NextApiRes, data: Record<string, unknown> = {}) {
|
||||
return res.status(200).json(data);
|
||||
}
|
||||
|
||||
// Client wrong data, etc
|
||||
export function badRequest(
|
||||
res: NextApiRes,
|
||||
message: string = 'Bad Request',
|
||||
data: Record<string, unknown> = {}
|
||||
) {
|
||||
return res.status(400).json({
|
||||
error: message,
|
||||
...data,
|
||||
});
|
||||
}
|
||||
|
||||
// No authorization
|
||||
export function unauthorized(
|
||||
res: NextApiRes,
|
||||
message: string = 'Unauthorized',
|
||||
data: Record<string, unknown> = {}
|
||||
) {
|
||||
return res.status(401).json({
|
||||
error: message,
|
||||
...data,
|
||||
});
|
||||
}
|
||||
|
||||
// User's permission level does not meet requirements for this resource
|
||||
export function forbidden(
|
||||
res: NextApiRes,
|
||||
message: string = 'Forbidden',
|
||||
data: Record<string, unknown> = {}
|
||||
) {
|
||||
return res.status(403).json({
|
||||
error: message,
|
||||
...data,
|
||||
});
|
||||
}
|
||||
|
||||
export function notFound(res: NextApiRes, message: string = 'Not Found', data: Record<string, unknown> = {}) {
|
||||
return res.status(404).json({
|
||||
error: message,
|
||||
...data,
|
||||
});
|
||||
}
|
||||
|
||||
export function ratelimited(
|
||||
res: NextApiRes,
|
||||
retryAfter: number,
|
||||
message: string = 'Ratelimited',
|
||||
data: Record<string, unknown> = {}
|
||||
) {
|
||||
res.setHeader('Retry-After', retryAfter);
|
||||
return res.status(429).json({
|
||||
error: message,
|
||||
retryAfter,
|
||||
...data,
|
||||
});
|
||||
}
|
||||
|
||||
export function serverError(
|
||||
res: NextApiRes,
|
||||
message: string = 'Internal Server Error',
|
||||
data: Record<string, unknown> = {}
|
||||
) {
|
||||
return res.status(500).json({
|
||||
error: message,
|
||||
...data,
|
||||
});
|
||||
}
|
||||
|
||||
export function methodNotAllowed(
|
||||
res: NextApiRes,
|
||||
message: string = 'Method Not Allowed',
|
||||
data: Record<string, unknown> = {}
|
||||
) {
|
||||
return res.status(405).json({
|
||||
error: message,
|
||||
method: res.req?.method || 'unknown',
|
||||
...data,
|
||||
});
|
||||
}
|
26
src/pages/_app.tsx
Normal file
26
src/pages/_app.tsx
Normal file
|
@ -0,0 +1,26 @@
|
|||
import { AppProps } from 'next/app';
|
||||
import Head from 'next/head';
|
||||
import { MantineProvider } from '@mantine/core';
|
||||
|
||||
export default function App(props: AppProps) {
|
||||
const { Component, pageProps } = props;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>Zipline</title>
|
||||
<meta name='viewport' content='minimum-scale=1, initial-scale=1, width=device-width' />
|
||||
</Head>
|
||||
|
||||
<MantineProvider
|
||||
withGlobalStyles
|
||||
withNormalizeCSS
|
||||
theme={{
|
||||
colorScheme: 'dark',
|
||||
}}
|
||||
>
|
||||
<Component {...pageProps} />
|
||||
</MantineProvider>
|
||||
</>
|
||||
);
|
||||
}
|
20
src/pages/_document.tsx
Normal file
20
src/pages/_document.tsx
Normal file
|
@ -0,0 +1,20 @@
|
|||
import { createGetInitialProps } from '@mantine/next';
|
||||
import Document, { Head, Html, Main, NextScript } from 'next/document';
|
||||
|
||||
const getInitialProps = createGetInitialProps();
|
||||
|
||||
export default class _Document extends Document {
|
||||
static getInitialProps = getInitialProps;
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Html>
|
||||
<Head />
|
||||
<body>
|
||||
<Main />
|
||||
<NextScript />
|
||||
</body>
|
||||
</Html>
|
||||
);
|
||||
}
|
||||
}
|
56
src/pages/api/auth/login.ts
Normal file
56
src/pages/api/auth/login.ts
Normal file
|
@ -0,0 +1,56 @@
|
|||
import { config } from '@/lib/config';
|
||||
import { serializeCookie } from '@/lib/cookie';
|
||||
import { encryptToken, verifyPassword } from '@/lib/crypto';
|
||||
import { User, getUser, getUserTokenRaw } from '@/lib/db/queries/user';
|
||||
import { combine } from '@/lib/middleware/combine';
|
||||
import { cors } from '@/lib/middleware/cors';
|
||||
import { method } from '@/lib/middleware/method';
|
||||
import { NextApiReq, NextApiRes, badRequest, ok } from '@/lib/response';
|
||||
|
||||
type Data = {
|
||||
user: User;
|
||||
token: string;
|
||||
};
|
||||
|
||||
type Body = {
|
||||
username: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
async function handler(req: NextApiReq<Body>, res: NextApiRes<Data>) {
|
||||
const { username, password } = req.body;
|
||||
|
||||
if (!username) return badRequest(res, 'Username is required');
|
||||
if (!password) return badRequest(res, 'Password is required');
|
||||
|
||||
const user = await getUser({ username }, { password: true });
|
||||
if (!user) return badRequest(res, 'Invalid username');
|
||||
|
||||
if (!user.password) return badRequest(res, 'User does not have a password, login through a provider');
|
||||
const valid = await verifyPassword(password, user.password);
|
||||
if (!valid) return badRequest(res, 'Invalid password');
|
||||
|
||||
const rawToken = await getUserTokenRaw({ id: user.id });
|
||||
if (!rawToken) return badRequest(res, 'User does not have a token');
|
||||
|
||||
const token = encryptToken(rawToken, config.core.secret);
|
||||
|
||||
const cookie = serializeCookie('zipline_token', token, {
|
||||
// week
|
||||
maxAge: 60 * 60 * 24 * 7,
|
||||
expires: new Date(Date.now() + 60 * 60 * 24 * 7 * 1000),
|
||||
path: '/',
|
||||
sameSite: 'lax',
|
||||
});
|
||||
|
||||
res.setHeader('Set-Cookie', cookie);
|
||||
|
||||
delete user.password;
|
||||
|
||||
return ok(res, {
|
||||
user,
|
||||
token,
|
||||
});
|
||||
}
|
||||
|
||||
export default combine([cors(), method(['POST'])], handler);
|
51
src/pages/api/healthcheck.ts
Normal file
51
src/pages/api/healthcheck.ts
Normal file
|
@ -0,0 +1,51 @@
|
|||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
|
||||
import { prisma } from '@/lib/db';
|
||||
import { log } from '@/lib/logger';
|
||||
import { badRequest, ok, ratelimited, serverError } from '@/lib/response';
|
||||
import { combine } from '@/lib/middleware/combine';
|
||||
import { cors } from '@/lib/middleware/cors';
|
||||
import { method } from '@/lib/middleware/method';
|
||||
|
||||
type Data = {
|
||||
pass: boolean;
|
||||
};
|
||||
|
||||
const ratelimit: Map<string, number> = new Map();
|
||||
|
||||
export async function handler(req: NextApiRequest, res: NextApiResponse<Data>) {
|
||||
const logger = log('api').c('healthcheck');
|
||||
|
||||
const ip = (req.headers['x-forwarded-for'] as string) || req.socket.remoteAddress;
|
||||
if (!ip) {
|
||||
logger.debug(`request without an ip address blocked`);
|
||||
return badRequest(res, 'no ip address found');
|
||||
}
|
||||
|
||||
const last = ratelimit.get(ip);
|
||||
|
||||
if (last) {
|
||||
if (last && Date.now() - last < 10000) {
|
||||
logger.debug(`request from ${ip} blocked due to ratelimit`);
|
||||
return ratelimited(res, Math.ceil((last + 10000 - Date.now()) / 1000));
|
||||
} else {
|
||||
ratelimit.delete(ip);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await prisma.$queryRaw`SELECT 1;`;
|
||||
ratelimit.set(ip, Date.now());
|
||||
|
||||
return ok(res, { pass: true });
|
||||
} catch (e) {
|
||||
logger.error('there was an error during a healthcheck').error(e);
|
||||
ratelimit.set(ip, Date.now());
|
||||
|
||||
return serverError(res, 'there was an error during a healthcheck', {
|
||||
pass: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default combine([cors(), method(['GET'])], handler);
|
74
src/pages/api/setup.ts
Normal file
74
src/pages/api/setup.ts
Normal file
|
@ -0,0 +1,74 @@
|
|||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
|
||||
import { prisma } from '@/lib/db';
|
||||
import { log } from '@/lib/logger';
|
||||
import { getZipline } from '@/lib/db/queries/zipline';
|
||||
import { NextApiReq, NextApiRes, badRequest, forbidden, methodNotAllowed, ok } from '@/lib/response';
|
||||
import { combine } from '@/lib/middleware/combine';
|
||||
import { cors } from '@/lib/middleware/cors';
|
||||
import { method } from '@/lib/middleware/method';
|
||||
import { createToken, hashPassword } from '@/lib/crypto';
|
||||
import { User } from '@/lib/db/queries/user';
|
||||
|
||||
type Response = {
|
||||
firstSetup: boolean;
|
||||
user: User;
|
||||
};
|
||||
|
||||
type Body = {
|
||||
username: string;
|
||||
password: string;
|
||||
};
|
||||
|
||||
export async function handler(req: NextApiReq<Body>, res: NextApiRes<Response>) {
|
||||
const logger = log('api').c('setup');
|
||||
const { firstSetup, id } = await getZipline();
|
||||
|
||||
if (!firstSetup) return forbidden(res);
|
||||
|
||||
logger.info('first setup running');
|
||||
|
||||
if (req.method === 'GET') {
|
||||
return ok(res, { firstSetup });
|
||||
}
|
||||
|
||||
const { username, password } = req.body;
|
||||
if (!username) return badRequest(res, 'Username is required');
|
||||
if (!password) return badRequest(res, 'Password is required');
|
||||
|
||||
if (password.length < 8) return badRequest(res, 'Password must be at least 8 characters long');
|
||||
|
||||
const user = await prisma.user.create({
|
||||
data: {
|
||||
username,
|
||||
password: await hashPassword(password),
|
||||
administrator: true,
|
||||
token: createToken(),
|
||||
},
|
||||
select: {
|
||||
administrator: true,
|
||||
id: true,
|
||||
createdAt: true,
|
||||
updatedAt: true,
|
||||
username: true,
|
||||
},
|
||||
});
|
||||
|
||||
logger.info('first setup complete');
|
||||
|
||||
await prisma.zipline.update({
|
||||
where: {
|
||||
id,
|
||||
},
|
||||
data: {
|
||||
firstSetup: false,
|
||||
},
|
||||
});
|
||||
|
||||
return ok(res, {
|
||||
firstSetup,
|
||||
user,
|
||||
});
|
||||
}
|
||||
|
||||
export default combine([cors(), method(['GET', 'POST'])], handler);
|
13
src/pages/api/user/index.ts
Normal file
13
src/pages/api/user/index.ts
Normal file
|
@ -0,0 +1,13 @@
|
|||
import { User } from '@/lib/db/queries/user';
|
||||
import { combine } from '@/lib/middleware/combine';
|
||||
import { cors } from '@/lib/middleware/cors';
|
||||
import { method } from '@/lib/middleware/method';
|
||||
import { NextApiReq, NextApiRes } from '@/lib/response';
|
||||
|
||||
type Response = {
|
||||
user: User;
|
||||
};
|
||||
|
||||
export async function handler(req: NextApiReq, res: NextApiRes<Response>) {}
|
||||
|
||||
export default combine([cors(), method(['GET', 'POST', 'PATCH'])], handler);
|
13
src/pages/auth/login.tsx
Normal file
13
src/pages/auth/login.tsx
Normal file
|
@ -0,0 +1,13 @@
|
|||
import { Box, Center, Text, TextInput, Title } from '@mantine/core';
|
||||
|
||||
export default function Login() {
|
||||
return (
|
||||
<Box py='xl'>
|
||||
<Center>
|
||||
<Title order={1}>
|
||||
<b>Zipline</b>
|
||||
</Title>
|
||||
</Center>
|
||||
</Box>
|
||||
);
|
||||
}
|
13
src/pages/index.tsx
Normal file
13
src/pages/index.tsx
Normal file
|
@ -0,0 +1,13 @@
|
|||
import Head from 'next/head';
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>Zipline</title>
|
||||
<meta name='viewport' content='width=device-width, initial-scale=1' />
|
||||
</Head>
|
||||
<main>hi</main>
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -1,58 +1,50 @@
|
|||
import { validateEnv } from '@/lib/config/validate';
|
||||
import { readEnv } from '@/lib/config/read';
|
||||
import { createToken, decryptToken, encryptToken } from '@/lib/crypto';
|
||||
import { runMigrations } from '@/lib/db/migration';
|
||||
import { log } from '@/lib/logger';
|
||||
import express from 'express';
|
||||
import { join } from 'path';
|
||||
import { createRequestHandler } from '@remix-run/express';
|
||||
import { convertEnv } from 'src/lib/config/convert';
|
||||
import { log } from 'src/lib/logger';
|
||||
import { readEnv } from 'src/lib/config/read';
|
||||
import { runMigrations } from 'src/lib/migration';
|
||||
import next from 'next';
|
||||
import { parse } from 'url';
|
||||
|
||||
const MODE = process.env.NODE_ENV || 'production';
|
||||
const BUILD_DIR = join(process.cwd(), 'build');
|
||||
|
||||
const logger = log('server');
|
||||
|
||||
logger.info(`starting zipline in ${MODE} mode`);
|
||||
async function main() {
|
||||
logger.info(`starting zipline in ${MODE} mode`);
|
||||
|
||||
runMigrations().then(() => {});
|
||||
const server = express();
|
||||
|
||||
const server = express();
|
||||
const config = convertEnv(readEnv());
|
||||
logger.info('reading environment for configuration');
|
||||
const config = validateEnv(readEnv());
|
||||
|
||||
server.disable('x-powered-by');
|
||||
process.env.DATABASE_URL = config.core.databaseUrl;
|
||||
|
||||
server.use('/modules', express.static('public/build', { maxAge: '1y', immutable: true }));
|
||||
server.use(express.static('public', { maxAge: '1h' }));
|
||||
await runMigrations();
|
||||
|
||||
server.all(
|
||||
'*',
|
||||
MODE === 'production'
|
||||
? createRequestHandler({ build: require(BUILD_DIR) })
|
||||
: (...args) => {
|
||||
purgeRequireCache();
|
||||
const requestHandler = createRequestHandler({
|
||||
build: require(BUILD_DIR),
|
||||
mode: MODE,
|
||||
getLoadContext() {
|
||||
return {
|
||||
config,
|
||||
};
|
||||
},
|
||||
});
|
||||
return requestHandler(...args);
|
||||
}
|
||||
);
|
||||
server.disable('x-powered-by');
|
||||
server.use(express.static('public', { maxAge: '1h' }));
|
||||
|
||||
server.listen(3000, () => {
|
||||
require(BUILD_DIR);
|
||||
const app = next({
|
||||
dev: MODE === 'development',
|
||||
quiet: MODE === 'production',
|
||||
hostname: config.core.hostname,
|
||||
port: config.core.port,
|
||||
dir: '.',
|
||||
});
|
||||
const handle = app.getRequestHandler();
|
||||
|
||||
logger.info(`server listening on port ${config.core.port}`);
|
||||
});
|
||||
await app.prepare();
|
||||
|
||||
function purgeRequireCache() {
|
||||
for (const key in require.cache) {
|
||||
if (key.startsWith(BUILD_DIR)) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
||||
delete require.cache[key];
|
||||
}
|
||||
}
|
||||
server.all('*', (req, res) => {
|
||||
const parsedUrl = parse(req.url!, true);
|
||||
return handle(req, res, parsedUrl);
|
||||
});
|
||||
|
||||
server.listen(config.core.port, config.core.hostname, () => {
|
||||
logger.info(`server listening on port ${config.core.port}`);
|
||||
});
|
||||
}
|
||||
|
||||
main();
|
||||
|
|
|
@ -1,22 +1,23 @@
|
|||
{
|
||||
"include": ["remix.env.d.ts", "**/*.ts", "**/*.tsx"],
|
||||
"compilerOptions": {
|
||||
"lib": ["DOM", "DOM.Iterable", "ES2019"],
|
||||
"esModuleInterop": true,
|
||||
"jsx": "react-jsx",
|
||||
"moduleResolution": "node",
|
||||
"target": "esnext",
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"~/*": ["src/app/*"],
|
||||
"lib/*": ["src/lib/*"]
|
||||
},
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"isolatedModules": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"noEmit": true
|
||||
}
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"incremental": true,
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
|
657
yarn.lock
657
yarn.lock
|
@ -3440,6 +3440,20 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@mantine/next@npm:^6.0.14":
|
||||
version: 6.0.14
|
||||
resolution: "@mantine/next@npm:6.0.14"
|
||||
dependencies:
|
||||
"@mantine/ssr": 6.0.14
|
||||
"@mantine/styles": 6.0.14
|
||||
peerDependencies:
|
||||
next: "*"
|
||||
react: ">=16.8.0"
|
||||
react-dom: ">=16.8.0"
|
||||
checksum: d610f6f353b5184610fd2f1cfd51de47d9a7f2006f9c74661c7911ff64e86c41876404e6642a76fb549600579d9a164b4f0b1de645ceeec78094a6ab922a4525
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@mantine/notifications@npm:^6.0.14":
|
||||
version: 6.0.14
|
||||
resolution: "@mantine/notifications@npm:6.0.14"
|
||||
|
@ -3455,33 +3469,17 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@mantine/prism@npm:^6.0.14":
|
||||
"@mantine/nprogress@npm:^6.0.14":
|
||||
version: 6.0.14
|
||||
resolution: "@mantine/prism@npm:6.0.14"
|
||||
resolution: "@mantine/nprogress@npm:6.0.14"
|
||||
dependencies:
|
||||
"@mantine/utils": 6.0.14
|
||||
prism-react-renderer: ^1.2.1
|
||||
peerDependencies:
|
||||
"@mantine/core": 6.0.14
|
||||
"@mantine/hooks": 6.0.14
|
||||
react: ">=16.8.0"
|
||||
react-dom: ">=16.8.0"
|
||||
checksum: a5e554a4ff310949ee3104e50f7d7b9086046b2a2fdaa705ec6b18c152963f5d2a416e7a165a33f5b053c23a0cf7c95cc2bdc0b9ddd34b0abf9efacb91351c2c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@mantine/remix@npm:^6.0.14":
|
||||
version: 6.0.14
|
||||
resolution: "@mantine/remix@npm:6.0.14"
|
||||
dependencies:
|
||||
"@mantine/ssr": 6.0.14
|
||||
"@mantine/styles": 6.0.14
|
||||
peerDependencies:
|
||||
"@mantine/core": 6.0.14
|
||||
"@mantine/hooks": 6.0.14
|
||||
react: ">=16.8.0"
|
||||
react-dom: ">=16.8.0"
|
||||
checksum: 52a131ebd7369ea240dd96464263e2f4440073ce32cddaa0810f5157924223828715a9644058a1d108e7a6bb56827ead474e110002f37dd771a6043bcb854219
|
||||
checksum: a26d216f3869624a512409d148f5eea6edeee1f29688ef8113f7a5229e051ed3d5d0bed9a13746a53d2d490e391cd8ca5a5ea22133aa868532d920a498620722
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
@ -3523,6 +3521,95 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@mapbox/node-pre-gyp@npm:^1.0.10":
|
||||
version: 1.0.10
|
||||
resolution: "@mapbox/node-pre-gyp@npm:1.0.10"
|
||||
dependencies:
|
||||
detect-libc: ^2.0.0
|
||||
https-proxy-agent: ^5.0.0
|
||||
make-dir: ^3.1.0
|
||||
node-fetch: ^2.6.7
|
||||
nopt: ^5.0.0
|
||||
npmlog: ^5.0.1
|
||||
rimraf: ^3.0.2
|
||||
semver: ^7.3.5
|
||||
tar: ^6.1.11
|
||||
bin:
|
||||
node-pre-gyp: bin/node-pre-gyp
|
||||
checksum: 1a98db05d955b74dad3814679593df293b9194853698f3f5f1ed00ecd93128cdd4b14fb8767fe44ac6981ef05c23effcfdc88710e7c1de99ccb6f647890597c8
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@next/env@npm:13.4.7":
|
||||
version: 13.4.7
|
||||
resolution: "@next/env@npm:13.4.7"
|
||||
checksum: 5a2bba68fb8c80c87324025f10af7fe7319efdb15777247bfa8ff58e61bcc19b150bce4068396351e6c6df3344294cc06c03a2fb1bb0330659d230830a202c53
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@next/swc-darwin-arm64@npm:13.4.7":
|
||||
version: 13.4.7
|
||||
resolution: "@next/swc-darwin-arm64@npm:13.4.7"
|
||||
conditions: os=darwin & cpu=arm64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@next/swc-darwin-x64@npm:13.4.7":
|
||||
version: 13.4.7
|
||||
resolution: "@next/swc-darwin-x64@npm:13.4.7"
|
||||
conditions: os=darwin & cpu=x64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@next/swc-linux-arm64-gnu@npm:13.4.7":
|
||||
version: 13.4.7
|
||||
resolution: "@next/swc-linux-arm64-gnu@npm:13.4.7"
|
||||
conditions: os=linux & cpu=arm64 & libc=glibc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@next/swc-linux-arm64-musl@npm:13.4.7":
|
||||
version: 13.4.7
|
||||
resolution: "@next/swc-linux-arm64-musl@npm:13.4.7"
|
||||
conditions: os=linux & cpu=arm64 & libc=musl
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@next/swc-linux-x64-gnu@npm:13.4.7":
|
||||
version: 13.4.7
|
||||
resolution: "@next/swc-linux-x64-gnu@npm:13.4.7"
|
||||
conditions: os=linux & cpu=x64 & libc=glibc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@next/swc-linux-x64-musl@npm:13.4.7":
|
||||
version: 13.4.7
|
||||
resolution: "@next/swc-linux-x64-musl@npm:13.4.7"
|
||||
conditions: os=linux & cpu=x64 & libc=musl
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@next/swc-win32-arm64-msvc@npm:13.4.7":
|
||||
version: 13.4.7
|
||||
resolution: "@next/swc-win32-arm64-msvc@npm:13.4.7"
|
||||
conditions: os=win32 & cpu=arm64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@next/swc-win32-ia32-msvc@npm:13.4.7":
|
||||
version: 13.4.7
|
||||
resolution: "@next/swc-win32-ia32-msvc@npm:13.4.7"
|
||||
conditions: os=win32 & cpu=ia32
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@next/swc-win32-x64-msvc@npm:13.4.7":
|
||||
version: 13.4.7
|
||||
resolution: "@next/swc-win32-x64-msvc@npm:13.4.7"
|
||||
conditions: os=win32 & cpu=x64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@nicolo-ribaudo/eslint-scope-5-internals@npm:5.1.1-v1":
|
||||
version: 5.1.1-v1
|
||||
resolution: "@nicolo-ribaudo/eslint-scope-5-internals@npm:5.1.1-v1"
|
||||
|
@ -3604,6 +3691,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@phc/format@npm:^1.0.0":
|
||||
version: 1.0.0
|
||||
resolution: "@phc/format@npm:1.0.0"
|
||||
checksum: 15ee02504fbc16590923d89b1f1c2f5892df27cf2bf19180e5678511413e87b6e5355815a092749cd01698855ee5a0fc5d2393951c727acd650934eed290e26e
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@pkgjs/parseargs@npm:^0.11.0":
|
||||
version: 0.11.0
|
||||
resolution: "@pkgjs/parseargs@npm:0.11.0"
|
||||
|
@ -4041,47 +4135,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@remix-run/express@npm:^1.17.1":
|
||||
version: 1.17.1
|
||||
resolution: "@remix-run/express@npm:1.17.1"
|
||||
dependencies:
|
||||
"@remix-run/node": 1.17.1
|
||||
peerDependencies:
|
||||
express: ^4.17.1
|
||||
checksum: b6109837f0f6d5c91794e137c68ea25eec40e43bcdecf63c6c80a3b38b1c27d34f06c91da54df6e3f2d16e6d65979f743fe58dd18bc85d2fe8bd7e38ef72a13f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@remix-run/node@npm:1.17.1, @remix-run/node@npm:^1.16.1":
|
||||
version: 1.17.1
|
||||
resolution: "@remix-run/node@npm:1.17.1"
|
||||
dependencies:
|
||||
"@remix-run/server-runtime": 1.17.1
|
||||
"@remix-run/web-fetch": ^4.3.4
|
||||
"@remix-run/web-file": ^3.0.2
|
||||
"@remix-run/web-stream": ^1.0.3
|
||||
"@web3-storage/multipart-parser": ^1.0.0
|
||||
abort-controller: ^3.0.0
|
||||
cookie-signature: ^1.1.0
|
||||
source-map-support: ^0.5.21
|
||||
stream-slice: ^0.1.2
|
||||
checksum: 4e4d984ca1dc01f7c50983f99fbbbc97cb0629cac4cedad3970fc3591207c8d1ff690cfa07451dc3b1f9ad30ae75200bb21006b3793548d89a64daa40d5bf5eb
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@remix-run/react@npm:^1.16.1":
|
||||
version: 1.17.1
|
||||
resolution: "@remix-run/react@npm:1.17.1"
|
||||
dependencies:
|
||||
"@remix-run/router": 1.6.3
|
||||
react-router-dom: 6.13.0
|
||||
peerDependencies:
|
||||
react: ">=16.8"
|
||||
react-dom: ">=16.8"
|
||||
checksum: 59f573c7d78af33b77ae4df76895ccb70cd5e11f73653567732a3a32dd92d192c984a1ccd3d178df214e66505e80a30b8b26b03bc829dc98ed4452db977a3c43
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@remix-run/router@npm:1.6.3":
|
||||
version: 1.6.3
|
||||
resolution: "@remix-run/router@npm:1.6.3"
|
||||
|
@ -4102,69 +4155,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@remix-run/v1-route-convention@npm:^0.1.2":
|
||||
version: 0.1.2
|
||||
resolution: "@remix-run/v1-route-convention@npm:0.1.2"
|
||||
dependencies:
|
||||
minimatch: ^7.4.3
|
||||
peerDependencies:
|
||||
"@remix-run/dev": ^1.15.0
|
||||
checksum: c057b4fd696e4bda869663be757fda2b647604ee65441882a2cf4aa5ea76e6630e293a4110817891cc8eab06574e04c60e6306f1e3b02dbfa21231882fc7a722
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@remix-run/web-blob@npm:^3.0.3, @remix-run/web-blob@npm:^3.0.4":
|
||||
version: 3.0.4
|
||||
resolution: "@remix-run/web-blob@npm:3.0.4"
|
||||
dependencies:
|
||||
"@remix-run/web-stream": ^1.0.0
|
||||
web-encoding: 1.1.5
|
||||
checksum: 07d9a71d1795e8973cdc59c1a325aaaae7b9099a96815849355a34667d7a953cac78a332e02b25e0722d4d7244b7fe6d7ce6fc854e8baf83e42e8403f4a321fd
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@remix-run/web-fetch@npm:^4.3.4":
|
||||
version: 4.3.4
|
||||
resolution: "@remix-run/web-fetch@npm:4.3.4"
|
||||
dependencies:
|
||||
"@remix-run/web-blob": ^3.0.4
|
||||
"@remix-run/web-form-data": ^3.0.3
|
||||
"@remix-run/web-stream": ^1.0.3
|
||||
"@web3-storage/multipart-parser": ^1.0.0
|
||||
abort-controller: ^3.0.0
|
||||
data-uri-to-buffer: ^3.0.1
|
||||
mrmime: ^1.0.0
|
||||
checksum: 0c3370bfde1867722654734cddfab90982ba657a4244bf8e7e4aa49cb5e3c0b3429ed94b760d6881b8bf45d93bf411f29b9ffaf19332ecdc0bae1fa42b9a06fb
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@remix-run/web-file@npm:^3.0.2":
|
||||
version: 3.0.2
|
||||
resolution: "@remix-run/web-file@npm:3.0.2"
|
||||
dependencies:
|
||||
"@remix-run/web-blob": ^3.0.3
|
||||
checksum: f3bda87b62648e3ef0c0049aa560318d64adf493566a6446eae5a9d15a6080eb0c8ba1f450d4d7bbfa6cbad8c8d6a7adf4e72d546a4305ce0c05f63a95f80db0
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@remix-run/web-form-data@npm:^3.0.3":
|
||||
version: 3.0.4
|
||||
resolution: "@remix-run/web-form-data@npm:3.0.4"
|
||||
dependencies:
|
||||
web-encoding: 1.1.5
|
||||
checksum: 75c4c07c3307081d17d63b6d26209c651e5ccb910b96fa467415dcceb3f5e7c82d18cc34f604ffc5e85e2d3e77b93b9c2ef670b97745deccfe03cf85647cca17
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@remix-run/web-stream@npm:^1.0.0, @remix-run/web-stream@npm:^1.0.3":
|
||||
version: 1.0.3
|
||||
resolution: "@remix-run/web-stream@npm:1.0.3"
|
||||
dependencies:
|
||||
web-streams-polyfill: ^3.1.1
|
||||
checksum: 61a76b9e4ddb364fa5faa8cf28484f39800bd9259d4c2b96c235bd436e539d8bb00d433fcf79730d7bdf103255473fe0ccfde5d3c20a152f738c5bb102b26377
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@rollup/pluginutils@npm:^4.0.0":
|
||||
version: 4.2.1
|
||||
resolution: "@rollup/pluginutils@npm:4.2.1"
|
||||
|
@ -4244,6 +4234,15 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@swc/helpers@npm:0.5.1":
|
||||
version: 0.5.1
|
||||
resolution: "@swc/helpers@npm:0.5.1"
|
||||
dependencies:
|
||||
tslib: ^2.4.0
|
||||
checksum: 71e0e27234590435e4c62b97ef5e796f88e786841a38c7116a5e27a3eafa7b9ead7cdec5249b32165902076de78446945311c973e59bddf77c1e24f33a8f272a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@szmarczak/http-timer@npm:^4.0.5":
|
||||
version: 4.0.6
|
||||
resolution: "@szmarczak/http-timer@npm:4.0.6"
|
||||
|
@ -4253,6 +4252,25 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@tabler/icons-react@npm:^2.22.0":
|
||||
version: 2.22.0
|
||||
resolution: "@tabler/icons-react@npm:2.22.0"
|
||||
dependencies:
|
||||
"@tabler/icons": 2.22.0
|
||||
prop-types: ^15.7.2
|
||||
peerDependencies:
|
||||
react: ^16.5.1 || ^17.0.0 || ^18.0.0
|
||||
checksum: fe2a4c3e5483269ee178195746cdc4b8c1e150dec78aae4bbf3884f33dd819929a7ff1e53eb6ba1426b914ba710e13a30d0e70caf9e588b8df45e500f70941dd
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@tabler/icons@npm:2.22.0":
|
||||
version: 2.22.0
|
||||
resolution: "@tabler/icons@npm:2.22.0"
|
||||
checksum: 3f0aaa801e8739d841ac5d335fbaee41399aaa9eeae03eb21c1dbe77877a29ba3664f16c225323a0635fad6548aca40212220edfb2919797454ea1029c5a5b78
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@tediousjs/connection-string@npm:^0.4.1":
|
||||
version: 0.4.2
|
||||
resolution: "@tediousjs/connection-string@npm:0.4.2"
|
||||
|
@ -4444,6 +4462,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/http-errors@npm:*":
|
||||
version: 2.0.1
|
||||
resolution: "@types/http-errors@npm:2.0.1"
|
||||
checksum: 3bb0c50b0a652e679a84c30cd0340d696c32ef6558518268c238840346c077f899315daaf1c26c09c57ddd5dc80510f2a7f46acd52bf949e339e35ed3ee9654f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/json-schema@npm:^7.0.9":
|
||||
version: 7.0.12
|
||||
resolution: "@types/json-schema@npm:7.0.12"
|
||||
|
@ -4621,12 +4646,13 @@ __metadata:
|
|||
linkType: hard
|
||||
|
||||
"@types/serve-static@npm:*":
|
||||
version: 1.15.1
|
||||
resolution: "@types/serve-static@npm:1.15.1"
|
||||
version: 1.15.2
|
||||
resolution: "@types/serve-static@npm:1.15.2"
|
||||
dependencies:
|
||||
"@types/http-errors": "*"
|
||||
"@types/mime": "*"
|
||||
"@types/node": "*"
|
||||
checksum: 2e078bdc1e458c7dfe69e9faa83cc69194b8896cce57cb745016580543c7ab5af07fdaa8ac1765eb79524208c81017546f66056f44d1204f812d72810613de36
|
||||
checksum: 15c261dbfc57890f7cc17c04d5b22b418dfa0330c912b46c5d8ae2064da5d6f844ef7f41b63c7f4bbf07675e97ebe6ac804b032635ec742ae45d6f1274259b3e
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
@ -4847,29 +4873,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@zxing/text-encoding@npm:0.9.0":
|
||||
version: 0.9.0
|
||||
resolution: "@zxing/text-encoding@npm:0.9.0"
|
||||
checksum: c23b12aee7639382e4949961304a1294776afaffa40f579e09ffecd0e5e68cf26ef3edd75009de46da8a536e571448755ca68b3e2ea707d53793c0edb2e2c34a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"abbrev@npm:^1.0.0":
|
||||
"abbrev@npm:1, abbrev@npm:^1.0.0":
|
||||
version: 1.1.1
|
||||
resolution: "abbrev@npm:1.1.1"
|
||||
checksum: a4a97ec07d7ea112c517036882b2ac22f3109b7b19077dc656316d07d308438aac28e4d9746dc4d84bf6b1e75b4a7b0a5f3cb30592419f128ca9a8cee3bcfa17
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"abort-controller@npm:^3.0.0":
|
||||
version: 3.0.0
|
||||
resolution: "abort-controller@npm:3.0.0"
|
||||
dependencies:
|
||||
event-target-shim: ^5.0.0
|
||||
checksum: 170bdba9b47b7e65906a28c8ce4f38a7a369d78e2271706f020849c1bfe0ee2067d4261df8bbb66eb84f79208fd5b710df759d64191db58cfba7ce8ef9c54b75
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"accepts@npm:~1.3.8":
|
||||
version: 1.3.8
|
||||
resolution: "accepts@npm:1.3.8"
|
||||
|
@ -5075,6 +5085,16 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"are-we-there-yet@npm:^2.0.0":
|
||||
version: 2.0.0
|
||||
resolution: "are-we-there-yet@npm:2.0.0"
|
||||
dependencies:
|
||||
delegates: ^1.0.0
|
||||
readable-stream: ^3.6.0
|
||||
checksum: 6c80b4fd04ecee6ba6e737e0b72a4b41bdc64b7d279edfc998678567ff583c8df27e27523bc789f2c99be603ffa9eaa612803da1d886962d2086e7ff6fa90c7c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"are-we-there-yet@npm:^3.0.0":
|
||||
version: 3.0.1
|
||||
resolution: "are-we-there-yet@npm:3.0.1"
|
||||
|
@ -5092,6 +5112,17 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"argon2@npm:^0.30.3":
|
||||
version: 0.30.3
|
||||
resolution: "argon2@npm:0.30.3"
|
||||
dependencies:
|
||||
"@mapbox/node-pre-gyp": ^1.0.10
|
||||
"@phc/format": ^1.0.0
|
||||
node-addon-api: ^5.0.0
|
||||
checksum: 36784f69af8adad1e0e155a0f1999320999a3b76fb41a3b8f4674e1d896dc65a6e76bea4385b9433e8f935d0912a9ca81088b5d8d22e5fe4119d5abf9d10761f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"argparse@npm:^2.0.1":
|
||||
version: 2.0.1
|
||||
resolution: "argparse@npm:2.0.1"
|
||||
|
@ -5576,6 +5607,15 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"busboy@npm:1.6.0":
|
||||
version: 1.6.0
|
||||
resolution: "busboy@npm:1.6.0"
|
||||
dependencies:
|
||||
streamsearch: ^1.1.0
|
||||
checksum: 32801e2c0164e12106bf236291a00795c3c4e4b709ae02132883fe8478ba2ae23743b11c5735a0aae8afe65ac4b6ca4568b91f0d9fed1fdbc32ede824a73746e
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"bytes@npm:3.1.2, bytes@npm:^3.1.2":
|
||||
version: 3.1.2
|
||||
resolution: "bytes@npm:3.1.2"
|
||||
|
@ -5675,6 +5715,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"caniuse-lite@npm:^1.0.30001406":
|
||||
version: 1.0.30001507
|
||||
resolution: "caniuse-lite@npm:1.0.30001507"
|
||||
checksum: 7044172bdf65140c927cdaaff50368a97676f06a9fd8b515c046613bdf52cb769e9efb832ee491b8f8cc21f82c15f154a896efbab690f431bb064c95f3a2b7a8
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"caniuse-lite@npm:^1.0.30001503":
|
||||
version: 1.0.30001506
|
||||
resolution: "caniuse-lite@npm:1.0.30001506"
|
||||
|
@ -5833,6 +5880,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"client-only@npm:0.0.1":
|
||||
version: 0.0.1
|
||||
resolution: "client-only@npm:0.0.1"
|
||||
checksum: 0c16bf660dadb90610553c1d8946a7fdfb81d624adea073b8440b7d795d5b5b08beb3c950c6a2cf16279365a3265158a236876d92bce16423c485c322d7dfaf8
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"clone-response@npm:^1.0.2":
|
||||
version: 1.0.3
|
||||
resolution: "clone-response@npm:1.0.3"
|
||||
|
@ -5888,7 +5942,7 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"color-support@npm:^1.1.3":
|
||||
"color-support@npm:^1.1.2, color-support@npm:^1.1.3":
|
||||
version: 1.1.3
|
||||
resolution: "color-support@npm:1.1.3"
|
||||
bin:
|
||||
|
@ -5960,7 +6014,7 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"console-control-strings@npm:^1.1.0":
|
||||
"console-control-strings@npm:^1.0.0, console-control-strings@npm:^1.1.0":
|
||||
version: 1.1.0
|
||||
resolution: "console-control-strings@npm:1.1.0"
|
||||
checksum: 8755d76787f94e6cf79ce4666f0c5519906d7f5b02d4b884cf41e11dcd759ed69c57da0670afd9236d229a46e0f9cf519db0cd829c6dca820bb5a5c3def584ed
|
||||
|
@ -5997,13 +6051,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"cookie-signature@npm:^1.1.0":
|
||||
version: 1.2.1
|
||||
resolution: "cookie-signature@npm:1.2.1"
|
||||
checksum: bb464aacac390b5d7d8ead2d6fff7c1c3b7378c7d0250921f48923fe889688e081ab33950448929db5f24d4f9f1506589a7ee1c685de8f12a3fdb30c49667ec5
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"cookie@npm:0.5.0":
|
||||
version: 0.5.0
|
||||
resolution: "cookie@npm:0.5.0"
|
||||
|
@ -6146,7 +6193,7 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"data-uri-to-buffer@npm:3, data-uri-to-buffer@npm:^3.0.1":
|
||||
"data-uri-to-buffer@npm:3":
|
||||
version: 3.0.1
|
||||
resolution: "data-uri-to-buffer@npm:3.0.1"
|
||||
checksum: c59c3009686a78c071806b72f4810856ec28222f0f4e252aa495ec027ed9732298ceea99c50328cf59b151dd34cbc3ad6150bbb43e41fc56fa19f48c99e9fc30
|
||||
|
@ -6404,6 +6451,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"detect-libc@npm:^2.0.0":
|
||||
version: 2.0.1
|
||||
resolution: "detect-libc@npm:2.0.1"
|
||||
checksum: ccb05fcabbb555beb544d48080179c18523a343face9ee4e1a86605a8715b4169f94d663c21a03c310ac824592f2ba9a5270218819bb411ad7be578a527593d7
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"detect-newline@npm:3.1.0":
|
||||
version: 3.1.0
|
||||
resolution: "detect-newline@npm:3.1.0"
|
||||
|
@ -7506,13 +7560,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"event-target-shim@npm:^5.0.0":
|
||||
version: 5.0.1
|
||||
resolution: "event-target-shim@npm:5.0.1"
|
||||
checksum: 1ffe3bb22a6d51bdeb6bf6f7cf97d2ff4a74b017ad12284cc9e6a279e727dc30a5de6bb613e5596ff4dc3e517841339ad09a7eec44266eccb1aa201a30448166
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"events@npm:^3.0.0":
|
||||
version: 3.3.0
|
||||
resolution: "events@npm:3.3.0"
|
||||
|
@ -8024,6 +8071,23 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"gauge@npm:^3.0.0":
|
||||
version: 3.0.2
|
||||
resolution: "gauge@npm:3.0.2"
|
||||
dependencies:
|
||||
aproba: ^1.0.3 || ^2.0.0
|
||||
color-support: ^1.1.2
|
||||
console-control-strings: ^1.0.0
|
||||
has-unicode: ^2.0.1
|
||||
object-assign: ^4.1.1
|
||||
signal-exit: ^3.0.0
|
||||
string-width: ^4.2.3
|
||||
strip-ansi: ^6.0.1
|
||||
wide-align: ^1.1.2
|
||||
checksum: 81296c00c7410cdd48f997800155fbead4f32e4f82109be0719c63edc8560e6579946cc8abd04205297640691ec26d21b578837fd13a4e96288ab4b40b1dc3e9
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"gauge@npm:^4.0.3":
|
||||
version: 4.0.4
|
||||
resolution: "gauge@npm:4.0.4"
|
||||
|
@ -8163,6 +8227,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"glob-to-regexp@npm:^0.4.1":
|
||||
version: 0.4.1
|
||||
resolution: "glob-to-regexp@npm:0.4.1"
|
||||
checksum: e795f4e8f06d2a15e86f76e4d92751cf8bbfcf0157cea5c2f0f35678a8195a750b34096b1256e436f0cebc1883b5ff0888c47348443e69546a5a87f9e1eb1167
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"glob@npm:7.1.6":
|
||||
version: 7.1.6
|
||||
resolution: "glob@npm:7.1.6"
|
||||
|
@ -8842,7 +8913,7 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"is-arguments@npm:^1.0.4, is-arguments@npm:^1.1.1":
|
||||
"is-arguments@npm:^1.1.1":
|
||||
version: 1.1.1
|
||||
resolution: "is-arguments@npm:1.1.1"
|
||||
dependencies:
|
||||
|
@ -8976,15 +9047,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"is-generator-function@npm:^1.0.7":
|
||||
version: 1.0.10
|
||||
resolution: "is-generator-function@npm:1.0.10"
|
||||
dependencies:
|
||||
has-tostringtag: ^1.0.0
|
||||
checksum: d54644e7dbaccef15ceb1e5d91d680eb5068c9ee9f9eb0a9e04173eb5542c9b51b5ab52c5537f5703e48d5fddfd376817c1ca07a84a407b7115b769d4bdde72b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"is-glob@npm:^4.0.0, is-glob@npm:^4.0.1, is-glob@npm:^4.0.3, is-glob@npm:~4.0.1":
|
||||
version: 4.0.3
|
||||
resolution: "is-glob@npm:4.0.3"
|
||||
|
@ -9165,7 +9227,7 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"is-typed-array@npm:^1.1.10, is-typed-array@npm:^1.1.3, is-typed-array@npm:^1.1.9":
|
||||
"is-typed-array@npm:^1.1.10, is-typed-array@npm:^1.1.9":
|
||||
version: 1.1.10
|
||||
resolution: "is-typed-array@npm:1.1.10"
|
||||
dependencies:
|
||||
|
@ -9248,13 +9310,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"isbot@npm:^3.6.10":
|
||||
version: 3.6.12
|
||||
resolution: "isbot@npm:3.6.12"
|
||||
checksum: e23782a6633bf60fc3db23171468fc3f4cdc36f5a707b0cf1f3b2aff2686bedb29d036411afec90a9de23901a251ff53596ab9ca31cc85223dd8f03392fc03e6
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"isexe@npm:^2.0.0":
|
||||
version: 2.0.0
|
||||
resolution: "isexe@npm:2.0.0"
|
||||
|
@ -9832,7 +9887,7 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"make-dir@npm:3.1.0, make-dir@npm:^3.0.0, make-dir@npm:^3.0.2":
|
||||
"make-dir@npm:3.1.0, make-dir@npm:^3.0.0, make-dir@npm:^3.0.2, make-dir@npm:^3.1.0":
|
||||
version: 3.1.0
|
||||
resolution: "make-dir@npm:3.1.0"
|
||||
dependencies:
|
||||
|
@ -10540,15 +10595,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"minimatch@npm:^7.4.3":
|
||||
version: 7.4.6
|
||||
resolution: "minimatch@npm:7.4.6"
|
||||
dependencies:
|
||||
brace-expansion: ^2.0.1
|
||||
checksum: 1a6c8d22618df9d2a88aabeef1de5622eb7b558e9f8010be791cb6b0fa6e102d39b11c28d75b855a1e377b12edc7db8ff12a99c20353441caa6a05e78deb5da9
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"minimatch@npm:^9.0.0, minimatch@npm:^9.0.1":
|
||||
version: 9.0.1
|
||||
resolution: "minimatch@npm:9.0.1"
|
||||
|
@ -10756,13 +10802,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"mrmime@npm:^1.0.0":
|
||||
version: 1.0.1
|
||||
resolution: "mrmime@npm:1.0.1"
|
||||
checksum: cc979da44bbbffebaa8eaf7a45117e851f2d4cb46a3ada6ceb78130466a04c15a0de9a9ce1c8b8ba6f6e1b8618866b1352992bf1757d241c0ddca558b9f28a77
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ms@npm:2.0.0":
|
||||
version: 2.0.0
|
||||
resolution: "ms@npm:2.0.0"
|
||||
|
@ -10828,7 +10867,7 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"nanoid@npm:^3.3.6":
|
||||
"nanoid@npm:^3.3.4, nanoid@npm:^3.3.6":
|
||||
version: 3.3.6
|
||||
resolution: "nanoid@npm:3.3.6"
|
||||
bin:
|
||||
|
@ -10879,6 +10918,65 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"next@npm:^13.4.7":
|
||||
version: 13.4.7
|
||||
resolution: "next@npm:13.4.7"
|
||||
dependencies:
|
||||
"@next/env": 13.4.7
|
||||
"@next/swc-darwin-arm64": 13.4.7
|
||||
"@next/swc-darwin-x64": 13.4.7
|
||||
"@next/swc-linux-arm64-gnu": 13.4.7
|
||||
"@next/swc-linux-arm64-musl": 13.4.7
|
||||
"@next/swc-linux-x64-gnu": 13.4.7
|
||||
"@next/swc-linux-x64-musl": 13.4.7
|
||||
"@next/swc-win32-arm64-msvc": 13.4.7
|
||||
"@next/swc-win32-ia32-msvc": 13.4.7
|
||||
"@next/swc-win32-x64-msvc": 13.4.7
|
||||
"@swc/helpers": 0.5.1
|
||||
busboy: 1.6.0
|
||||
caniuse-lite: ^1.0.30001406
|
||||
postcss: 8.4.14
|
||||
styled-jsx: 5.1.1
|
||||
watchpack: 2.4.0
|
||||
zod: 3.21.4
|
||||
peerDependencies:
|
||||
"@opentelemetry/api": ^1.1.0
|
||||
fibers: ">= 3.1.0"
|
||||
react: ^18.2.0
|
||||
react-dom: ^18.2.0
|
||||
sass: ^1.3.0
|
||||
dependenciesMeta:
|
||||
"@next/swc-darwin-arm64":
|
||||
optional: true
|
||||
"@next/swc-darwin-x64":
|
||||
optional: true
|
||||
"@next/swc-linux-arm64-gnu":
|
||||
optional: true
|
||||
"@next/swc-linux-arm64-musl":
|
||||
optional: true
|
||||
"@next/swc-linux-x64-gnu":
|
||||
optional: true
|
||||
"@next/swc-linux-x64-musl":
|
||||
optional: true
|
||||
"@next/swc-win32-arm64-msvc":
|
||||
optional: true
|
||||
"@next/swc-win32-ia32-msvc":
|
||||
optional: true
|
||||
"@next/swc-win32-x64-msvc":
|
||||
optional: true
|
||||
peerDependenciesMeta:
|
||||
"@opentelemetry/api":
|
||||
optional: true
|
||||
fibers:
|
||||
optional: true
|
||||
sass:
|
||||
optional: true
|
||||
bin:
|
||||
next: dist/bin/next
|
||||
checksum: 76026a5def68c00064bc4860cd15a5f292220ccc73ff24245b3658a90a46f66c290d3543a59e1cb91310145141d4ad1238d7cf652f41f47cdf434ab8705af7d1
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"nice-try@npm:^1.0.4":
|
||||
version: 1.0.5
|
||||
resolution: "nice-try@npm:1.0.5"
|
||||
|
@ -10902,7 +11000,16 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"node-fetch@npm:2.6.11, node-fetch@npm:^2.6.9":
|
||||
"node-addon-api@npm:^5.0.0":
|
||||
version: 5.1.0
|
||||
resolution: "node-addon-api@npm:5.1.0"
|
||||
dependencies:
|
||||
node-gyp: latest
|
||||
checksum: 2508bd2d2981945406243a7bd31362fc7af8b70b8b4d65f869c61731800058fb818cc2fd36c8eac714ddd0e568cc85becf5e165cebbdf7b5024d5151bbc75ea1
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"node-fetch@npm:2.6.11, node-fetch@npm:^2.6.7, node-fetch@npm:^2.6.9":
|
||||
version: 2.6.11
|
||||
resolution: "node-fetch@npm:2.6.11"
|
||||
dependencies:
|
||||
|
@ -10944,6 +11051,17 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"nopt@npm:^5.0.0":
|
||||
version: 5.0.0
|
||||
resolution: "nopt@npm:5.0.0"
|
||||
dependencies:
|
||||
abbrev: 1
|
||||
bin:
|
||||
nopt: bin/nopt.js
|
||||
checksum: d35fdec187269503843924e0114c0c6533fb54bbf1620d0f28b4b60ba01712d6687f62565c55cc20a504eff0fbe5c63e22340c3fad549ad40469ffb611b04f2f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"nopt@npm:^6.0.0":
|
||||
version: 6.0.0
|
||||
resolution: "nopt@npm:6.0.0"
|
||||
|
@ -11050,6 +11168,18 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"npmlog@npm:^5.0.1":
|
||||
version: 5.0.1
|
||||
resolution: "npmlog@npm:5.0.1"
|
||||
dependencies:
|
||||
are-we-there-yet: ^2.0.0
|
||||
console-control-strings: ^1.1.0
|
||||
gauge: ^3.0.0
|
||||
set-blocking: ^2.0.0
|
||||
checksum: 516b2663028761f062d13e8beb3f00069c5664925871a9b57989642ebe09f23ab02145bf3ab88da7866c4e112cafff72401f61a672c7c8a20edc585a7016ef5f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"npmlog@npm:^6.0.0":
|
||||
version: 6.0.2
|
||||
resolution: "npmlog@npm:6.0.2"
|
||||
|
@ -11835,6 +11965,17 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"postcss@npm:8.4.14":
|
||||
version: 8.4.14
|
||||
resolution: "postcss@npm:8.4.14"
|
||||
dependencies:
|
||||
nanoid: ^3.3.4
|
||||
picocolors: ^1.0.0
|
||||
source-map-js: ^1.0.2
|
||||
checksum: fe58766ff32e4becf65a7d57678995cfd239df6deed2fe0557f038b47c94e4132e7e5f68b5aa820c13adfec32e523b693efaeb65798efb995ce49ccd83953816
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"postcss@npm:^8.4.19, postcss@npm:^8.4.23":
|
||||
version: 8.4.24
|
||||
resolution: "postcss@npm:8.4.24"
|
||||
|
@ -11919,15 +12060,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"prism-react-renderer@npm:^1.2.1":
|
||||
version: 1.3.5
|
||||
resolution: "prism-react-renderer@npm:1.3.5"
|
||||
peerDependencies:
|
||||
react: ">=0.14.9"
|
||||
checksum: c18806dcbc4c0b4fd6fd15bd06b4f7c0a6da98d93af235c3e970854994eb9b59e23315abb6cfc29e69da26d36709a47e25da85ab27fed81b6812f0a52caf6dfa
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"prisma@npm:^4.16.1":
|
||||
version: 4.16.1
|
||||
resolution: "prisma@npm:4.16.1"
|
||||
|
@ -11981,7 +12113,7 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"prop-types@npm:^15.6.2, prop-types@npm:^15.8.1":
|
||||
"prop-types@npm:^15.6.2, prop-types@npm:^15.7.2, prop-types@npm:^15.8.1":
|
||||
version: 15.8.1
|
||||
resolution: "prop-types@npm:15.8.1"
|
||||
dependencies:
|
||||
|
@ -12212,30 +12344,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"react-router-dom@npm:6.13.0":
|
||||
version: 6.13.0
|
||||
resolution: "react-router-dom@npm:6.13.0"
|
||||
dependencies:
|
||||
"@remix-run/router": 1.6.3
|
||||
react-router: 6.13.0
|
||||
peerDependencies:
|
||||
react: ">=16.8"
|
||||
react-dom: ">=16.8"
|
||||
checksum: f51131063c2d5e127b6b3f3f813c6d4988d0f37694a06697dc9d4a4d9d3825e2a4487ec9b81a1d356eb269018814d884ffc2e3d9ff056a46ae59c99c9e7e1086
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"react-router@npm:6.13.0":
|
||||
version: 6.13.0
|
||||
resolution: "react-router@npm:6.13.0"
|
||||
dependencies:
|
||||
"@remix-run/router": 1.6.3
|
||||
peerDependencies:
|
||||
react: ">=16.8"
|
||||
checksum: 31a187005d05e063c59324564a283cd28052eaf848ad446c87658f4fc48fa9543329fe8a14d7be14d9bbf62410d383f8cf1cf13898a838bf9c1e3201fe93384c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"react-style-singleton@npm:^2.2.1":
|
||||
version: 2.2.1
|
||||
resolution: "react-style-singleton@npm:2.2.1"
|
||||
|
@ -12958,7 +13066,7 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"signal-exit@npm:^3.0.2, signal-exit@npm:^3.0.3, signal-exit@npm:^3.0.7":
|
||||
"signal-exit@npm:^3.0.0, signal-exit@npm:^3.0.2, signal-exit@npm:^3.0.3, signal-exit@npm:^3.0.7":
|
||||
version: 3.0.7
|
||||
resolution: "signal-exit@npm:3.0.7"
|
||||
checksum: a2f098f247adc367dffc27845853e9959b9e88b01cb301658cfe4194352d8d2bb32e18467c786a7fe15f1d44b233ea35633d076d5e737870b7139949d1ab6318
|
||||
|
@ -13236,10 +13344,10 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"stream-slice@npm:^0.1.2":
|
||||
version: 0.1.2
|
||||
resolution: "stream-slice@npm:0.1.2"
|
||||
checksum: 027111397bd709f299fb1bb34902baf4707bba8851219c9115df673be1075a2cecf54d8671e6258c94483d1fa4e931c6784e49f2e005b1b6d5e3b8b61028fbe1
|
||||
"streamsearch@npm:^1.1.0":
|
||||
version: 1.1.0
|
||||
resolution: "streamsearch@npm:1.1.0"
|
||||
checksum: 1cce16cea8405d7a233d32ca5e00a00169cc0e19fbc02aa839959985f267335d435c07f96e5e0edd0eadc6d39c98d5435fb5bbbdefc62c41834eadc5622ad942
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
@ -13456,6 +13564,22 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"styled-jsx@npm:5.1.1":
|
||||
version: 5.1.1
|
||||
resolution: "styled-jsx@npm:5.1.1"
|
||||
dependencies:
|
||||
client-only: 0.0.1
|
||||
peerDependencies:
|
||||
react: ">= 16.8.0 || 17.x.x || ^18.0.0-0"
|
||||
peerDependenciesMeta:
|
||||
"@babel/core":
|
||||
optional: true
|
||||
babel-plugin-macros:
|
||||
optional: true
|
||||
checksum: 523a33b38603492547e861b98e29c873939b04e15fbe5ef16132c6f1e15958126647983c7d4675325038b428a5e91183d996e90141b18bdd1bbadf6e2c45b2fa
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"stylis@npm:4.2.0":
|
||||
version: 4.2.0
|
||||
resolution: "stylis@npm:4.2.0"
|
||||
|
@ -14313,19 +14437,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"util@npm:^0.12.3":
|
||||
version: 0.12.5
|
||||
resolution: "util@npm:0.12.5"
|
||||
dependencies:
|
||||
inherits: ^2.0.3
|
||||
is-arguments: ^1.0.4
|
||||
is-generator-function: ^1.0.7
|
||||
is-typed-array: ^1.1.3
|
||||
which-typed-array: ^1.1.2
|
||||
checksum: 705e51f0de5b446f4edec10739752ac25856541e0254ea1e7e45e5b9f9b0cb105bc4bd415736a6210edc68245a7f903bf085ffb08dd7deb8a0e847f60538a38a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"utils-merge@npm:1.0.1":
|
||||
version: 1.0.1
|
||||
resolution: "utils-merge@npm:1.0.1"
|
||||
|
@ -14480,6 +14591,16 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"watchpack@npm:2.4.0":
|
||||
version: 2.4.0
|
||||
resolution: "watchpack@npm:2.4.0"
|
||||
dependencies:
|
||||
glob-to-regexp: ^0.4.1
|
||||
graceful-fs: ^4.1.2
|
||||
checksum: 23d4bc58634dbe13b86093e01c6a68d8096028b664ab7139d58f0c37d962d549a940e98f2f201cecdabd6f9c340338dc73ef8bf094a2249ef582f35183d1a131
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"wcwidth@npm:^1.0.1":
|
||||
version: 1.0.1
|
||||
resolution: "wcwidth@npm:1.0.1"
|
||||
|
@ -14489,26 +14610,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"web-encoding@npm:1.1.5":
|
||||
version: 1.1.5
|
||||
resolution: "web-encoding@npm:1.1.5"
|
||||
dependencies:
|
||||
"@zxing/text-encoding": 0.9.0
|
||||
util: ^0.12.3
|
||||
dependenciesMeta:
|
||||
"@zxing/text-encoding":
|
||||
optional: true
|
||||
checksum: 2234a2b122f41006ce07859b3c0bf2e18f46144fda2907d5db0b571b76aa5c26977c646100ad9c00d2f8a4f6f2b848bc02147845d8c447ab365ec4eff376338d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"web-streams-polyfill@npm:^3.1.1":
|
||||
version: 3.2.1
|
||||
resolution: "web-streams-polyfill@npm:3.2.1"
|
||||
checksum: b119c78574b6d65935e35098c2afdcd752b84268e18746606af149e3c424e15621b6f1ff0b42b2676dc012fc4f0d313f964b41a4b5031e525faa03997457da02
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"webidl-conversions@npm:^3.0.0":
|
||||
version: 3.0.1
|
||||
resolution: "webidl-conversions@npm:3.0.1"
|
||||
|
@ -14586,7 +14687,7 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"which-typed-array@npm:^1.1.2, which-typed-array@npm:^1.1.9":
|
||||
"which-typed-array@npm:^1.1.9":
|
||||
version: 1.1.9
|
||||
resolution: "which-typed-array@npm:1.1.9"
|
||||
dependencies:
|
||||
|
@ -14622,7 +14723,7 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"wide-align@npm:^1.1.5":
|
||||
"wide-align@npm:^1.1.2, wide-align@npm:^1.1.5":
|
||||
version: 1.1.5
|
||||
resolution: "wide-align@npm:1.1.5"
|
||||
dependencies:
|
||||
|
@ -14807,24 +14908,22 @@ __metadata:
|
|||
"@mantine/form": ^6.0.14
|
||||
"@mantine/hooks": ^6.0.14
|
||||
"@mantine/modals": ^6.0.14
|
||||
"@mantine/next": ^6.0.14
|
||||
"@mantine/notifications": ^6.0.14
|
||||
"@mantine/prism": ^6.0.14
|
||||
"@mantine/remix": ^6.0.14
|
||||
"@mantine/nprogress": ^6.0.14
|
||||
"@prisma/client": 4.16.1
|
||||
"@prisma/internals": ^4.16.1
|
||||
"@prisma/migrate": ^4.16.1
|
||||
"@remix-run/dev": ^1.16.1
|
||||
"@remix-run/eslint-config": ^1.16.1
|
||||
"@remix-run/express": ^1.17.1
|
||||
"@remix-run/node": ^1.16.1
|
||||
"@remix-run/react": ^1.16.1
|
||||
"@remix-run/v1-route-convention": ^0.1.2
|
||||
"@tabler/icons-react": ^2.22.0
|
||||
"@types/bytes": ^3.1.1
|
||||
"@types/express": ^4.17.17
|
||||
"@types/node": ^20.3.1
|
||||
"@types/react": ^18.2.7
|
||||
"@types/react-dom": ^18.2.4
|
||||
"@types/signale": ^1.4.4
|
||||
argon2: ^0.30.3
|
||||
bytes: ^3.1.2
|
||||
colorette: ^2.0.20
|
||||
cross-env: ^7.0.3
|
||||
|
@ -14832,8 +14931,8 @@ __metadata:
|
|||
dotenv: ^16.1.3
|
||||
eslint: ^8.41.0
|
||||
express: ^4.18.2
|
||||
isbot: ^3.6.10
|
||||
ms: ^2.1.3
|
||||
next: ^13.4.7
|
||||
npm-run-all: ^4.1.5
|
||||
prisma: ^4.16.1
|
||||
react: ^18.2.0
|
||||
|
@ -14856,7 +14955,7 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"zod@npm:^3.21.4":
|
||||
"zod@npm:3.21.4, zod@npm:^3.21.4":
|
||||
version: 3.21.4
|
||||
resolution: "zod@npm:3.21.4"
|
||||
checksum: f185ba87342ff16f7a06686767c2b2a7af41110c7edf7c1974095d8db7a73792696bcb4a00853de0d2edeb34a5b2ea6a55871bc864227dace682a0a28de33e1f
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue