diff --git a/.gitignore b/.gitignore
index f09f4c3d..000cd898 100644
--- a/.gitignore
+++ b/.gitignore
@@ -36,4 +36,6 @@ yarn-error.log*
# zipline
config.toml
-uploads/
\ No newline at end of file
+uploads/
+dist/
+docker-compose.local.yml
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
index 33cca007..ff885fd0 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -11,10 +11,9 @@ WORKDIR /build
COPY --from=deps /build/node_modules ./node_modules
COPY src ./src
-COPY server ./server
COPY scripts ./scripts
COPY prisma ./prisma
-COPY package.json yarn.lock next.config.js next-env.d.ts zip-env.d.ts tsconfig.json ./
+COPY package.json yarn.lock esbuild.config.js next.config.js next-env.d.ts zip-env.d.ts tsconfig.json ./
ENV ZIPLINE_DOCKER_BUILD 1
ENV NEXT_TELEMETRY_DISABLED 1
@@ -31,11 +30,11 @@ RUN addgroup --system --gid 1001 zipline
RUN adduser --system --uid 1001 zipline
COPY --from=builder --chown=zipline:zipline /build/.next ./.next
+COPY --from=builder --chown=zipline:zipline /build/dist ./dist
COPY --from=builder --chown=zipline:zipline /build/node_modules ./node_modules
COPY --from=builder /build/next.config.js ./next.config.js
COPY --from=builder /build/src ./src
-COPY --from=builder /build/server ./server
COPY --from=builder /build/scripts ./scripts
COPY --from=builder /build/prisma ./prisma
COPY --from=builder /build/tsconfig.json ./tsconfig.json
@@ -43,4 +42,4 @@ COPY --from=builder /build/package.json ./package.json
USER zipline
-CMD ["node", "server"]
\ No newline at end of file
+CMD ["node", "dist/server"]
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
index c228d422..fd27359e 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
MIT License
-Copyright (c) 2021 dicedtomato
+Copyright (c) 2022 dicedtomato
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml
index c43b6962..071ebfd1 100644
--- a/docker-compose.dev.yml
+++ b/docker-compose.dev.yml
@@ -26,11 +26,12 @@ services:
- SECRET=changethis
- HOST=0.0.0.0
- PORT=3000
+ - DATASOURCE_TYPE=local
+ - DATASOURCE_DIRECTORY=./uploads
- DATABASE_URL=postgresql://postgres:postgres@postgres/postgres/
- UPLOADER_ROUTE=/u
- UPLOADER_EMBED_ROUTE=/a
- UPLOADER_LENGTH=6
- - UPLOADER_DIRECTORY=./uploads
- UPLOADER_ADMIN_LIMIT=104900000
- UPLOADER_USER_LIMIT=104900000
- UPLOADER_DISABLED_EXTS=
diff --git a/docker-compose.yml b/docker-compose.yml
index 2272c011..2fe3fefa 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -24,11 +24,12 @@ services:
- SECRET=changethis
- HOST=0.0.0.0
- PORT=3000
+ - DATASOURCE_TYPE=local
+ - DATASOURCE_DIRECTORY=./uploads
- DATABASE_URL=postgresql://postgres:postgres@postgres/postgres/
- UPLOADER_ROUTE=/u
- UPLOADER_EMBED_ROUTE=/a
- UPLOADER_LENGTH=6
- - UPLOADER_DIRECTORY=./uploads
- UPLOADER_ADMIN_LIMIT=104900000
- UPLOADER_USER_LIMIT=104900000
- UPLOADER_DISABLED_EXTS=
diff --git a/esbuild.config.js b/esbuild.config.js
new file mode 100644
index 00000000..209831ff
--- /dev/null
+++ b/esbuild.config.js
@@ -0,0 +1,33 @@
+const esbuild = require('esbuild');
+
+(async () => {
+ const watch = process.argv[2] === '--watch';
+
+ await esbuild.build({
+ tsconfig: 'tsconfig.json',
+ outdir: 'dist',
+ bundle: false,
+ platform: 'node',
+ treeShaking: true,
+ entryPoints: [
+ 'src/server/index.ts',
+ 'src/server/util.ts',
+ 'src/server/validateConfig.ts',
+ 'src/lib/logger.ts',
+ 'src/lib/readConfig.ts',
+ 'src/lib/datasource/datasource.ts',
+ 'src/lib/datasource/index.ts',
+ 'src/lib/datasource/Local.ts',
+ 'src/lib/datasource/S3.ts',
+ 'src/lib/ds.ts',
+ 'src/lib/config.ts',
+ ],
+ format: 'cjs',
+ resolveExtensions: ['.ts', '.js'],
+ write: true,
+ watch,
+ incremental: watch,
+ sourcemap: false,
+ minify: process.env.NODE_ENV === 'production',
+ });
+})();
\ No newline at end of file
diff --git a/package.json b/package.json
index 3c547abb..a0af9271 100644
--- a/package.json
+++ b/package.json
@@ -1,10 +1,11 @@
{
"name": "zip3",
- "version": "3.4.0",
+ "version": "3.4.1",
"license": "MIT",
"scripts": {
- "dev": "NODE_ENV=development node server",
- "build": "npm-run-all build:schema build:next",
+ "dev": "node esbuild.config.js && REACT_EDITOR=code-insiders NODE_ENV=development node dist/server",
+ "build": "npm-run-all build:server build:schema build:next",
+ "build:server": "node esbuild.config.js",
"build:next": "next build",
"build:schema": "prisma generate --schema=prisma/schema.prisma",
"migrate:dev": "prisma migrate dev --create-only",
@@ -30,6 +31,7 @@
"@prisma/sdk": "^3.9.2",
"@reduxjs/toolkit": "^1.6.0",
"argon2": "^0.28.2",
+ "aws-sdk": "^2.1085.0",
"colorette": "^1.2.2",
"cookie": "^0.4.1",
"fecha": "^4.2.1",
@@ -50,6 +52,7 @@
"@types/multer": "^1.4.6",
"@types/node": "^15.12.2",
"babel-plugin-import": "^1.13.3",
+ "esbuild": "^0.14.23",
"eslint": "^7.32.0",
"eslint-config-next": "11.0.0",
"npm-run-all": "^4.1.5",
diff --git a/server/validateConfig.js b/server/validateConfig.js
deleted file mode 100644
index 1ae84710..00000000
--- a/server/validateConfig.js
+++ /dev/null
@@ -1,40 +0,0 @@
-const { object, bool, string, number, boolean, array } = require('yup');
-
-const validator = object({
- core: object({
- secure: bool().default(false),
- secret: string().min(8).required(),
- host: string().default('0.0.0.0'),
- port: number().default(3000),
- database_url: string().required(),
- logger: boolean().default(false),
- stats_interval: number().default(1800),
- }).required(),
- uploader: object({
- route: string().default('/u'),
- embed_route: string().default('/a'),
- length: number().default(6),
- directory: string().default('./uploads'),
- admin_limit: number().default(104900000),
- user_limit: number().default(104900000),
- disabled_extensions: array().default([]),
- }).required(),
- urls: object({
- route: string().default('/go'),
- length: number().default(6),
- }).required(),
- ratelimit: object({
- user: number().default(0),
- admin: number().default(0),
- }),
-});
-
-
-module.exports = function validate(config) {
- try {
- return validator.validateSync(config, { abortEarly: false });
- } catch (e) {
- if (process.env.ZIPLINE_DOCKER_BUILD) return {};
- throw `${e.errors.length} errors occured\n${e.errors.map(x => '\t' + x).join('\n')}`;
- }
-};
\ No newline at end of file
diff --git a/src/components/ImagesTable.tsx b/src/components/ImagesTable.tsx
index 237ef789..21dbe569 100644
--- a/src/components/ImagesTable.tsx
+++ b/src/components/ImagesTable.tsx
@@ -8,7 +8,6 @@ import {
} from 'react-table';
import {
ActionIcon,
- Checkbox,
createStyles,
Divider,
Group,
diff --git a/src/components/StatText.tsx b/src/components/StatText.tsx
new file mode 100644
index 00000000..23ee0bfd
--- /dev/null
+++ b/src/components/StatText.tsx
@@ -0,0 +1,6 @@
+import React from 'react';
+import { Text } from '@mantine/core';
+
+export default function StatText({ children }) {
+ return {children};
+}
\ No newline at end of file
diff --git a/src/components/pages/Dashboard.tsx b/src/components/pages/Dashboard.tsx
index 1b15713e..ab740829 100644
--- a/src/components/pages/Dashboard.tsx
+++ b/src/components/pages/Dashboard.tsx
@@ -1,15 +1,16 @@
import React, { useEffect, useState } from 'react';
import Card from 'components/Card';
-import Image from 'components/Image';
+import ZiplineImage from 'components/Image';
import ImagesTable from 'components/ImagesTable';
import useFetch from 'lib/hooks/useFetch';
import { useStoreSelector } from 'lib/redux/store';
-import { Box, Text, Table, Skeleton, Title, SimpleGrid } from '@mantine/core';
+import { Text, Skeleton, Title, SimpleGrid } from '@mantine/core';
import { randomId, useClipboard } from '@mantine/hooks';
import Link from 'components/Link';
import { CopyIcon, Cross1Icon, TrashIcon } from '@modulz/radix-icons';
import { useNotifications } from '@mantine/notifications';
+import StatText from 'components/StatText';
type Aligns = 'inherit' | 'right' | 'left' | 'center' | 'justify';
@@ -27,37 +28,6 @@ export function bytesToRead(bytes: number) {
return `${bytes.toFixed(1)} ${units[num]}`;
}
-function StatText({ children }) {
- return {children};
-}
-
-function StatTable({ rows, columns }) {
- return (
-
-
-
-
- {columns.map(col => (
- {col.name} |
- ))}
-
-
-
- {rows.map(row => (
-
- {columns.map(col => (
-
- {col.format ? col.format(row[col.id]) : row[col.id]}
- |
- ))}
-
- ))}
-
-
-
- );
-}
-
export default function Dashboard() {
const user = useStoreSelector(state => state.user);
@@ -128,8 +98,7 @@ export default function Dashboard() {
]}
>
{recent.length ? recent.map(image => (
- // eslint-disable-next-line jsx-a11y/alt-text
-
+
)) : [1,2,3,4].map(x => (
diff --git a/src/components/pages/Stats.tsx b/src/components/pages/Stats.tsx
index 3727e95e..809acefb 100644
--- a/src/components/pages/Stats.tsx
+++ b/src/components/pages/Stats.tsx
@@ -1,17 +1,11 @@
import React, { useEffect, useState } from 'react';
import Card from 'components/Card';
-import Image from 'components/Image';
-import ImagesTable from 'components/ImagesTable';
+import StatText from 'components/StatText';
import useFetch from 'lib/hooks/useFetch';
import { useStoreSelector } from 'lib/redux/store';
import { Box, Text, Table, Skeleton, Title, SimpleGrid } from '@mantine/core';
-import { randomId, useClipboard } from '@mantine/hooks';
-import Link from 'components/Link';
-import { CopyIcon, Cross1Icon, TrashIcon } from '@modulz/radix-icons';
-import { useNotifications } from '@mantine/notifications';
-
-type Aligns = 'inherit' | 'right' | 'left' | 'center' | 'justify';
+import { randomId } from '@mantine/hooks';
export function bytesToRead(bytes: number) {
if (isNaN(bytes)) return '0.0 B';
@@ -27,10 +21,6 @@ export function bytesToRead(bytes: number) {
return `${bytes.toFixed(1)} ${units[num]}`;
}
-function StatText({ children }) {
- return {children};
-}
-
function StatTable({ rows, columns }) {
return (
@@ -58,9 +48,7 @@ function StatTable({ rows, columns }) {
);
}
-export default function Dashboard() {
- const user = useStoreSelector(state => state.user);
-
+export default function Stats() {
const [stats, setStats] = useState(null);
const update = async () => {
diff --git a/src/components/pages/Upload.tsx b/src/components/pages/Upload.tsx
index 9d0adbf1..9c33fe5f 100644
--- a/src/components/pages/Upload.tsx
+++ b/src/components/pages/Upload.tsx
@@ -30,7 +30,7 @@ function getIconColor(status, theme) {
: theme.black;
}
-export default function Upload({ route }) {
+export default function Upload() {
const theme = useMantineTheme();
const notif = useNotifications();
const clipboard = useClipboard();
@@ -89,10 +89,8 @@ export default function Upload({ route }) {
return (
<>
- setFiles([...files, ...f])}
- >
- {(status) => (
+ setFiles([...files, ...f])}>
+ {status => (
<>
{
+ await writeFile(join(process.cwd(), this.path, file), data);
+ }
+
+ public async get(file: string): Promise {
+ try {
+ const data = await readFile(join(process.cwd(), this.path, file));
+ return data;
+ } catch (e) {
+ return null;
+ }
+ }
+
+ public async size(): Promise {
+ const files = await readdir(this.path);
+
+ let size = 0;
+ for (let i = 0, L = files.length; i !== L; ++i) {
+ const sta = await stat(join(this.path, files[i]));
+ size += sta.size;
+ }
+
+ return size;
+ }
+}
\ No newline at end of file
diff --git a/src/lib/datasource/S3.ts b/src/lib/datasource/S3.ts
new file mode 100644
index 00000000..71499783
--- /dev/null
+++ b/src/lib/datasource/S3.ts
@@ -0,0 +1,66 @@
+import { Datasource } from './datasource';
+import AWS from 'aws-sdk';
+
+export class S3 extends Datasource {
+ public name: string = 'S3';
+ public s3: AWS.S3;
+
+ public constructor(
+ public accessKey: string,
+ public secretKey: string,
+ public bucket: string,
+ ) {
+ super();
+ this.s3 = new AWS.S3({
+ accessKeyId: accessKey,
+ secretAccessKey: secretKey,
+ });
+ }
+
+ public async save(file: string, data: Buffer): Promise {
+ return new Promise((resolve, reject) => {
+ this.s3.upload({
+ Bucket: this.bucket,
+ Key: file,
+ Body: data,
+ }, err => {
+ if (err) {
+ reject(err);
+ } else {
+ resolve();
+ }
+ });
+ });
+ }
+
+ public async get(file: string): Promise {
+ return new Promise((resolve, reject) => {
+ this.s3.getObject({
+ Bucket: this.bucket,
+ Key: file,
+ }, (err, data) => {
+ if (err) {
+ reject(err);
+ } else {
+ // @ts-ignore
+ resolve(Buffer.from(data.Body));
+ }
+ });
+ });
+ }
+
+ public async size(): Promise {
+ return new Promise((resolve, reject) => {
+ this.s3.listObjects({
+ Bucket: this.bucket,
+ }, (err, data) => {
+ if (err) {
+ reject(err);
+ } else {
+ const size = data.Contents.reduce((acc, cur) => acc + cur.Size, 0);
+ resolve(size);
+ }
+ });
+ });
+ }
+}
\ No newline at end of file
diff --git a/src/lib/datasource/datasource.ts b/src/lib/datasource/datasource.ts
new file mode 100644
index 00000000..f598fe5d
--- /dev/null
+++ b/src/lib/datasource/datasource.ts
@@ -0,0 +1,7 @@
+export abstract class Datasource {
+ public name: string;
+
+ public abstract save(file: string, data: Buffer): Promise;
+ public abstract get(file: string): Promise;
+ public abstract size(): Promise;
+}
\ No newline at end of file
diff --git a/src/lib/datasource/index.ts b/src/lib/datasource/index.ts
new file mode 100644
index 00000000..6290a447
--- /dev/null
+++ b/src/lib/datasource/index.ts
@@ -0,0 +1,4 @@
+export { Datasource } from './datasource';
+export { Local } from './Local';
+export { S3 } from './S3';
+
diff --git a/src/lib/ds.ts b/src/lib/ds.ts
new file mode 100644
index 00000000..9988f508
--- /dev/null
+++ b/src/lib/ds.ts
@@ -0,0 +1,20 @@
+import config from './config';
+import { S3, Local } from './datasource';
+import Logger from './logger';
+
+if (!global.datasource) {
+ switch (config.datasource.type) {
+ case 's3':
+ global.datasource = new S3(config.datasource.s3.access_key_id, config.datasource.s3.secret_access_key, config.datasource.s3.bucket);
+ Logger.get('datasource').info(`Using S3:${config.datasource.s3.bucket} datasource`);
+ break;
+ case 'local':
+ global.datasource = new Local(config.datasource.local.directory);
+ Logger.get('datasource').info(`Using local:${config.datasource.local.directory} datasource`);
+ break;
+ default:
+ throw new Error('Invalid datasource type');
+ }
+};
+
+export default global.datasource;
\ No newline at end of file
diff --git a/src/lib/logger.js b/src/lib/logger.js
deleted file mode 100644
index 9490ac72..00000000
--- a/src/lib/logger.js
+++ /dev/null
@@ -1,38 +0,0 @@
-const { format } = require('fecha');
-const { blueBright, red, cyan } = require('colorette');
-
-module.exports = class Logger {
- static get(clas) {
- if (typeof clas !== 'function') if (typeof clas !== 'string') throw new Error('not string/function');
-
- const name = clas.name ?? clas;
-
- return new Logger(name);
- }
-
- constructor (name) {
- this.name = name;
- }
-
- info(message) {
- console.log(this.formatMessage('INFO', this.name, message));
- }
-
- error(error) {
- console.log(this.formatMessage('ERROR', this.name, error.stack ?? error));
- }
-
- formatMessage(level, name, message) {
- const time = format(new Date(), 'YYYY-MM-DD hh:mm:ss,SSS A');
- return `${time} ${this.formatLevel(level)} [${blueBright(name)}] ${message}`;
- }
-
- formatLevel(level) {
- switch (level) {
- case 'INFO':
- return cyan('INFO ');
- case 'ERROR':
- return red('ERROR');
- }
- }
-};
\ No newline at end of file
diff --git a/src/lib/logger.ts b/src/lib/logger.ts
new file mode 100644
index 00000000..a8484150
--- /dev/null
+++ b/src/lib/logger.ts
@@ -0,0 +1,45 @@
+import { format } from 'fecha';
+import { blueBright, red, cyan } from 'colorette';
+
+export enum LoggerLevel {
+ ERROR,
+ INFO,
+}
+
+export default class Logger {
+ public name: string;
+
+ static get(clas: any) {
+ if (typeof clas !== 'function') if (typeof clas !== 'string') throw new Error('not string/function');
+
+ const name = clas.name ?? clas;
+
+ return new Logger(name);
+ }
+
+ constructor(name: string) {
+ this.name = name;
+ }
+
+ info(message: string) {
+ console.log(this.formatMessage(LoggerLevel.INFO, this.name, message));
+ }
+
+ error(error: any) {
+ console.log(this.formatMessage(LoggerLevel.ERROR, this.name, error.stack ?? error));
+ }
+
+ formatMessage(level: LoggerLevel, name, message) {
+ const time = format(new Date(), 'YYYY-MM-DD hh:mm:ss,SSS A');
+ return `${time} ${this.formatLevel(level)} [${blueBright(name)}] ${message}`;
+ }
+
+ formatLevel(level: LoggerLevel) {
+ switch (level) {
+ case LoggerLevel.INFO:
+ return cyan('INFO ');
+ case LoggerLevel.ERROR:
+ return red('ERROR');
+ }
+ }
+};
\ No newline at end of file
diff --git a/src/lib/middleware/withZipline.ts b/src/lib/middleware/withZipline.ts
index 112ab26f..4fa26aca 100644
--- a/src/lib/middleware/withZipline.ts
+++ b/src/lib/middleware/withZipline.ts
@@ -11,7 +11,7 @@ export interface NextApiFile {
originalname: string;
encoding: string;
mimetype: string;
- buffer: string;
+ buffer: Buffer;
size: number;
}
diff --git a/src/lib/readConfig.js b/src/lib/readConfig.ts
similarity index 75%
rename from src/lib/readConfig.js
rename to src/lib/readConfig.ts
index b1829f9e..912edd66 100644
--- a/src/lib/readConfig.js
+++ b/src/lib/readConfig.ts
@@ -1,7 +1,8 @@
-const { existsSync, readFileSync } = require('fs');
-const { join } = require('path');
-const parse = require('@iarna/toml/parse-string.js');
-const Logger = require('./logger.js');
+import { existsSync, readFileSync } from 'fs';
+import { join } from 'path';
+import parse from '@iarna/toml/parse-string';
+import Logger from './logger';
+import { Config } from './types';
const e = (val, type, fn) => ({ val, type, fn });
@@ -14,9 +15,14 @@ const envValues = [
e('LOGGER', 'boolean', (c, v) => c.core.logger = v ?? true),
e('STATS_INTERVAL', 'number', (c, v) => c.core.stats_interval = v),
+ e('DATASOURCE_TYPE', 'string', (c, v) => c.datasource.type = v),
+ e('DATASOURCE_LOCAL_DIRECTORY', 'string', (c, v) => c.datasource.local.directory = v),
+ e('DATASOURCE_S3_ACCESS_KEY_ID', 'string', (c, v) => c.datasource.s3.access_key_id = v ),
+ e('DATASOURCE_S3_SECRET_ACCESS_KEY', 'string', (c, v) => c.datasource.s3.secret_access_key = v),
+ e('DATASOURCE_S3_BUCKET', 'string', (c, v) => c.datasource.s3.bucket = v),
+
e('UPLOADER_ROUTE', 'string', (c, v) => c.uploader.route = v),
e('UPLOADER_LENGTH', 'number', (c, v) => c.uploader.length = v),
- e('UPLOADER_DIRECTORY', 'string', (c, v) => c.uploader.directory = v),
e('UPLOADER_ADMIN_LIMIT', 'number', (c, v) => c.uploader.admin_limit = v),
e('UPLOADER_USER_LIMIT', 'number', (c, v) => c.uploader.user_limit = v),
e('UPLOADER_DISABLED_EXTS', 'array', (c, v) => v ? c.uploader.disabled_extentions = v : c.uploader.disabled_extentions = []),
@@ -28,7 +34,7 @@ const envValues = [
e('RATELIMIT_ADMIN', 'number', (c, v) => c.ratelimit.user = v ?? 0),
];
-module.exports = function readConfig() {
+export default function readConfig(): Config {
if (!existsSync(join(process.cwd(), 'config.toml'))) {
if (!process.env.ZIPLINE_DOCKER_BUILD) Logger.get('config').info('reading environment');
return tryReadEnv();
@@ -43,7 +49,7 @@ module.exports = function readConfig() {
}
};
-function tryReadEnv() {
+function tryReadEnv(): Config {
const config = {
core: {
secure: undefined,
@@ -54,10 +60,20 @@ function tryReadEnv() {
logger: undefined,
stats_interval: undefined,
},
+ datasource: {
+ type: undefined,
+ local: {
+ directory: undefined,
+ },
+ s3: {
+ access_key_id: undefined,
+ secret_access_key: undefined,
+ bucket: undefined,
+ },
+ },
uploader: {
route: undefined,
length: undefined,
- directory: undefined,
admin_limit: undefined,
user_limit: undefined,
disabled_extentions: undefined,
@@ -74,7 +90,7 @@ function tryReadEnv() {
for (let i = 0, L = envValues.length; i !== L; ++i) {
const envValue = envValues[i];
- let value = process.env[envValue.val];
+ let value: any = process.env[envValue.val];
if (!value) {
envValues[i].fn(config, undefined);
diff --git a/src/lib/types.ts b/src/lib/types.ts
index 425e5af1..b098346c 100644
--- a/src/lib/types.ts
+++ b/src/lib/types.ts
@@ -21,6 +21,26 @@ export interface ConfigCore {
stats_interval: number;
}
+export interface ConfigDatasource {
+ // The type of datasource
+ type: 'local' | 's3';
+
+ // The local datasource
+ local: ConfigLocalDatasource;
+ s3?: ConfigS3Datasource;
+}
+
+export interface ConfigLocalDatasource {
+ // The directory to store files in
+ directory: string;
+}
+
+export interface ConfigS3Datasource {
+ access_key_id: string;
+ secret_access_key: string;
+ bucket: string;
+}
+
export interface ConfigUploader {
// The route uploads will be served on
route: string;
@@ -28,9 +48,6 @@ export interface ConfigUploader {
// Length of random chars to generate for file names
length: number;
- // Where uploads are stored
- directory: string;
-
// Admin file upload limit
admin_limit: number;
@@ -63,4 +80,5 @@ export interface Config {
uploader: ConfigUploader;
urls: ConfigUrls;
ratelimit: ConfigRatelimit;
+ datasource: ConfigDatasource;
}
diff --git a/src/pages/[...id].tsx b/src/pages/[...id].tsx
index de596c77..d9e8240f 100644
--- a/src/pages/[...id].tsx
+++ b/src/pages/[...id].tsx
@@ -1,14 +1,11 @@
import React, { useEffect } from 'react';
import Head from 'next/head';
import { GetServerSideProps } from 'next';
-import { Box, useMantineTheme } from '@mantine/core';
+import { Box } from '@mantine/core';
import config from 'lib/config';
import prisma from 'lib/prisma';
-import { getFile } from '../../server/util';
import { parse } from 'lib/clientUtils';
import * as exts from '../../scripts/exts';
-import { Prism } from '@mantine/prism';
-import ZiplineTheming from 'components/Theming';
export default function EmbeddedImage({ image, user }) {
const dataURL = (route: string) => `${route}/${image.file}`;
@@ -120,8 +117,6 @@ export const getServerSideProps: GetServerSideProps = async (context) => {
image.created_at = image.created_at.toString();
const prismRender = Object.keys(exts).includes(image.file.split('.').pop());
- // let prismRenderCode;/
- // if (prismRender) prismRenderCode = (await getFile(config.uploader.directory, id)).toString();
if (prismRender) return {
redirect: {
destination: `/code/${image.file}`,
@@ -130,7 +125,9 @@ export const getServerSideProps: GetServerSideProps = async (context) => {
};
if (!image.mimetype.startsWith('image')) {
- const data = await getFile(config.uploader.directory, id);
+ const { default: datasource } = await import('lib/ds');
+
+ const data = await datasource.get(image.file);
if (!data) return { notFound: true };
context.res.end(data);
diff --git a/src/pages/api/upload.ts b/src/pages/api/upload.ts
index 44b22289..134d2ef7 100644
--- a/src/pages/api/upload.ts
+++ b/src/pages/api/upload.ts
@@ -3,12 +3,11 @@ import prisma from 'lib/prisma';
import zconfig from 'lib/config';
import { NextApiReq, NextApiRes, withZipline } from 'lib/middleware/withZipline';
import { createInvisImage, randomChars } from 'lib/util';
-import { writeFile } from 'fs/promises';
-import { join } from 'path';
import Logger from 'lib/logger';
import { ImageFormat, InvisibleImage } from '@prisma/client';
import { format as formatDate } from 'fecha';
import { v4 } from 'uuid';
+import datasource from 'lib/ds';
const uploader = multer({
storage: multer.memoryStorage(),
@@ -70,7 +69,7 @@ async function handler(req: NextApiReq, res: NextApiRes) {
if (req.headers.zws) invis = await createInvisImage(zconfig.uploader.length, image.id);
- await writeFile(join(process.cwd(), zconfig.uploader.directory, image.file), file.buffer);
+ await datasource.save(image.file, file.buffer);
Logger.get('image').info(`User ${user.username} (${user.id}) uploaded an image ${image.file} (${image.id})`);
files.push(`${zconfig.core.secure ? 'https' : 'http'}://${req.headers.host}${zconfig.uploader.route}/${invis ? invis.invis : image.file}`);
}
diff --git a/src/pages/api/user/files.ts b/src/pages/api/user/files.ts
index e779a7cf..04f9ca49 100644
--- a/src/pages/api/user/files.ts
+++ b/src/pages/api/user/files.ts
@@ -43,6 +43,9 @@ async function handler(req: NextApiReq, res: NextApiRes) {
userId: user.id,
favorite: !!req.query.favorite,
},
+ orderBy: {
+ created_at: 'desc',
+ },
select: {
created_at: true,
file: true,
diff --git a/src/pages/dashboard/upload.tsx b/src/pages/dashboard/upload.tsx
index f7689c5d..cb87dc58 100644
--- a/src/pages/dashboard/upload.tsx
+++ b/src/pages/dashboard/upload.tsx
@@ -1,9 +1,7 @@
import React from 'react';
-import { GetStaticProps } from 'next';
import useLogin from 'hooks/useLogin';
import Layout from 'components/Layout';
import Upload from 'components/pages/Upload';
-import config from 'lib/config';
export default function UploadPage({ route }) {
const { user, loading } = useLogin();
@@ -14,17 +12,9 @@ export default function UploadPage({ route }) {
-
+
);
}
-export const getStaticProps: GetStaticProps = async () => {
- return {
- props: {
- route: process.env.ZIPLINE_DOCKER_BUILD === '1' ? '/u' : config.uploader.route,
- },
- };
-};
-
UploadPage.title = 'Zipline - Upload';
\ No newline at end of file
diff --git a/server/index.js b/src/server/index.ts
similarity index 74%
rename from server/index.js
rename to src/server/index.ts
index 0d3d541d..6fde30b8 100644
--- a/server/index.js
+++ b/src/server/index.ts
@@ -1,15 +1,15 @@
-const next = require('next').default;
-const { createServer } = require('http');
-const { mkdir } = require('fs/promises');
-const { extname } = require('path');
-const validateConfig = require('./validateConfig');
-const Logger = require('../src/lib/logger');
-const readConfig = require('../src/lib/readConfig');
-const mimes = require('../scripts/mimes');
-const { log, getStats, getFile, migrations } = require('./util');
-const { PrismaClient } = require('@prisma/client');
-const { version } = require('../package.json');
-const exts = require('../scripts/exts');
+import next from 'next';
+import { createServer } from 'http';
+import { extname } from 'path';
+import Logger from '../lib/logger';
+import mimes from '../../scripts/mimes';
+import { log, getStats, migrations } from './util';
+import { PrismaClient } from '@prisma/client';
+import { version } from '../../package.json';
+import exts from '../../scripts/exts';
+import datasource from '../lib/ds';
+import config from '../lib/config';
+import { mkdir } from 'fs/promises';
const serverLog = Logger.get('server');
serverLog.info(`starting zipline@${version} server`);
@@ -26,13 +26,12 @@ const dev = process.env.NODE_ENV === 'development';
})();
async function run() {
- const a = readConfig();
- const config = validateConfig(a);
-
process.env.DATABASE_URL = config.core.database_url;
await migrations();
- await mkdir(config.uploader.directory, { recursive: true });
+ if (config.datasource.type === 'local') {
+ await mkdir(config.datasource.local.directory, { recursive: true });
+ }
const app = next({
dir: '.',
@@ -68,14 +67,14 @@ async function run() {
});
if (!image) {
- const data = await getFile(config.uploader.directory, parts[2]);
+ const data = await datasource.get(parts[2]);
if (!data) return app.render404(req, res);
const mimetype = mimes[extname(parts[2])] ?? 'application/octet-stream';
res.setHeader('Content-Type', mimetype);
res.end(data);
} else {
- const data = await getFile(config.uploader.directory, image.file);
+ const data = await datasource.get(parts[2]);
if (!data) return app.render404(req, res);
await prisma.image.update({
@@ -106,7 +105,7 @@ async function run() {
});
if (!image) {
- const data = await getFile(config.uploader.directory, parts[2]);
+ const data = await datasource.get(parts[2]);
if (!data) return app.render404(req, res);
const mimetype = mimes[extname(parts[2])] ?? 'application/octet-stream';
@@ -117,7 +116,8 @@ async function run() {
} else {
const ext = image.file.split('.').pop();
if (Object.keys(exts).includes(ext)) return handle(req, res);
- const data = await getFile(config.uploader.directory, image.file);
+
+ const data = await datasource.get(parts[2]);
if (!data) return app.render404(req, res);
await prisma.image.update({
@@ -131,7 +131,7 @@ async function run() {
handle(req, res);
}
- if (config.core.logger) log(req.url, res.statusCode);
+ if (config.core.logger) log(req.url);
});
srv.on('error', (e) => {
@@ -146,14 +146,14 @@ async function run() {
srv.listen(config.core.port, config.core.host ?? '0.0.0.0');
- const stats = await getStats(prisma, config);
+ const stats = await getStats(prisma, datasource);
await prisma.stats.create({
data: {
data: stats,
},
});
setInterval(async () => {
- const stats = await getStats(prisma, config);
+ const stats = await getStats(prisma, datasource);
await prisma.stats.create({
data: {
data: stats,
diff --git a/server/util.js b/src/server/util.ts
similarity index 74%
rename from server/util.js
rename to src/server/util.ts
index 6f9ec638..f5f15b84 100644
--- a/server/util.js
+++ b/src/server/util.ts
@@ -1,9 +1,11 @@
-const { readFile, readdir, stat } = require('fs/promises');
-const { join } = require('path');
-const { Migrate } = require('@prisma/migrate/dist/Migrate.js');
-const Logger = require('../src/lib/logger.js');
+import { readFile, readdir, stat } from 'fs/promises';
+import { join } from 'path';
+import { Migrate } from '@prisma/migrate/dist/Migrate';
+import Logger from '../lib/logger';
+import { execSync } from 'child_process';
+import { Datasource } from '../lib/datasource/index';
-async function migrations() {
+export async function migrations() {
const migrate = new Migrate('./prisma/schema.prisma');
const diagnose = await migrate.diagnoseMigrationHistory({
optInToShadowDatabase: false,
@@ -18,12 +20,12 @@ async function migrations() {
migrate.stop();
}
-function log(url) {
+export function log(url) {
if (url.startsWith('/_next') || url.startsWith('/__nextjs')) return;
return Logger.get('url').info(url);
}
-function shouldUseYarn() {
+export function shouldUseYarn() {
try {
execSync('yarnpkg --version', { stdio: 'ignore' });
return true;
@@ -32,17 +34,7 @@ function shouldUseYarn() {
}
}
-
-async function getFile(dir, file) {
- try {
- const data = await readFile(join(process.cwd(), dir, file));
- return data;
- } catch (e) {
- return null;
- }
-}
-
-async function sizeOfDir(directory) {
+export async function sizeOfDir(directory) {
const files = await readdir(directory);
let size = 0;
@@ -54,7 +46,7 @@ async function sizeOfDir(directory) {
return size;
}
-function bytesToRead(bytes) {
+export function bytesToRead(bytes) {
const units = ['B', 'kB', 'MB', 'GB', 'TB', 'PB'];
let num = 0;
@@ -67,8 +59,8 @@ function bytesToRead(bytes) {
}
-async function getStats(prisma, config) {
- const size = await sizeOfDir(join(process.cwd(), config.uploader.directory));
+export async function getStats(prisma, datasource: Datasource) {
+ const size = await datasource.size();
const byUser = await prisma.image.groupBy({
by: ['userId'],
_count: {
@@ -117,14 +109,4 @@ async function getStats(prisma, config) {
views_count: (viewsCount[0]?._sum?.views ?? 0),
types_count: types_count.sort((a,b) => b.count-a.count),
};
-}
-
-module.exports = {
- migrations,
- bytesToRead,
- getFile,
- getStats,
- log,
- sizeOfDir,
- shouldUseYarn,
-};
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/src/server/validateConfig.ts b/src/server/validateConfig.ts
new file mode 100644
index 00000000..4e38ea13
--- /dev/null
+++ b/src/server/validateConfig.ts
@@ -0,0 +1,60 @@
+import { Config } from 'lib/types';
+import { object, bool, string, number, boolean, array } from 'yup';
+
+const validator = object({
+ core: object({
+ secure: bool().default(false),
+ secret: string().min(8).required(),
+ host: string().default('0.0.0.0'),
+ port: number().default(3000),
+ database_url: string().required(),
+ logger: boolean().default(false),
+ stats_interval: number().default(1800),
+ }).required(),
+ datasource: object({
+ type: string().default('local'),
+ local: object({
+ directory: string().default('./uploads'),
+ }),
+ s3: object({
+ access_key_id: string(),
+ secret_access_key: string(),
+ bucket: string(),
+ }).notRequired(),
+ }).required(),
+ uploader: object({
+ route: string().default('/u'),
+ embed_route: string().default('/a'),
+ length: number().default(6),
+ admin_limit: number().default(104900000),
+ user_limit: number().default(104900000),
+ disabled_extensions: array().default([]),
+ }).required(),
+ urls: object({
+ route: string().default('/go'),
+ length: number().default(6),
+ }).required(),
+ ratelimit: object({
+ user: number().default(0),
+ admin: number().default(0),
+ }),
+});
+
+
+export default function validate(config): Config {
+ try {
+ const validated = validator.validateSync(config, { abortEarly: false });
+ if (validated.datasource.type === 's3') {
+ const errors = [];
+ if (!validated.datasource.s3.access_key_id) errors.push('datasource.s3.access_key_id is a required field');
+ if (!validated.datasource.s3.secret_access_key) errors.push('datasource.s3.secret_access_key is a required field');
+ if (!validated.datasource.s3.bucket) errors.push('datasource.s3.bucket is a required field');
+ if (errors.length) throw { errors };
+ }
+
+ return validated as unknown as Config;
+ } catch (e) {
+ if (process.env.ZIPLINE_DOCKER_BUILD) return null;
+ throw `${e.errors.length} errors occured\n${e.errors.map(x => '\t' + x).join('\n')}`;
+ }
+};
\ No newline at end of file
diff --git a/yarn.lock b/yarn.lock
index 4869927b..1e8bbf78 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1407,6 +1407,21 @@ attr-accept@^2.2.2:
resolved "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.2.tgz"
integrity sha512-7prDjvt9HmqiZ0cl5CRjtS84sEyhsHP2coDkaZKRKVfCDo9s7iw7ChVmar78Gu9pC4SoR/28wFu/G5JJhTnqEg==
+aws-sdk@^2.1085.0:
+ version "2.1085.0"
+ resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.1085.0.tgz#bb509d1e321754fbcd90dff3449f5601e64fb607"
+ integrity sha512-zL20v5QXoSsb2S6RKAhDmfnZYUIRldR93ihJ6mCYrw6Zl+Dir2SVmALy++U/zduDbKB4NaU72mscumx2RWc7Hg==
+ dependencies:
+ buffer "4.9.2"
+ events "1.1.1"
+ ieee754 "1.1.13"
+ jmespath "0.16.0"
+ querystring "0.2.0"
+ sax "1.2.1"
+ url "0.10.3"
+ uuid "3.3.2"
+ xml2js "0.4.19"
+
axe-core@^4.0.2:
version "4.2.2"
resolved "https://registry.npmjs.org/axe-core/-/axe-core-4.2.2.tgz"
@@ -1446,7 +1461,7 @@ balanced-match@^1.0.0:
resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz"
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
-base64-js@^1.3.1:
+base64-js@^1.0.2, base64-js@^1.3.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
@@ -1509,6 +1524,15 @@ buffer-writer@2.0.0:
resolved "https://registry.yarnpkg.com/buffer-writer/-/buffer-writer-2.0.0.tgz#ce7eb81a38f7829db09c873f2fbb792c0c98ec04"
integrity sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==
+buffer@4.9.2:
+ version "4.9.2"
+ resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.2.tgz#230ead344002988644841ab0244af8c44bbe3ef8"
+ integrity sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==
+ dependencies:
+ base64-js "^1.0.2"
+ ieee754 "^1.1.4"
+ isarray "^1.0.0"
+
buffer@^5.5.0:
version "5.7.1"
resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0"
@@ -2056,6 +2080,126 @@ es-to-primitive@^1.2.1:
is-date-object "^1.0.1"
is-symbol "^1.0.2"
+esbuild-android-arm64@0.14.23:
+ version "0.14.23"
+ resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.23.tgz#c89b3c50b4f47668dcbeb0b34ee4615258818e71"
+ integrity sha512-k9sXem++mINrZty1v4FVt6nC5BQCFG4K2geCIUUqHNlTdFnuvcqsY7prcKZLFhqVC1rbcJAr9VSUGFL/vD4vsw==
+
+esbuild-darwin-64@0.14.23:
+ version "0.14.23"
+ resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.23.tgz#1c131e8cb133ed935ca32f824349a117c896a15b"
+ integrity sha512-lB0XRbtOYYL1tLcYw8BoBaYsFYiR48RPrA0KfA/7RFTr4MV7Bwy/J4+7nLsVnv9FGuQummM3uJ93J3ptaTqFug==
+
+esbuild-darwin-arm64@0.14.23:
+ version "0.14.23"
+ resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.23.tgz#3c6245a50109dd84953f53d7833bd3b4f0e8c6fa"
+ integrity sha512-yat73Z/uJ5tRcfRiI4CCTv0FSnwErm3BJQeZAh+1tIP0TUNh6o+mXg338Zl5EKChD+YGp6PN+Dbhs7qa34RxSw==
+
+esbuild-freebsd-64@0.14.23:
+ version "0.14.23"
+ resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.23.tgz#0cdc54e72d3dd9cd992f9c2960055e68a7f8650c"
+ integrity sha512-/1xiTjoLuQ+LlbfjJdKkX45qK/M7ARrbLmyf7x3JhyQGMjcxRYVR6Dw81uH3qlMHwT4cfLW4aEVBhP1aNV7VsA==
+
+esbuild-freebsd-arm64@0.14.23:
+ version "0.14.23"
+ resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.23.tgz#1d11faed3a0c429e99b7dddef84103eb509788b2"
+ integrity sha512-uyPqBU/Zcp6yEAZS4LKj5jEE0q2s4HmlMBIPzbW6cTunZ8cyvjG6YWpIZXb1KK3KTJDe62ltCrk3VzmWHp+iLg==
+
+esbuild-linux-32@0.14.23:
+ version "0.14.23"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.14.23.tgz#fd9f033fc27dcab61100cb1eb1c936893a68c841"
+ integrity sha512-37R/WMkQyUfNhbH7aJrr1uCjDVdnPeTHGeDhZPUNhfoHV0lQuZNCKuNnDvlH/u/nwIYZNdVvz1Igv5rY/zfrzQ==
+
+esbuild-linux-64@0.14.23:
+ version "0.14.23"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.14.23.tgz#c04c438514f1359ecb1529205d0c836d4165f198"
+ integrity sha512-H0gztDP60qqr8zoFhAO64waoN5yBXkmYCElFklpd6LPoobtNGNnDe99xOQm28+fuD75YJ7GKHzp/MLCLhw2+vQ==
+
+esbuild-linux-arm64@0.14.23:
+ version "0.14.23"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.23.tgz#d1b3ab2988ab0734886eb9e811726f7db099ab96"
+ integrity sha512-c4MLOIByNHR55n3KoYf9hYDfBRghMjOiHLaoYLhkQkIabb452RWi+HsNgB41sUpSlOAqfpqKPFNg7VrxL3UX9g==
+
+esbuild-linux-arm@0.14.23:
+ version "0.14.23"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.23.tgz#df7558b6a5076f5eb9fd387c8704f768b61d97fb"
+ integrity sha512-x64CEUxi8+EzOAIpCUeuni0bZfzPw/65r8tC5cy5zOq9dY7ysOi5EVQHnzaxS+1NmV+/RVRpmrzGw1QgY2Xpmw==
+
+esbuild-linux-mips64le@0.14.23:
+ version "0.14.23"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.23.tgz#bb4c47fccc9493d460ffeb1f88e8a97a98a14f8b"
+ integrity sha512-kHKyKRIAedYhKug2EJpyJxOUj3VYuamOVA1pY7EimoFPzaF3NeY7e4cFBAISC/Av0/tiV0xlFCt9q0HJ68IBIw==
+
+esbuild-linux-ppc64le@0.14.23:
+ version "0.14.23"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.23.tgz#a332dbc8a1b4e30cfe1261bfaa5cef57c9c8c02a"
+ integrity sha512-7ilAiJEPuJJnJp/LiDO0oJm5ygbBPzhchJJh9HsHZzeqO+3PUzItXi+8PuicY08r0AaaOe25LA7sGJ0MzbfBag==
+
+esbuild-linux-riscv64@0.14.23:
+ version "0.14.23"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.23.tgz#85675f3f931f5cd7cfb238fd82f77a62ffcb6d86"
+ integrity sha512-fbL3ggK2wY0D8I5raPIMPhpCvODFE+Bhb5QGtNP3r5aUsRR6TQV+ZBXIaw84iyvKC8vlXiA4fWLGhghAd/h/Zg==
+
+esbuild-linux-s390x@0.14.23:
+ version "0.14.23"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.23.tgz#a526282a696e6d846f4c628f5315475518c0c0f0"
+ integrity sha512-GHMDCyfy7+FaNSO8RJ8KCFsnax8fLUsOrj9q5Gi2JmZMY0Zhp75keb5abTFCq2/Oy6KVcT0Dcbyo/bFb4rIFJA==
+
+esbuild-netbsd-64@0.14.23:
+ version "0.14.23"
+ resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.23.tgz#8e456605694719aa1be4be266d6cd569c06dfaf5"
+ integrity sha512-ovk2EX+3rrO1M2lowJfgMb/JPN1VwVYrx0QPUyudxkxLYrWeBxDKQvc6ffO+kB4QlDyTfdtAURrVzu3JeNdA2g==
+
+esbuild-openbsd-64@0.14.23:
+ version "0.14.23"
+ resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.23.tgz#f2fc51714b4ddabc86e4eb30ca101dd325db2f7d"
+ integrity sha512-uYYNqbVR+i7k8ojP/oIROAHO9lATLN7H2QeXKt2H310Fc8FJj4y3Wce6hx0VgnJ4k1JDrgbbiXM8rbEgQyg8KA==
+
+esbuild-sunos-64@0.14.23:
+ version "0.14.23"
+ resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.23.tgz#a408f33ea20e215909e20173a0fd78b1aaad1f8e"
+ integrity sha512-hAzeBeET0+SbScknPzS2LBY6FVDpgE+CsHSpe6CEoR51PApdn2IB0SyJX7vGelXzlyrnorM4CAsRyb9Qev4h9g==
+
+esbuild-windows-32@0.14.23:
+ version "0.14.23"
+ resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.14.23.tgz#b9005bbff54dac3975ff355d5de2b5e37165d128"
+ integrity sha512-Kttmi3JnohdaREbk6o9e25kieJR379TsEWF0l39PQVHXq3FR6sFKtVPgY8wk055o6IB+rllrzLnbqOw/UV60EA==
+
+esbuild-windows-64@0.14.23:
+ version "0.14.23"
+ resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.14.23.tgz#2b5a99befeaca6aefdad32d738b945730a60a060"
+ integrity sha512-JtIT0t8ymkpl6YlmOl6zoSWL5cnCgyLaBdf/SiU/Eg3C13r0NbHZWNT/RDEMKK91Y6t79kTs3vyRcNZbfu5a8g==
+
+esbuild-windows-arm64@0.14.23:
+ version "0.14.23"
+ resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.23.tgz#edc560bbadb097eb45fc235aeacb942cb94a38c0"
+ integrity sha512-cTFaQqT2+ik9e4hePvYtRZQ3pqOvKDVNarzql0VFIzhc0tru/ZgdLoXd6epLiKT+SzoSce6V9YJ+nn6RCn6SHw==
+
+esbuild@^0.14.23:
+ version "0.14.23"
+ resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.14.23.tgz#95e842cb22bc0c7d82c140adc16788aac91469fe"
+ integrity sha512-XjnIcZ9KB6lfonCa+jRguXyRYcldmkyZ99ieDksqW/C8bnyEX299yA4QH2XcgijCgaddEZePPTgvx/2imsq7Ig==
+ optionalDependencies:
+ esbuild-android-arm64 "0.14.23"
+ esbuild-darwin-64 "0.14.23"
+ esbuild-darwin-arm64 "0.14.23"
+ esbuild-freebsd-64 "0.14.23"
+ esbuild-freebsd-arm64 "0.14.23"
+ esbuild-linux-32 "0.14.23"
+ esbuild-linux-64 "0.14.23"
+ esbuild-linux-arm "0.14.23"
+ esbuild-linux-arm64 "0.14.23"
+ esbuild-linux-mips64le "0.14.23"
+ esbuild-linux-ppc64le "0.14.23"
+ esbuild-linux-riscv64 "0.14.23"
+ esbuild-linux-s390x "0.14.23"
+ esbuild-netbsd-64 "0.14.23"
+ esbuild-openbsd-64 "0.14.23"
+ esbuild-sunos-64 "0.14.23"
+ esbuild-windows-32 "0.14.23"
+ esbuild-windows-64 "0.14.23"
+ esbuild-windows-arm64 "0.14.23"
+
escape-string-regexp@4.0.0, escape-string-regexp@^4.0.0:
version "4.0.0"
resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz"
@@ -2276,6 +2420,11 @@ esutils@^2.0.2:
resolved "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz"
integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
+events@1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924"
+ integrity sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=
+
events@^3.0.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400"
@@ -2738,7 +2887,12 @@ iconv-lite@^0.6.3:
dependencies:
safer-buffer ">= 2.1.2 < 3.0.0"
-ieee754@^1.1.13, ieee754@^1.2.1:
+ieee754@1.1.13:
+ version "1.1.13"
+ resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84"
+ integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==
+
+ieee754@^1.1.13, ieee754@^1.1.4, ieee754@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
@@ -2957,7 +3111,7 @@ isarray@0.0.1:
resolved "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz"
integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=
-isarray@~1.0.0:
+isarray@^1.0.0, isarray@~1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz"
integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
@@ -2967,6 +3121,11 @@ isexe@^2.0.0:
resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz"
integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=
+jmespath@0.16.0:
+ version "0.16.0"
+ resolved "https://registry.yarnpkg.com/jmespath/-/jmespath-0.16.0.tgz#b15b0a85dfd4d930d43e69ed605943c802785076"
+ integrity sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==
+
"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
version "4.0.0"
resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz"
@@ -4079,11 +4238,21 @@ psl@^1.1.33:
resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24"
integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==
+punycode@1.3.2:
+ version "1.3.2"
+ resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d"
+ integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=
+
punycode@^2.1.0, punycode@^2.1.1:
version "2.1.1"
resolved "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz"
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
+querystring@0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620"
+ integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=
+
queue-microtask@^1.2.2:
version "1.2.3"
resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz"
@@ -4393,6 +4562,11 @@ safe-buffer@~5.1.0, safe-buffer@~5.1.1:
resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
+sax@1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.1.tgz#7b8e656190b228e81a66aea748480d828cd2d37a"
+ integrity sha1-e45lYZCyKOgaZq6nSEgNgozS03o=
+
sax@>=0.6.0:
version "1.2.4"
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
@@ -5083,6 +5257,14 @@ uri-js@^4.2.2:
dependencies:
punycode "^2.1.0"
+url@0.10.3:
+ version "0.10.3"
+ resolved "https://registry.yarnpkg.com/url/-/url-0.10.3.tgz#021e4d9c7705f21bbf37d03ceb58767402774c64"
+ integrity sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=
+ dependencies:
+ punycode "1.3.2"
+ querystring "0.2.0"
+
use-composed-ref@^1.0.0:
version "1.2.1"
resolved "https://registry.yarnpkg.com/use-composed-ref/-/use-composed-ref-1.2.1.tgz#9bdcb5ccd894289105da2325e1210079f56bf849"
@@ -5112,6 +5294,11 @@ util-deprecate@^1.0.1, util-deprecate@~1.0.1:
resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz"
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
+uuid@3.3.2:
+ version "3.3.2"
+ resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131"
+ integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==
+
uuid@8.3.2, uuid@^8.3.0, uuid@^8.3.2:
version "8.3.2"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
@@ -5206,6 +5393,14 @@ wrappy@1:
resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz"
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
+xml2js@0.4.19:
+ version "0.4.19"
+ resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.19.tgz#686c20f213209e94abf0d1bcf1efaa291c7827a7"
+ integrity sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==
+ dependencies:
+ sax ">=0.6.0"
+ xmlbuilder "~9.0.1"
+
xml2js@^0.4.19:
version "0.4.23"
resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.23.tgz#a0c69516752421eb2ac758ee4d4ccf58843eac66"
@@ -5219,6 +5414,11 @@ xmlbuilder@~11.0.0:
resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3"
integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==
+xmlbuilder@~9.0.1:
+ version "9.0.7"
+ resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d"
+ integrity sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=
+
xtend@^4.0.0:
version "4.0.2"
resolved "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz"
diff --git a/zip-env.d.ts b/zip-env.d.ts
index 3fbf3dce..1c7a6dac 100644
--- a/zip-env.d.ts
+++ b/zip-env.d.ts
@@ -1,11 +1,13 @@
import type { PrismaClient } from '@prisma/client';
-import type { Config } from './src/lib/types';
+import type { Datasource } from 'lib/datasource';
+import type { Config } from '.lib/types';
declare global {
namespace NodeJS {
interface Global {
prisma: PrismaClient;
config: Config;
+ datasource: Datasource
}
interface ProcessEnv {