fix: update node@18, fix views aggregation, force update stats
This commit is contained in:
parent
f2be036bac
commit
e6cebd8c46
10 changed files with 101 additions and 46 deletions
2
.nvmrc
2
.nvmrc
|
@ -1 +1 @@
|
|||
16.16.0
|
||||
18.12.0
|
11
Dockerfile
11
Dockerfile
|
@ -1,16 +1,15 @@
|
|||
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
|
||||
WORKDIR /build
|
||||
|
||||
COPY .yarn .yarn
|
||||
COPY package.json yarn.lock .yarnrc.yml ./
|
||||
|
||||
RUN apk add --no-cache nodejs yarn
|
||||
RUN yarn install --immutable
|
||||
|
||||
FROM alpine:3.16 AS builder
|
||||
FROM node:alpine3.16 AS builder
|
||||
WORKDIR /build
|
||||
|
||||
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_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 src ./src
|
||||
|
@ -34,7 +33,7 @@ ENV NEXT_TELEMETRY_DISABLED 1
|
|||
|
||||
RUN yarn build
|
||||
|
||||
FROM alpine:3.16 AS runner
|
||||
FROM node:alpine3.16 AS runner
|
||||
WORKDIR /zipline
|
||||
|
||||
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_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 NEXT_TELEMETRY_DISABLED 1
|
||||
|
|
|
@ -65,7 +65,7 @@
|
|||
"@types/cookie": "^0.5.1",
|
||||
"@types/minio": "^7.0.14",
|
||||
"@types/multer": "^1.4.7",
|
||||
"@types/node": "16.11.68",
|
||||
"@types/node": "^18.11.7",
|
||||
"@types/react": "^18.0.24",
|
||||
"@types/sharp": "^0.31.0",
|
||||
"cross-env": "^7.0.3",
|
||||
|
|
|
@ -118,7 +118,7 @@ export default function File({ image, updateImages, disableMediaPreview }) {
|
|||
<Stack>
|
||||
<Type
|
||||
file={image}
|
||||
src={image.url}
|
||||
src={`/r/${image.file}`}
|
||||
alt={image.file}
|
||||
popup
|
||||
sx={{ minHeight: 200 }}
|
||||
|
@ -183,7 +183,7 @@ export default function File({ image, updateImages, disableMediaPreview }) {
|
|||
width: '100%',
|
||||
cursor: 'pointer',
|
||||
}}
|
||||
src={image.url}
|
||||
src={`/r/${image.file}`}
|
||||
alt={image.file}
|
||||
onClick={() => setOpen(true)}
|
||||
disableMediaPreview={disableMediaPreview}
|
||||
|
|
|
@ -19,6 +19,7 @@ import { randomId, useInterval } from '@mantine/hooks';
|
|||
import { useModals } from '@mantine/modals';
|
||||
import { showNotification, updateNotification } from '@mantine/notifications';
|
||||
import {
|
||||
CheckIcon,
|
||||
CrossIcon,
|
||||
DeleteIcon,
|
||||
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);
|
||||
useEffect(() => {
|
||||
getExports();
|
||||
|
@ -400,6 +420,17 @@ export default function Manage() {
|
|||
)}
|
||||
</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>
|
||||
<Group>
|
||||
<Button size='xl' onClick={() => setShareXOpen(true)} rightIcon={<ShareXIcon />}>
|
||||
|
|
|
@ -2,36 +2,51 @@ import { NextApiReq, NextApiRes, withZipline } from 'middleware/withZipline';
|
|||
import prisma from 'lib/prisma';
|
||||
import config from 'lib/config';
|
||||
import { Stats } from '@prisma/client';
|
||||
import { getStats } from 'server/util';
|
||||
import datasource from 'lib/datasource';
|
||||
|
||||
async function handler(req: NextApiReq, res: NextApiRes) {
|
||||
const user = await req.user();
|
||||
if (!user) return res.forbid('not logged in');
|
||||
|
||||
let amount = typeof req.query.amount === 'string' ? Number(req.query.amount) : 2;
|
||||
if (isNaN(amount)) return res.bad('invalid amount');
|
||||
if (req.method === 'GET') {
|
||||
let amount = typeof req.query.amount === 'string' ? Number(req.query.amount) : 2;
|
||||
if (isNaN(amount)) return res.bad('invalid amount');
|
||||
|
||||
// get stats per day
|
||||
// get stats per day
|
||||
|
||||
let stats: Stats[] = await prisma.$queryRaw`
|
||||
SELECT *
|
||||
FROM "Stats" as t JOIN
|
||||
(SELECT MAX(t2."created_at") as max_timestamp
|
||||
FROM "Stats" t2
|
||||
GROUP BY date(t2."created_at")
|
||||
) t2
|
||||
ON t."created_at" = t2.max_timestamp
|
||||
ORDER BY t."created_at" DESC
|
||||
LIMIT ${amount}
|
||||
`;
|
||||
let stats: Stats[] = await prisma.$queryRaw`
|
||||
SELECT *
|
||||
FROM "Stats" as t JOIN
|
||||
(SELECT MAX(t2."created_at") as max_timestamp
|
||||
FROM "Stats" t2
|
||||
GROUP BY date(t2."created_at")
|
||||
) t2
|
||||
ON t."created_at" = t2.max_timestamp
|
||||
ORDER BY t."created_at" DESC
|
||||
LIMIT ${amount}
|
||||
`;
|
||||
|
||||
if (config.website.show_files_per_user) {
|
||||
stats = stats.map((stat) => {
|
||||
(stat.data as any).count_by_user = [];
|
||||
return stat;
|
||||
if (config.website.show_files_per_user) {
|
||||
stats = stats.map((stat) => {
|
||||
(stat.data as any).count_by_user = [];
|
||||
return stat;
|
||||
});
|
||||
}
|
||||
|
||||
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);
|
||||
return res.json(stats_data);
|
||||
}
|
||||
}
|
||||
|
||||
export default withZipline(handler);
|
||||
|
|
|
@ -3,6 +3,7 @@ import prisma from 'lib/prisma';
|
|||
import { chunk } from 'lib/util';
|
||||
import Logger from 'lib/logger';
|
||||
import datasource from 'lib/datasource';
|
||||
import config from 'lib/config';
|
||||
|
||||
async function handler(req: NextApiReq, res: NextApiRes) {
|
||||
const user = await req.user();
|
||||
|
@ -62,7 +63,16 @@ async function handler(req: NextApiReq, res: NextApiRes) {
|
|||
delete image.password;
|
||||
return res.json(image);
|
||||
} 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: {
|
||||
userId: user.id,
|
||||
favorite: !!req.query.favorite,
|
||||
|
@ -82,8 +92,10 @@ async function handler(req: NextApiReq, res: NextApiRes) {
|
|||
},
|
||||
});
|
||||
|
||||
// @ts-ignore
|
||||
images.map((image) => (image.url = `/r/${image.file}`));
|
||||
for (let i = 0; i !== images.length; ++i) {
|
||||
(images[i] as unknown as { url: string }).url = `${config.uploader.route}/${images[i].file}`;
|
||||
}
|
||||
|
||||
if (req.query.filter && req.query.filter === 'media')
|
||||
images = images.filter((x) => /^(video|audio|image|text)/.test(x.mimetype));
|
||||
|
||||
|
|
|
@ -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')
|
||||
images = images.filter((x) => /^(video|audio|image)/.test(x.mimetype));
|
||||
|
||||
|
|
|
@ -68,8 +68,8 @@ export async function getStats(prisma: PrismaClient, datasource: Datasource) {
|
|||
}
|
||||
|
||||
const count = await prisma.image.count();
|
||||
const viewsCount = await prisma.image.groupBy({
|
||||
by: ['views'],
|
||||
|
||||
const views = await prisma.image.aggregate({
|
||||
_sum: {
|
||||
views: true,
|
||||
},
|
||||
|
@ -94,7 +94,7 @@ export async function getStats(prisma: PrismaClient, datasource: Datasource) {
|
|||
count,
|
||||
count_by_user: count_by_user.sort((a, b) => b.count - a.count),
|
||||
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),
|
||||
};
|
||||
}
|
||||
|
|
16
yarn.lock
16
yarn.lock
|
@ -1659,13 +1659,6 @@ __metadata:
|
|||
languageName: node
|
||||
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":
|
||||
version: 17.0.45
|
||||
resolution: "@types/node@npm:17.0.45"
|
||||
|
@ -1673,6 +1666,13 @@ __metadata:
|
|||
languageName: node
|
||||
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":
|
||||
version: 2.4.1
|
||||
resolution: "@types/normalize-package-data@npm:2.4.1"
|
||||
|
@ -8634,7 +8634,7 @@ __metadata:
|
|||
"@types/cookie": ^0.5.1
|
||||
"@types/minio": ^7.0.14
|
||||
"@types/multer": ^1.4.7
|
||||
"@types/node": 16.11.68
|
||||
"@types/node": ^18.11.7
|
||||
"@types/react": ^18.0.24
|
||||
"@types/sharp": ^0.31.0
|
||||
argon2: ^0.30.1
|
||||
|
|
Loading…
Reference in a new issue