feat(api): add support for invisible images
This commit is contained in:
parent
912f716362
commit
3daac34d3e
9 changed files with 81 additions and 56 deletions
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "zip3",
|
"name": "zip3",
|
||||||
"version": "3.2.0",
|
"version": "3.2.1",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prepare": "husky install",
|
"prepare": "husky install",
|
||||||
"dev": "NODE_ENV=development node server",
|
"dev": "NODE_ENV=development node server",
|
||||||
|
@ -20,7 +20,7 @@
|
||||||
"@material-ui/data-grid": "^4.0.0-alpha.32",
|
"@material-ui/data-grid": "^4.0.0-alpha.32",
|
||||||
"@material-ui/icons": "^5.0.0-alpha.37",
|
"@material-ui/icons": "^5.0.0-alpha.37",
|
||||||
"@material-ui/styles": "^5.0.0-alpha.35",
|
"@material-ui/styles": "^5.0.0-alpha.35",
|
||||||
"@prisma/client": "^2.30.0",
|
"@prisma/client": "^2.30.3",
|
||||||
"@reduxjs/toolkit": "^1.6.0",
|
"@reduxjs/toolkit": "^1.6.0",
|
||||||
"argon2": "^0.28.2",
|
"argon2": "^0.28.2",
|
||||||
"colorette": "^1.2.2",
|
"colorette": "^1.2.2",
|
||||||
|
@ -29,8 +29,8 @@
|
||||||
"fecha": "^4.2.1",
|
"fecha": "^4.2.1",
|
||||||
"formik": "^2.2.9",
|
"formik": "^2.2.9",
|
||||||
"multer": "^1.4.2",
|
"multer": "^1.4.2",
|
||||||
"next": "11.1.0",
|
"next": "11.1.1",
|
||||||
"prisma": "^2.30.0",
|
"prisma": "^2.30.3",
|
||||||
"react": "17.0.2",
|
"react": "17.0.2",
|
||||||
"react-dom": "17.0.2",
|
"react-dom": "17.0.2",
|
||||||
"react-dropzone": "^11.3.2",
|
"react-dropzone": "^11.3.2",
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Image" ADD COLUMN "embed" BOOLEAN NOT NULL DEFAULT false;
|
|
@ -43,6 +43,7 @@ model Image {
|
||||||
created_at DateTime @default(now())
|
created_at DateTime @default(now())
|
||||||
views Int @default(0)
|
views Int @default(0)
|
||||||
favorite Boolean @default(false)
|
favorite Boolean @default(false)
|
||||||
|
embed Boolean @default(false)
|
||||||
invisible InvisibleImage?
|
invisible InvisibleImage?
|
||||||
user User @relation(fields: [userId], references: [id])
|
user User @relation(fields: [userId], references: [id])
|
||||||
userId Int
|
userId Int
|
||||||
|
|
|
@ -60,14 +60,16 @@ function shouldUseYarn() {
|
||||||
const prisma = new PrismaClient();
|
const prisma = new PrismaClient();
|
||||||
|
|
||||||
const srv = createServer(async (req, res) => {
|
const srv = createServer(async (req, res) => {
|
||||||
if (req.url.startsWith(config.uploader.route)) {
|
if (req.url.startsWith('/raw')) {
|
||||||
const parts = req.url.split('/');
|
const parts = req.url.split('/');
|
||||||
if (!parts[2] || parts[2] === '') return;
|
if (!parts[2] || parts[2] === '') return;
|
||||||
|
|
||||||
let image = await prisma.image.findFirst({
|
let image = await prisma.image.findFirst({
|
||||||
where: {
|
where: {
|
||||||
OR: { file: parts[2] },
|
OR: [
|
||||||
OR: { invisible: { invis: decodeURI(parts[2]) } }
|
{ file: parts[2] },
|
||||||
|
{ invisible:{ invis: decodeURI(parts[2]) } }
|
||||||
|
]
|
||||||
},
|
},
|
||||||
select: {
|
select: {
|
||||||
mimetype: true,
|
mimetype: true,
|
||||||
|
|
|
@ -14,7 +14,6 @@ module.exports = async config => {
|
||||||
path('core.port', 'number'),
|
path('core.port', 'number'),
|
||||||
path('core.database_url', 'string'),
|
path('core.database_url', 'string'),
|
||||||
path('uploader.route', 'string'),
|
path('uploader.route', 'string'),
|
||||||
path('uploader.embed_route', 'string'),
|
|
||||||
path('uploader.length', 'number'),
|
path('uploader.length', 'number'),
|
||||||
path('uploader.directory', 'string'),
|
path('uploader.directory', 'string'),
|
||||||
path('uploader.admin_limit', 'number'),
|
path('uploader.admin_limit', 'number'),
|
||||||
|
|
|
@ -11,12 +11,11 @@ const envValues = [
|
||||||
e('PORT', 'number', (c, v) => c.core.port = v),
|
e('PORT', 'number', (c, v) => c.core.port = v),
|
||||||
e('DATABASE_URL', 'string', (c, v) => c.core.database_url = v),
|
e('DATABASE_URL', 'string', (c, v) => c.core.database_url = v),
|
||||||
e('UPLOADER_ROUTE', 'string', (c, v) => c.uploader.route = v),
|
e('UPLOADER_ROUTE', 'string', (c, v) => c.uploader.route = v),
|
||||||
e('UPLOADER_EMBED_ROUTE', 'string', (c, v) => c.uploader.embed_route = v),
|
|
||||||
e('UPLOADER_LENGTH', 'number', (c, v) => c.uploader.length = v),
|
e('UPLOADER_LENGTH', 'number', (c, v) => c.uploader.length = v),
|
||||||
e('UPLOADER_DIRECTORY', 'string', (c, v) => c.uploader.directory = 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_ADMIN_LIMIT', 'number', (c, v) => c.uploader.admin_limit = v),
|
||||||
e('UPLOADER_USER_LIMIT', 'number', (c, v) => c.uploader.user_limit = v),
|
e('UPLOADER_USER_LIMIT', 'number', (c, v) => c.uploader.user_limit = v),
|
||||||
e('UPLOADER_DISABLED_EXTS', 'array', (c, v) => c.uploader.disabled_extentions = v),
|
e('UPLOADER_DISABLED_EXTS', 'array', (c, v) => v ? c.uploader.disabled_extentions = v : c.uploader.disabled_extentions = [], false),
|
||||||
];
|
];
|
||||||
|
|
||||||
module.exports = () => {
|
module.exports = () => {
|
||||||
|
@ -43,7 +42,6 @@ function tryReadEnv() {
|
||||||
},
|
},
|
||||||
uploader: {
|
uploader: {
|
||||||
route: undefined,
|
route: undefined,
|
||||||
embed_route: undefined,
|
|
||||||
length: undefined,
|
length: undefined,
|
||||||
directory: undefined,
|
directory: undefined,
|
||||||
admin_limit: undefined,
|
admin_limit: undefined,
|
||||||
|
@ -63,10 +61,12 @@ function tryReadEnv() {
|
||||||
}
|
}
|
||||||
|
|
||||||
envValues[i].fn(config, value);
|
envValues[i].fn(config, value);
|
||||||
if (envValue.type === 'number') value = parseToNumber(value);
|
if (envValue.required) {
|
||||||
else if (envValue.type === 'boolean') value = parseToBoolean(value);
|
if (envValue.type === 'number') value = parseToNumber(value);
|
||||||
else if (envValue.type === 'array') value = parseToArray(value);
|
else if (envValue.type === 'boolean') value = parseToBoolean(value);
|
||||||
envValues[i].fn(config, value);
|
else if (envValue.type === 'array') value = parseToArray(value);
|
||||||
|
envValues[i].fn(config, value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React from 'react';
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
import Head from 'next/head';
|
import Head from 'next/head';
|
||||||
import { GetServerSideProps } from 'next';
|
import { GetServerSideProps } from 'next';
|
||||||
import { Box } from '@material-ui/core';
|
import { Box } from '@material-ui/core';
|
||||||
|
@ -8,31 +8,47 @@ import prisma from 'lib/prisma';
|
||||||
export default function EmbeddedImage({ image, title, username, color, normal, embed }) {
|
export default function EmbeddedImage({ image, title, username, color, normal, embed }) {
|
||||||
const dataURL = (route: string) => `${route}/${image.file}`;
|
const dataURL = (route: string) => `${route}/${image.file}`;
|
||||||
|
|
||||||
|
const updateImage = () => {
|
||||||
|
const original = new Image;
|
||||||
|
original.src = dataURL('/raw');
|
||||||
|
|
||||||
|
const imageEl = document.getElementById('image_content') as HTMLImageElement;
|
||||||
|
imageEl.width = Math.floor(original.width * Math.min((innerHeight / original.height), (innerWidth / original.width)));
|
||||||
|
imageEl.height = innerHeight;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (typeof window !== 'undefined') window.onresize = () => updateImage();
|
||||||
|
|
||||||
|
useEffect(() => updateImage(), []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Head>
|
<Head>
|
||||||
{title ? (
|
{embed && (
|
||||||
<>
|
<>
|
||||||
<meta property='og:site_name' content={`${image.file} • ${username}`} />
|
{title ? (
|
||||||
<meta property='og:title' content={title} />
|
<>
|
||||||
|
<meta property='og:site_name' content={`${image.file} • ${username}`} />
|
||||||
|
<meta property='og:title' content={title} />
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<meta property='og:title' content={`${image.file} • ${username}`} />
|
||||||
|
)}
|
||||||
|
<meta property='theme-color' content={color}/>
|
||||||
|
<meta property='og:url' content={dataURL(normal)} />
|
||||||
|
<meta property='og:image' content={dataURL('/raw')} />
|
||||||
|
<meta property='twitter:card' content='summary_large_image' />
|
||||||
</>
|
</>
|
||||||
) : (
|
|
||||||
<meta property='og:title' content={`${image.file} • ${username}`} />
|
|
||||||
)}
|
)}
|
||||||
<meta property='theme-color' content={color}/>
|
|
||||||
<meta property='og:url' content={dataURL(embed)} />
|
|
||||||
<meta property='og:image' content={dataURL(normal)} />
|
|
||||||
<meta property='twitter:card' content='summary_large_image' />
|
|
||||||
<title>{image.file}</title>
|
<title>{image.file}</title>
|
||||||
</Head>
|
</Head>
|
||||||
|
|
||||||
<Box
|
<Box
|
||||||
display='flex'
|
display='flex'
|
||||||
justifyContent='center'
|
justifyContent='center'
|
||||||
alignItems='center'
|
alignItems='center'
|
||||||
minHeight='100vh'
|
minHeight='100vh'
|
||||||
>
|
>
|
||||||
<img src={dataURL(normal)} alt={dataURL(normal)}/>
|
<img src={dataURL('/raw')} alt={dataURL('/raw')} id='image_content' />
|
||||||
</Box>
|
</Box>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -41,24 +57,28 @@ export default function EmbeddedImage({ image, title, username, color, normal, e
|
||||||
export const getServerSideProps: GetServerSideProps = async (context) => {
|
export const getServerSideProps: GetServerSideProps = async (context) => {
|
||||||
const id = context.params.id[1];
|
const id = context.params.id[1];
|
||||||
const route = context.params.id[0];
|
const route = context.params.id[0];
|
||||||
if (route !== config.uploader.embed_route.substr(1)) return {
|
if (route !== config.uploader.route.substring(1)) return {
|
||||||
notFound: true
|
notFound: true
|
||||||
};
|
};
|
||||||
|
|
||||||
const image = await prisma.image.findFirst({
|
const image = await prisma.image.findFirst({
|
||||||
where: {
|
where: {
|
||||||
file: id
|
OR: [
|
||||||
|
{ file: id },
|
||||||
|
{ invisible: { invis: id } }
|
||||||
|
]
|
||||||
},
|
},
|
||||||
select: {
|
select: {
|
||||||
file: true,
|
|
||||||
mimetype: true,
|
mimetype: true,
|
||||||
userId: true
|
id: true,
|
||||||
|
file: true,
|
||||||
|
invisible: true,
|
||||||
|
userId: true,
|
||||||
|
embed: true
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!image) return {
|
if (!image) return { notFound: true };
|
||||||
notFound: true
|
|
||||||
};
|
|
||||||
|
|
||||||
const user = await prisma.user.findFirst({
|
const user = await prisma.user.findFirst({
|
||||||
select: {
|
select: {
|
||||||
|
@ -74,7 +94,7 @@ export const getServerSideProps: GetServerSideProps = async (context) => {
|
||||||
if (!image.mimetype.startsWith('image')) return {
|
if (!image.mimetype.startsWith('image')) return {
|
||||||
redirect: {
|
redirect: {
|
||||||
permanent: true,
|
permanent: true,
|
||||||
destination: `${config.uploader.route}/${image.file}`,
|
destination: `raw/${image.file}`,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -85,7 +105,7 @@ export const getServerSideProps: GetServerSideProps = async (context) => {
|
||||||
color: user.embedColor,
|
color: user.embedColor,
|
||||||
username: user.username,
|
username: user.username,
|
||||||
normal: config.uploader.route,
|
normal: config.uploader.route,
|
||||||
embed: config.uploader.embed_route
|
embed: image.embed
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -33,7 +33,8 @@ async function handler(req: NextApiReq, res: NextApiRes) {
|
||||||
data: {
|
data: {
|
||||||
file: `${rand}.${ext}`,
|
file: `${rand}.${ext}`,
|
||||||
mimetype: req.file.mimetype,
|
mimetype: req.file.mimetype,
|
||||||
userId: user.id
|
userId: user.id,
|
||||||
|
embed: !!req.headers.embed
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -44,7 +45,7 @@ async function handler(req: NextApiReq, res: NextApiRes) {
|
||||||
Logger.get('image').info(`User ${user.username} (${user.id}) uploaded an image ${image.file} (${image.id})`);
|
Logger.get('image').info(`User ${user.username} (${user.id}) uploaded an image ${image.file} (${image.id})`);
|
||||||
|
|
||||||
return res.json({
|
return res.json({
|
||||||
url: `${zconfig.core.secure ? 'https' : 'http'}://${req.headers.host}${req.headers.embed ? zconfig.uploader.embed_route : zconfig.uploader.route}/${invis ? invis.invis : image.file}`
|
url: `${zconfig.core.secure ? 'https' : 'http'}://${req.headers.host}${zconfig.uploader.route}/${invis ? invis.invis : image.file}`
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
36
yarn.lock
36
yarn.lock
|
@ -599,22 +599,22 @@
|
||||||
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.9.2.tgz#adea7b6953cbb34651766b0548468e743c6a2353"
|
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.9.2.tgz#adea7b6953cbb34651766b0548468e743c6a2353"
|
||||||
integrity sha512-VZMYa7+fXHdwIq1TDhSXoVmSPEGM/aa+6Aiq3nVVJ9bXr24zScr+NlKFKC3iPljA7ho/GAZr+d2jOf5GIRC30Q==
|
integrity sha512-VZMYa7+fXHdwIq1TDhSXoVmSPEGM/aa+6Aiq3nVVJ9bXr24zScr+NlKFKC3iPljA7ho/GAZr+d2jOf5GIRC30Q==
|
||||||
|
|
||||||
"@prisma/client@^2.30.0":
|
"@prisma/client@^2.30.3":
|
||||||
version "2.30.0"
|
version "2.30.3"
|
||||||
resolved "https://registry.yarnpkg.com/@prisma/client/-/client-2.30.0.tgz#b0ed9db67405f619e428577f2d45843104142e00"
|
resolved "https://registry.yarnpkg.com/@prisma/client/-/client-2.30.3.tgz#49c1015e2cec26a44b20c62eb2fd738cb0bb043b"
|
||||||
integrity sha512-tjJNHVfgyNOwS2F+AkjMMCJGPnXzHuUCrOnAMJyidAu4aNzxbJ8jWwjt96rRMpyrg9Hwen3xqqQ2oA+ikK7nhQ==
|
integrity sha512-Ey2miZ+Hne12We3rA8XrlPoAF0iuKEhw5IK2nropaelSt0Ju3b2qSz9Qt50a/1Mx3+7yRSu/iSXt8y9TUMl/Yw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@prisma/engines-version" "2.30.0-28.60b19f4a1de4fe95741da371b4c44a92f4d1adcb"
|
"@prisma/engines-version" "2.30.1-2.b8c35d44de987a9691890b3ddf3e2e7effb9bf20"
|
||||||
|
|
||||||
"@prisma/engines-version@2.30.0-28.60b19f4a1de4fe95741da371b4c44a92f4d1adcb":
|
"@prisma/engines-version@2.30.1-2.b8c35d44de987a9691890b3ddf3e2e7effb9bf20":
|
||||||
version "2.30.0-28.60b19f4a1de4fe95741da371b4c44a92f4d1adcb"
|
version "2.30.1-2.b8c35d44de987a9691890b3ddf3e2e7effb9bf20"
|
||||||
resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-2.30.0-28.60b19f4a1de4fe95741da371b4c44a92f4d1adcb.tgz#1360113dc19e1d43d4442e3b638ccfa0e1711943"
|
resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-2.30.1-2.b8c35d44de987a9691890b3ddf3e2e7effb9bf20.tgz#d5ef55c92beeba56e52bba12b703af0bfd30530d"
|
||||||
integrity sha512-oThNpx7HtJ0eEmnvrWARYcNCs6dqFdAK3Smt2bJVDD6Go4HLuuhjx028osP+rHaFrGOTx7OslLZYtvvFlAXRDA==
|
integrity sha512-/iDRgaoSQC77WN2oDsOM8dn61fykm6tnZUAClY+6p+XJbOEgZ9gy4CKuKTBgrjSGDVjtQ/S2KGcYd3Ring8xaw==
|
||||||
|
|
||||||
"@prisma/engines@2.30.0-28.60b19f4a1de4fe95741da371b4c44a92f4d1adcb":
|
"@prisma/engines@2.30.1-2.b8c35d44de987a9691890b3ddf3e2e7effb9bf20":
|
||||||
version "2.30.0-28.60b19f4a1de4fe95741da371b4c44a92f4d1adcb"
|
version "2.30.1-2.b8c35d44de987a9691890b3ddf3e2e7effb9bf20"
|
||||||
resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-2.30.0-28.60b19f4a1de4fe95741da371b4c44a92f4d1adcb.tgz#b4d91ff876662b1de83e0cc913149a1c088becc7"
|
resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-2.30.1-2.b8c35d44de987a9691890b3ddf3e2e7effb9bf20.tgz#2df768aa7c9f84acaa1f35c970417822233a9fb1"
|
||||||
integrity sha512-LPKq88lIbYezvX0OOc1PU42hHdTsSMPJWmK8lusaHK7DaLHyXjDp/551LbsVapypbjW6N3Jx/If6GoMDASSMSw==
|
integrity sha512-WPnA/IUrxDihrRhdP6+8KAVSwsc0zsh8ioPYsLJjOhzVhwpRbuFH2tJDRIAbc+qFh+BbTIZbeyBYt8fpNXaYQQ==
|
||||||
|
|
||||||
"@reduxjs/toolkit@^1.6.0":
|
"@reduxjs/toolkit@^1.6.0":
|
||||||
version "1.6.0"
|
version "1.6.0"
|
||||||
|
@ -4426,12 +4426,12 @@ prepend-http@^1.0.1:
|
||||||
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc"
|
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc"
|
||||||
integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=
|
integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=
|
||||||
|
|
||||||
prisma@^2.30.0:
|
prisma@^2.30.3:
|
||||||
version "2.30.0"
|
version "2.30.3"
|
||||||
resolved "https://registry.yarnpkg.com/prisma/-/prisma-2.30.0.tgz#5b12091c480d538540b898d364b73651d44b4a01"
|
resolved "https://registry.yarnpkg.com/prisma/-/prisma-2.30.3.tgz#e4a770e1f52151e72c1c5be0aa2e75222a0135c4"
|
||||||
integrity sha512-2XYpSibcVpMd1JDxYypGDU/JKq0W2f/HI1itdddr4Pfg+q6qxt/ItWKcftv4/lqN6u/BVlQ2gDzXVEjpHeO5kQ==
|
integrity sha512-48qYba2BIyUmXuosBZs0g3kYGrxKvo4VkSHYOuLlDdDirmKyvoY2hCYMUYHSx3f++8ovfgs+MX5KmNlP+iAZrQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@prisma/engines" "2.30.0-28.60b19f4a1de4fe95741da371b4c44a92f4d1adcb"
|
"@prisma/engines" "2.30.1-2.b8c35d44de987a9691890b3ddf3e2e7effb9bf20"
|
||||||
|
|
||||||
process-nextick-args@~2.0.0:
|
process-nextick-args@~2.0.0:
|
||||||
version "2.0.1"
|
version "2.0.1"
|
||||||
|
|
Loading…
Reference in a new issue