fix: update node@18, fix views aggregation, force update stats

This commit is contained in:
diced 2022-10-29 10:52:27 -07:00
parent f2be036bac
commit e6cebd8c46
No known key found for this signature in database
GPG key ID: 370BD1BA142842D1
10 changed files with 101 additions and 46 deletions

2
.nvmrc
View file

@ -1 +1 @@
16.16.0 18.12.0

View file

@ -1,16 +1,15 @@
FROM ghcr.io/diced/prisma-binaries:4.5.x as prisma FROM ghcr.io/diced/prisma-binaries:4.5.x as prisma
FROM alpine:3.16 AS deps FROM node:alpine3.16 AS deps
RUN mkdir -p /prisma-engines RUN mkdir -p /prisma-engines
WORKDIR /build WORKDIR /build
COPY .yarn .yarn COPY .yarn .yarn
COPY package.json yarn.lock .yarnrc.yml ./ COPY package.json yarn.lock .yarnrc.yml ./
RUN apk add --no-cache nodejs yarn
RUN yarn install --immutable RUN yarn install --immutable
FROM alpine:3.16 AS builder FROM node:alpine3.16 AS builder
WORKDIR /build WORKDIR /build
COPY --from=prisma /prisma-engines /prisma-engines COPY --from=prisma /prisma-engines /prisma-engines
@ -21,7 +20,7 @@ ENV PRISMA_QUERY_ENGINE_BINARY=/prisma-engines/query-engine \
PRISMA_CLI_QUERY_ENGINE_TYPE=binary \ PRISMA_CLI_QUERY_ENGINE_TYPE=binary \
PRISMA_CLIENT_ENGINE_TYPE=binary PRISMA_CLIENT_ENGINE_TYPE=binary
RUN apk add --no-cache nodejs yarn openssl openssl-dev RUN apk add --no-cache openssl openssl-dev
COPY --from=deps /build/node_modules ./node_modules COPY --from=deps /build/node_modules ./node_modules
COPY src ./src COPY src ./src
@ -34,7 +33,7 @@ ENV NEXT_TELEMETRY_DISABLED 1
RUN yarn build RUN yarn build
FROM alpine:3.16 AS runner FROM node:alpine3.16 AS runner
WORKDIR /zipline WORKDIR /zipline
COPY --from=prisma /prisma-engines /prisma-engines COPY --from=prisma /prisma-engines /prisma-engines
@ -45,7 +44,7 @@ ENV PRISMA_QUERY_ENGINE_BINARY=/prisma-engines/query-engine \
PRISMA_CLI_QUERY_ENGINE_TYPE=binary \ PRISMA_CLI_QUERY_ENGINE_TYPE=binary \
PRISMA_CLIENT_ENGINE_TYPE=binary PRISMA_CLIENT_ENGINE_TYPE=binary
RUN apk add --no-cache nodejs yarn openssl openssl-dev RUN apk add --no-cache openssl openssl-dev
ENV NODE_ENV production ENV NODE_ENV production
ENV NEXT_TELEMETRY_DISABLED 1 ENV NEXT_TELEMETRY_DISABLED 1

View file

@ -65,7 +65,7 @@
"@types/cookie": "^0.5.1", "@types/cookie": "^0.5.1",
"@types/minio": "^7.0.14", "@types/minio": "^7.0.14",
"@types/multer": "^1.4.7", "@types/multer": "^1.4.7",
"@types/node": "16.11.68", "@types/node": "^18.11.7",
"@types/react": "^18.0.24", "@types/react": "^18.0.24",
"@types/sharp": "^0.31.0", "@types/sharp": "^0.31.0",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",

View file

@ -118,7 +118,7 @@ export default function File({ image, updateImages, disableMediaPreview }) {
<Stack> <Stack>
<Type <Type
file={image} file={image}
src={image.url} src={`/r/${image.file}`}
alt={image.file} alt={image.file}
popup popup
sx={{ minHeight: 200 }} sx={{ minHeight: 200 }}
@ -183,7 +183,7 @@ export default function File({ image, updateImages, disableMediaPreview }) {
width: '100%', width: '100%',
cursor: 'pointer', cursor: 'pointer',
}} }}
src={image.url} src={`/r/${image.file}`}
alt={image.file} alt={image.file}
onClick={() => setOpen(true)} onClick={() => setOpen(true)}
disableMediaPreview={disableMediaPreview} disableMediaPreview={disableMediaPreview}

View file

@ -19,6 +19,7 @@ import { randomId, useInterval } from '@mantine/hooks';
import { useModals } from '@mantine/modals'; import { useModals } from '@mantine/modals';
import { showNotification, updateNotification } from '@mantine/notifications'; import { showNotification, updateNotification } from '@mantine/notifications';
import { import {
CheckIcon,
CrossIcon, CrossIcon,
DeleteIcon, DeleteIcon,
FlameshotIcon, FlameshotIcon,
@ -270,6 +271,25 @@ export default function Manage() {
}, },
}); });
const forceUpdateStats = async () => {
const res = await useFetch('/api/stats', 'POST');
if (res.error) {
showNotification({
title: 'Error updating stats',
message: res.error,
color: 'red',
icon: <CrossIcon />,
});
} else {
showNotification({
title: 'Updated stats',
message: '',
color: 'green',
icon: <CheckIcon />,
});
}
};
const interval = useInterval(() => getExports(), 30000); const interval = useInterval(() => getExports(), 30000);
useEffect(() => { useEffect(() => {
getExports(); getExports();
@ -400,6 +420,17 @@ export default function Manage() {
)} )}
</Card> </Card>
{user.administrator && (
<Box mt='md'>
<Title>Server</Title>
<Group>
<Button size='md' onClick={forceUpdateStats} color='red' rightIcon={<RefreshIcon />}>
Force Update Stats
</Button>
</Group>
</Box>
)}
<Title my='md'>Uploaders</Title> <Title my='md'>Uploaders</Title>
<Group> <Group>
<Button size='xl' onClick={() => setShareXOpen(true)} rightIcon={<ShareXIcon />}> <Button size='xl' onClick={() => setShareXOpen(true)} rightIcon={<ShareXIcon />}>

View file

@ -2,11 +2,14 @@ import { NextApiReq, NextApiRes, withZipline } from 'middleware/withZipline';
import prisma from 'lib/prisma'; import prisma from 'lib/prisma';
import config from 'lib/config'; import config from 'lib/config';
import { Stats } from '@prisma/client'; import { Stats } from '@prisma/client';
import { getStats } from 'server/util';
import datasource from 'lib/datasource';
async function handler(req: NextApiReq, res: NextApiRes) { async function handler(req: NextApiReq, res: NextApiRes) {
const user = await req.user(); const user = await req.user();
if (!user) return res.forbid('not logged in'); if (!user) return res.forbid('not logged in');
if (req.method === 'GET') {
let amount = typeof req.query.amount === 'string' ? Number(req.query.amount) : 2; let amount = typeof req.query.amount === 'string' ? Number(req.query.amount) : 2;
if (isNaN(amount)) return res.bad('invalid amount'); if (isNaN(amount)) return res.bad('invalid amount');
@ -32,6 +35,18 @@ async function handler(req: NextApiReq, res: NextApiRes) {
} }
return res.json(stats); return res.json(stats);
} else if (req.method === 'POST') {
if (!user.administrator) return res.forbid('unable to force update stats as a non-admin');
const stats = await getStats(prisma, datasource);
const stats_data = await prisma.stats.create({
data: {
data: stats,
},
});
return res.json(stats_data);
}
} }
export default withZipline(handler); export default withZipline(handler);

View file

@ -3,6 +3,7 @@ import prisma from 'lib/prisma';
import { chunk } from 'lib/util'; import { chunk } from 'lib/util';
import Logger from 'lib/logger'; import Logger from 'lib/logger';
import datasource from 'lib/datasource'; import datasource from 'lib/datasource';
import config from 'lib/config';
async function handler(req: NextApiReq, res: NextApiRes) { async function handler(req: NextApiReq, res: NextApiRes) {
const user = await req.user(); const user = await req.user();
@ -62,7 +63,16 @@ async function handler(req: NextApiReq, res: NextApiRes) {
delete image.password; delete image.password;
return res.json(image); return res.json(image);
} else { } else {
let images = await prisma.image.findMany({ let images: {
favorite: boolean;
created_at: Date;
id: number;
file: string;
mimetype: string;
expires_at: Date;
maxViews: number;
views: number;
}[] = await prisma.image.findMany({
where: { where: {
userId: user.id, userId: user.id,
favorite: !!req.query.favorite, favorite: !!req.query.favorite,
@ -82,8 +92,10 @@ async function handler(req: NextApiReq, res: NextApiRes) {
}, },
}); });
// @ts-ignore for (let i = 0; i !== images.length; ++i) {
images.map((image) => (image.url = `/r/${image.file}`)); (images[i] as unknown as { url: string }).url = `${config.uploader.route}/${images[i].file}`;
}
if (req.query.filter && req.query.filter === 'media') if (req.query.filter && req.query.filter === 'media')
images = images.filter((x) => /^(video|audio|image|text)/.test(x.mimetype)); images = images.filter((x) => /^(video|audio|image|text)/.test(x.mimetype));

View file

@ -28,8 +28,6 @@ async function handler(req: NextApiReq, res: NextApiRes) {
}, },
}); });
// @ts-ignore
images.map((image) => (image.url = `/r/${image.file}`));
if (req.query.filter && req.query.filter === 'media') if (req.query.filter && req.query.filter === 'media')
images = images.filter((x) => /^(video|audio|image)/.test(x.mimetype)); images = images.filter((x) => /^(video|audio|image)/.test(x.mimetype));

View file

@ -68,8 +68,8 @@ export async function getStats(prisma: PrismaClient, datasource: Datasource) {
} }
const count = await prisma.image.count(); const count = await prisma.image.count();
const viewsCount = await prisma.image.groupBy({
by: ['views'], const views = await prisma.image.aggregate({
_sum: { _sum: {
views: true, views: true,
}, },
@ -94,7 +94,7 @@ export async function getStats(prisma: PrismaClient, datasource: Datasource) {
count, count,
count_by_user: count_by_user.sort((a, b) => b.count - a.count), count_by_user: count_by_user.sort((a, b) => b.count - a.count),
count_users, count_users,
views_count: viewsCount[0]?._sum?.views ?? 0, views_count: views?._sum?.views ?? 0,
types_count: types_count.sort((a, b) => b.count - a.count), types_count: types_count.sort((a, b) => b.count - a.count),
}; };
} }

View file

@ -1659,13 +1659,6 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@types/node@npm:16.11.68":
version: 16.11.68
resolution: "@types/node@npm:16.11.68"
checksum: a35293b6b8867e10ab9e10b6cd5f0e4224a86256d5ef102581dcb9f79f35f255ad87443e8584ddf8dce1229ee28885c58187e41822cf8d1e82495e7897c47c3f
languageName: node
linkType: hard
"@types/node@npm:^17.0.10": "@types/node@npm:^17.0.10":
version: 17.0.45 version: 17.0.45
resolution: "@types/node@npm:17.0.45" resolution: "@types/node@npm:17.0.45"
@ -1673,6 +1666,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@types/node@npm:^18.11.7":
version: 18.11.7
resolution: "@types/node@npm:18.11.7"
checksum: 69d630825cf6fbf580d08d76a4d4836ef8c34ed4fe0842221ade87d275f517099cbfabe8e22397208e564bd24926db97699ab9db5c091383269a432b336665e2
languageName: node
linkType: hard
"@types/normalize-package-data@npm:^2.4.0": "@types/normalize-package-data@npm:^2.4.0":
version: 2.4.1 version: 2.4.1
resolution: "@types/normalize-package-data@npm:2.4.1" resolution: "@types/normalize-package-data@npm:2.4.1"
@ -8634,7 +8634,7 @@ __metadata:
"@types/cookie": ^0.5.1 "@types/cookie": ^0.5.1
"@types/minio": ^7.0.14 "@types/minio": ^7.0.14
"@types/multer": ^1.4.7 "@types/multer": ^1.4.7
"@types/node": 16.11.68 "@types/node": ^18.11.7
"@types/react": ^18.0.24 "@types/react": ^18.0.24
"@types/sharp": ^0.31.0 "@types/sharp": ^0.31.0
argon2: ^0.30.1 argon2: ^0.30.1