feat(api): formats for uploaded images

This commit is contained in:
diced 2022-02-20 22:01:31 -08:00
parent 762d2927f7
commit 2b9af0e0de
No known key found for this signature in database
GPG key ID: 85AB64C74535D76E
9 changed files with 53 additions and 13 deletions

View file

@ -16,6 +16,9 @@
- Fast - Fast
- Built with Next.js & React - Built with Next.js & React
- Token protected uploading - Token protected uploading
- Image uploading
- URL shortening
- Text uploading
- Easy setup instructions on [docs](https://zipline.diced.tech/) (One command install `docker-compose up`) - Easy setup instructions on [docs](https://zipline.diced.tech/) (One command install `docker-compose up`)
## Installing ## Installing

View file

@ -8,6 +8,7 @@
"build": "npm-run-all build:schema build:next", "build": "npm-run-all build:schema build:next",
"build:next": "next build", "build:next": "next build",
"build:schema": "prisma generate --schema=prisma/schema.prisma", "build:schema": "prisma generate --schema=prisma/schema.prisma",
"migrate:dev": "prisma migrate dev --create-only",
"start": "node server", "start": "node server",
"lint": "next lint", "lint": "next lint",
"seed": "ts-node --compiler-options \"{\\\"module\\\":\\\"commonjs\\\"}\" --transpile-only prisma/seed.ts" "seed": "ts-node --compiler-options \"{\\\"module\\\":\\\"commonjs\\\"}\" --transpile-only prisma/seed.ts"
@ -39,6 +40,7 @@
"react-redux": "^7.2.4", "react-redux": "^7.2.4",
"redux": "^4.1.0", "redux": "^4.1.0",
"redux-thunk": "^2.3.0", "redux-thunk": "^2.3.0",
"uuid": "^8.3.2",
"yup": "^0.32.9" "yup": "^0.32.9"
}, },
"devDependencies": { "devDependencies": {

View file

@ -0,0 +1,5 @@
-- CreateEnum
CREATE TYPE "ImageFormat" AS ENUM ('UUID', 'DATE', 'RANDOM');
-- AlterTable
ALTER TABLE "Image" ADD COLUMN "format" "ImageFormat" NOT NULL DEFAULT E'RANDOM';

View file

@ -0,0 +1,2 @@
-- AlterEnum
ALTER TYPE "ImageFormat" ADD VALUE 'NAME';

View file

@ -38,6 +38,13 @@ model Theme {
userId Int userId Int
} }
enum ImageFormat {
UUID
DATE
RANDOM
NAME
}
model Image { model Image {
id Int @id @default(autoincrement()) id Int @id @default(autoincrement())
file String file String
@ -47,6 +54,7 @@ model Image {
favorite Boolean @default(false) favorite Boolean @default(false)
embed Boolean @default(false) embed Boolean @default(false)
invisible InvisibleImage? invisible InvisibleImage?
format ImageFormat @default(RANDOM)
user User @relation(fields: [userId], references: [id]) user User @relation(fields: [userId], references: [id])
userId Int userId Int
} }

View file

@ -1,4 +1,4 @@
const NextServer = require('next/dist/server/next-server').default; const next = require('next').default;
const defaultConfig = require('next/dist/server/config-shared').defaultConfig; const defaultConfig = require('next/dist/server/config-shared').defaultConfig;
const { createServer } = require('http'); const { createServer } = require('http');
const { stat, mkdir } = require('fs/promises'); const { stat, mkdir } = require('fs/promises');
@ -41,18 +41,17 @@ async function run() {
await migrations(); await migrations();
await mkdir(config.uploader.directory, { recursive: true }); await mkdir(config.uploader.directory, { recursive: true });
const app = new NextServer({
const app = next({
dir: '.', dir: '.',
dev, dev,
quiet: dev, quiet: !dev,
customServer: false, hostname: config.core.host,
host: config.core.host,
port: config.core.port, port: config.core.port,
conf: Object.assign(defaultConfig, nextConfig), conf: Object.assign(defaultConfig, nextConfig),
}); });
await app.prepare(); await app.prepare();
await stat('./.next');
const handle = app.getRequestHandler(); const handle = app.getRequestHandler();
const prisma = new PrismaClient(); const prisma = new PrismaClient();

View file

@ -126,4 +126,5 @@ module.exports = {
getStats, getStats,
log, log,
sizeOfDir, sizeOfDir,
shouldUseYarn,
}; };

View file

@ -6,6 +6,9 @@ import { createInvisImage, randomChars } from 'lib/util';
import { writeFile } from 'fs/promises'; import { writeFile } from 'fs/promises';
import { join } from 'path'; import { join } from 'path';
import Logger from 'lib/logger'; import Logger from 'lib/logger';
import { ImageFormat, InvisibleImage } from '@prisma/client';
import { format as formatDate } from 'fecha';
import { v4 } from 'uuid';
const uploader = multer({ const uploader = multer({
storage: multer.memoryStorage(), storage: multer.memoryStorage(),
@ -22,29 +25,46 @@ async function handler(req: NextApiReq, res: NextApiRes) {
}); });
if (!user) return res.forbid('authorization incorect'); if (!user) return res.forbid('authorization incorect');
if (user.ratelimited) return res.ratelimited(); if (user.ratelimited) return res.ratelimited();
if (!req.files) return res.error('no files'); if (!req.files) return res.error('no files');
if (req.files && req.files.length === 0) return res.error('no files'); if (req.files && req.files.length === 0) return res.error('no files');
const rawFormat = ((req.headers.format || '') as string).toUpperCase() || 'RANDOM';
const format: ImageFormat = Object.keys(ImageFormat).includes(rawFormat) && ImageFormat[rawFormat];
const files = []; const files = [];
for (let i = 0; i !== req.files.length; ++i) { for (let i = 0; i !== req.files.length; ++i) {
const file = req.files[i]; const file = req.files[i];
if (file.size > zconfig.uploader[user.administrator ? 'admin_limit' : 'user_limit']) return res.error('file size too big'); if (file.size > zconfig.uploader[user.administrator ? 'admin_limit' : 'user_limit']) return res.error(`file[${i}] size too big`);
const ext = file.originalname.split('.').pop(); const ext = file.originalname.split('.').pop();
if (zconfig.uploader.disabled_extentions.includes(ext)) return res.error('disabled extension recieved: ' + ext); if (zconfig.uploader.disabled_extentions.includes(ext)) return res.error('disabled extension recieved: ' + ext);
const rand = randomChars(zconfig.uploader.length); let fileName: string;
let invis; switch (format) {
case ImageFormat.RANDOM:
fileName = randomChars(zconfig.uploader.length);
break;
case ImageFormat.DATE:
fileName = formatDate(new Date(), 'YYYY-MM-DD_HH:mm:ss');
break;
case ImageFormat.UUID:
fileName = v4();
break;
case ImageFormat.NAME:
fileName = file.originalname.split('.')[0];
break;
}
let invis: InvisibleImage;
const image = await prisma.image.create({ const image = await prisma.image.create({
data: { data: {
file: `${rand}.${ext}`, file: `${fileName}.${ext}`,
mimetype: file.mimetype, mimetype: file.mimetype,
userId: user.id, userId: user.id,
embed: !!req.headers.embed, embed: !!req.headers.embed,
format,
}, },
}); });

View file

@ -6346,7 +6346,7 @@ util-deprecate@^1.0.1, util-deprecate@~1.0.1:
resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz"
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
uuid@8.3.2, uuid@^8.3.0: uuid@8.3.2, uuid@^8.3.0, uuid@^8.3.2:
version "8.3.2" version "8.3.2"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==