feat: auditing for bubble edits
This commit is contained in:
parent
b51c7bded3
commit
3f28cfcfd8
10 changed files with 320 additions and 23 deletions
|
@ -28,7 +28,7 @@ export default function Button({
|
|||
'transition-colors'
|
||||
);
|
||||
if (!noDefaultColous) {
|
||||
classes.push('bg-slate-600', 'text-slate-50', 'hover:bg-slate-500');
|
||||
classes.push('bg-slate-600', 'text-slate-50', 'hover:bg-slate-500', 'disabled:bg-slate-900', 'disabled:text-slate-200');
|
||||
}
|
||||
return <button className={classes.join(' ')} {...props} />;
|
||||
}
|
||||
|
|
|
@ -6,8 +6,6 @@ export default function NavBar() {
|
|||
const { data, status } = useSession();
|
||||
const router = useRouter();
|
||||
|
||||
console.log(data);
|
||||
|
||||
return (
|
||||
<nav className='flex flex-row h-12 bg-gray-900 justify-end items-center p-4 gap-1 shadow mb-4'>
|
||||
{status === 'loading' && <>...</>}
|
||||
|
|
11
next-auth.d.ts
vendored
Normal file
11
next-auth.d.ts
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
import NextAuth, { DefaultSession } from "next-auth"
|
||||
|
||||
declare module "next-auth" {
|
||||
interface Session {
|
||||
user: {
|
||||
/** The user's internal ID. */
|
||||
id: string;
|
||||
} & DefaultSession["user"]
|
||||
}
|
||||
}
|
||||
|
|
@ -17,6 +17,8 @@
|
|||
"@fortawesome/react-fontawesome": "^0.2.0",
|
||||
"@next-auth/prisma-adapter": "^1.0.4",
|
||||
"@prisma/client": "^4.1.0",
|
||||
"diff": "^5.1.0",
|
||||
"discord.js": "^14.0.3",
|
||||
"next": "12.2.2",
|
||||
"next-auth": "^4.10.1",
|
||||
"react": "18.2.0",
|
||||
|
@ -26,6 +28,7 @@
|
|||
"zod": "^3.17.10"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/diff": "^5.0.2",
|
||||
"@types/node": "18.0.6",
|
||||
"@types/react": "18.0.15",
|
||||
"@types/react-dom": "18.0.6",
|
||||
|
|
|
@ -30,6 +30,12 @@ export const authOptions: NextAuthOptions = {
|
|||
params.account['not-before-policy'] = undefined;
|
||||
return true;
|
||||
},
|
||||
session(params) {
|
||||
if (params.session.user) {
|
||||
params.session.user.id = params.user.id;
|
||||
}
|
||||
return params.session;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ import { ComicBubble, Prisma } from '@prisma/client';
|
|||
import { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { unstable_getServerSession } from 'next-auth';
|
||||
import { z } from 'zod';
|
||||
import { auditPageUpdate } from '../../src/audit';
|
||||
import { prisma } from '../../src/db';
|
||||
import { authOptions } from './auth/[...nextauth]';
|
||||
|
||||
|
@ -23,6 +24,8 @@ const PageBubblesSchema = z.object({
|
|||
pageId: z.number(),
|
||||
});
|
||||
|
||||
export type PageBubbles = z.infer<typeof PageBubblesSchema>;
|
||||
|
||||
export default async function handler(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse
|
||||
|
@ -31,7 +34,7 @@ export default async function handler(
|
|||
return res.status(405).json({ error: 'method not allowed' });
|
||||
|
||||
const session = await unstable_getServerSession(req, res, authOptions);
|
||||
if (!session)
|
||||
if (!session || !session.user)
|
||||
return res.status(401).json({ error: 'authentication required' });
|
||||
|
||||
const data = PageBubblesSchema.parse(req.body);
|
||||
|
@ -41,6 +44,9 @@ export default async function handler(
|
|||
comicId: data.comicId,
|
||||
id: data.pageId,
|
||||
},
|
||||
include: {
|
||||
bubbles: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!page) return res.status(404).json({ error: 'not found' });
|
||||
|
@ -92,5 +98,22 @@ export default async function handler(
|
|||
}
|
||||
await prisma.$transaction(ops);
|
||||
|
||||
const after = await prisma.comicPage.update({
|
||||
where: {
|
||||
comicId_id: {
|
||||
comicId: page.comicId,
|
||||
id: page.id,
|
||||
},
|
||||
},
|
||||
data: {
|
||||
lastEditedById: session.user.id,
|
||||
},
|
||||
include: {
|
||||
bubbles: true,
|
||||
}
|
||||
});
|
||||
|
||||
await auditPageUpdate(page, after, data, session.user);
|
||||
|
||||
res.status(201).json({});
|
||||
}
|
||||
|
|
189
pnpm-lock.yaml
189
pnpm-lock.yaml
|
@ -7,11 +7,14 @@ specifiers:
|
|||
'@fortawesome/react-fontawesome': ^0.2.0
|
||||
'@next-auth/prisma-adapter': ^1.0.4
|
||||
'@prisma/client': ^4.1.0
|
||||
'@types/diff': ^5.0.2
|
||||
'@types/node': 18.0.6
|
||||
'@types/react': 18.0.15
|
||||
'@types/react-dom': 18.0.6
|
||||
'@typescript-eslint/eslint-plugin': ^5.31.0
|
||||
autoprefixer: ^10.4.7
|
||||
diff: ^5.1.0
|
||||
discord.js: ^14.0.3
|
||||
eslint: 8.20.0
|
||||
eslint-config-next: 12.2.2
|
||||
next: 12.2.2
|
||||
|
@ -35,6 +38,8 @@ dependencies:
|
|||
'@fortawesome/react-fontawesome': 0.2.0_990294e19fa18431d2a389426fd0d6bc
|
||||
'@next-auth/prisma-adapter': 1.0.4_9818e1f55ecb9769742928f823304203
|
||||
'@prisma/client': 4.1.0_prisma@4.1.0
|
||||
diff: 5.1.0
|
||||
discord.js: 14.0.3
|
||||
next: 12.2.2_react-dom@18.2.0+react@18.2.0
|
||||
next-auth: 4.10.1_react-dom@18.2.0+react@18.2.0
|
||||
react: 18.2.0
|
||||
|
@ -44,6 +49,7 @@ dependencies:
|
|||
zod: 3.17.10
|
||||
|
||||
devDependencies:
|
||||
'@types/diff': 5.0.2
|
||||
'@types/node': 18.0.6
|
||||
'@types/react': 18.0.15
|
||||
'@types/react-dom': 18.0.6
|
||||
|
@ -141,6 +147,35 @@ packages:
|
|||
'@jridgewell/trace-mapping': 0.3.9
|
||||
dev: true
|
||||
|
||||
/@discordjs/builders/1.0.0:
|
||||
resolution: {integrity: sha512-8y91ZfpOHubiGJu5tVyGI9tQCEyHZDTeqUWVcJd0dq7B96xIf84S0L4fwmD1k9zTe1eqEFSk0gc7BpY+FKn7Ww==}
|
||||
engines: {node: '>=16.9.0'}
|
||||
dependencies:
|
||||
'@sapphire/shapeshift': 3.5.1
|
||||
discord-api-types: 0.36.3
|
||||
fast-deep-equal: 3.1.3
|
||||
ts-mixer: 6.0.1
|
||||
tslib: 2.4.0
|
||||
dev: false
|
||||
|
||||
/@discordjs/collection/1.0.0:
|
||||
resolution: {integrity: sha512-nAxDQYE5dNAzEGQ7HU20sujDsG5vLowUKCEqZkKUIlrXERZFTt/60zKUj/g4+AVCGeq+pXC5hivMaNtiC+PY5Q==}
|
||||
engines: {node: '>=16.9.0'}
|
||||
dev: false
|
||||
|
||||
/@discordjs/rest/1.0.0:
|
||||
resolution: {integrity: sha512-uDAvnE0P2a8axMdD4C51EGjvCRQ2HZk2Yxf6vHWZgIqG87D8DGKMPwmquIxrrB07MjV+rwci2ObU+mGhGP+bJg==}
|
||||
engines: {node: '>=16.9.0'}
|
||||
dependencies:
|
||||
'@discordjs/collection': 1.0.0
|
||||
'@sapphire/async-queue': 1.3.2
|
||||
'@sapphire/snowflake': 3.2.2
|
||||
discord-api-types: 0.36.3
|
||||
file-type: 17.1.4
|
||||
tslib: 2.4.0
|
||||
undici: 5.8.0
|
||||
dev: false
|
||||
|
||||
/@emotion/babel-plugin/11.9.2:
|
||||
resolution: {integrity: sha512-Pr/7HGH6H6yKgnVFNEj2MVlreu3ADqftqjqwUvDy/OJzKFgxKeTQ+eeUf20FOTuHVkDON2iNa25rAXVYtWJCjw==}
|
||||
peerDependencies:
|
||||
|
@ -497,12 +532,34 @@ packages:
|
|||
resolution: {integrity: sha512-LwzQKA4vzIct1zNZzBmRKI9QuNpLgTQMEjsQLf3BXuGYb3QPTP4Yjf6mkdX+X1mYttZ808QpOwAzZjv28kq7DA==}
|
||||
dev: true
|
||||
|
||||
/@sapphire/async-queue/1.3.2:
|
||||
resolution: {integrity: sha512-rUpMLATsoAMnlN3gecAcr9Ecnw1vG7zi5Xr+IX22YzRzi1k9PF9vKzoT8RuEJbiIszjcimu3rveqUnvwDopz8g==}
|
||||
engines: {node: '>=v14.0.0', npm: '>=7.0.0'}
|
||||
dev: false
|
||||
|
||||
/@sapphire/shapeshift/3.5.1:
|
||||
resolution: {integrity: sha512-7JFsW5IglyOIUQI1eE0g6h06D/Far6HqpcowRScgCiLSqTf3hhkPWCWotVTtVycnDCMYIwPeaw6IEPBomKC8pA==}
|
||||
engines: {node: '>=v14.0.0', npm: '>=7.0.0'}
|
||||
dependencies:
|
||||
fast-deep-equal: 3.1.3
|
||||
lodash.uniqwith: 4.5.0
|
||||
dev: false
|
||||
|
||||
/@sapphire/snowflake/3.2.2:
|
||||
resolution: {integrity: sha512-ula2O0kpSZtX9rKXNeQMrHwNd7E4jPDJYUXmEGTFdMRfyfMw+FPyh04oKMjAiDuOi64bYgVkOV3MjK+loImFhQ==}
|
||||
engines: {node: '>=v14.0.0', npm: '>=7.0.0'}
|
||||
dev: false
|
||||
|
||||
/@swc/helpers/0.4.2:
|
||||
resolution: {integrity: sha512-556Az0VX7WR6UdoTn4htt/l3zPQ7bsQWK+HqdG4swV7beUCxo/BqmvbOpUkTIm/9ih86LIf1qsUnywNL3obGHw==}
|
||||
dependencies:
|
||||
tslib: 2.4.0
|
||||
dev: false
|
||||
|
||||
/@tokenizer/token/0.3.0:
|
||||
resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==}
|
||||
dev: false
|
||||
|
||||
/@tsconfig/node10/1.0.9:
|
||||
resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==}
|
||||
dev: true
|
||||
|
@ -519,6 +576,10 @@ packages:
|
|||
resolution: {integrity: sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==}
|
||||
dev: true
|
||||
|
||||
/@types/diff/5.0.2:
|
||||
resolution: {integrity: sha512-uw8eYMIReOwstQ0QKF0sICefSy8cNO/v7gOTiIy9SbwuHyEecJUm7qlgueOO5S1udZ5I/irVydHVwMchgzbKTg==}
|
||||
dev: true
|
||||
|
||||
/@types/json-schema/7.0.11:
|
||||
resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==}
|
||||
dev: true
|
||||
|
@ -529,7 +590,6 @@ packages:
|
|||
|
||||
/@types/node/18.0.6:
|
||||
resolution: {integrity: sha512-/xUq6H2aQm261exT6iZTMifUySEt4GR5KX8eYyY+C4MSNPqSh9oNIP7tz2GLKTlFaiBbgZNxffoR3CVRG+cljw==}
|
||||
dev: true
|
||||
|
||||
/@types/parse-json/4.0.0:
|
||||
resolution: {integrity: sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==}
|
||||
|
@ -560,6 +620,12 @@ packages:
|
|||
/@types/scheduler/0.16.2:
|
||||
resolution: {integrity: sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==}
|
||||
|
||||
/@types/ws/8.5.3:
|
||||
resolution: {integrity: sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==}
|
||||
dependencies:
|
||||
'@types/node': 18.0.6
|
||||
dev: false
|
||||
|
||||
/@typescript-eslint/eslint-plugin/5.31.0_eslint@8.20.0+typescript@4.7.4:
|
||||
resolution: {integrity: sha512-VKW4JPHzG5yhYQrQ1AzXgVgX8ZAJEvCz0QI6mLRX4tf7rnFfh5D8SKm0Pq6w5PyNfAWJk6sv313+nEt3ohWMBQ==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
|
@ -1126,6 +1192,11 @@ packages:
|
|||
engines: {node: '>=0.3.1'}
|
||||
dev: true
|
||||
|
||||
/diff/5.1.0:
|
||||
resolution: {integrity: sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==}
|
||||
engines: {node: '>=0.3.1'}
|
||||
dev: false
|
||||
|
||||
/dir-glob/3.0.1:
|
||||
resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==}
|
||||
engines: {node: '>=8'}
|
||||
|
@ -1133,6 +1204,30 @@ packages:
|
|||
path-type: 4.0.0
|
||||
dev: true
|
||||
|
||||
/discord-api-types/0.36.3:
|
||||
resolution: {integrity: sha512-bz/NDyG0KBo/tY14vSkrwQ/n3HKPf87a0WFW/1M9+tXYK+vp5Z5EksawfCWo2zkAc6o7CClc0eff1Pjrqznlwg==}
|
||||
dev: false
|
||||
|
||||
/discord.js/14.0.3:
|
||||
resolution: {integrity: sha512-wH/VQl4CqN8/+dcXEtYis1iurqxGlDpEe0O4CqH5FGqZGIjVpTdtK0STXXx7bVNX8MT/0GvLZLkmO/5gLDWZVg==}
|
||||
engines: {node: '>=16.9.0'}
|
||||
dependencies:
|
||||
'@discordjs/builders': 1.0.0
|
||||
'@discordjs/collection': 1.0.0
|
||||
'@discordjs/rest': 1.0.0
|
||||
'@sapphire/snowflake': 3.2.2
|
||||
'@types/ws': 8.5.3
|
||||
discord-api-types: 0.36.3
|
||||
fast-deep-equal: 3.1.3
|
||||
lodash.snakecase: 4.1.1
|
||||
tslib: 2.4.0
|
||||
undici: 5.8.0
|
||||
ws: 8.8.1
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- utf-8-validate
|
||||
dev: false
|
||||
|
||||
/dlv/1.1.3:
|
||||
resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==}
|
||||
dev: true
|
||||
|
@ -1483,7 +1578,6 @@ packages:
|
|||
|
||||
/fast-deep-equal/3.1.3:
|
||||
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
|
||||
dev: true
|
||||
|
||||
/fast-glob/3.2.11:
|
||||
resolution: {integrity: sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==}
|
||||
|
@ -1522,6 +1616,15 @@ packages:
|
|||
engines: {node: '>=8'}
|
||||
dev: false
|
||||
|
||||
/file-type/17.1.4:
|
||||
resolution: {integrity: sha512-3w/rJUUPBj6CYhVER3D5JCKwYJJiC36uj5dP+LnyubHI6H6FJo1TeWVCEA09YLVoZqV3/mLP26j9+Pz1GjAyjQ==}
|
||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||
dependencies:
|
||||
readable-web-to-node-stream: 3.0.2
|
||||
strtok3: 7.0.0-alpha.9
|
||||
token-types: 5.0.0-alpha.2
|
||||
dev: false
|
||||
|
||||
/fill-range/7.0.1:
|
||||
resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==}
|
||||
engines: {node: '>=8'}
|
||||
|
@ -1706,6 +1809,10 @@ packages:
|
|||
resolution: {integrity: sha512-slx8Q6oywCCSfKgPgL0sEsXtPVnSbTLWpyiDcu6msHOyKOLari1TD1qocXVCft80umnkk3/Qqh3lwoFt8T/BPQ==}
|
||||
dev: false
|
||||
|
||||
/ieee754/1.2.1:
|
||||
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
|
||||
dev: false
|
||||
|
||||
/ignore/5.2.0:
|
||||
resolution: {integrity: sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==}
|
||||
engines: {node: '>= 4'}
|
||||
|
@ -1954,6 +2061,14 @@ packages:
|
|||
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
|
||||
dev: true
|
||||
|
||||
/lodash.snakecase/4.1.1:
|
||||
resolution: {integrity: sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==}
|
||||
dev: false
|
||||
|
||||
/lodash.uniqwith/4.5.0:
|
||||
resolution: {integrity: sha512-7lYL8bLopMoy4CTICbxygAUq6CdRJ36vFc80DucPueUee+d5NBRxz3FdT9Pes/HEx5mPoT9jwnsEJWz1N7uq7Q==}
|
||||
dev: false
|
||||
|
||||
/loose-envify/1.4.0:
|
||||
resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
|
||||
hasBin: true
|
||||
|
@ -2276,6 +2391,11 @@ packages:
|
|||
resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
/peek-readable/5.0.0-alpha.5:
|
||||
resolution: {integrity: sha512-pJohF/tDwV3ntnT5+EkUo4E700q/j/OCDuPxtM+5/kFGjyOai/sK4/We4Cy1MB2OiTQliWU5DxPvYIKQAdPqAA==}
|
||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||
dev: false
|
||||
|
||||
/picocolors/1.0.0:
|
||||
resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
|
||||
|
||||
|
@ -2491,6 +2611,22 @@ packages:
|
|||
pify: 2.3.0
|
||||
dev: true
|
||||
|
||||
/readable-stream/3.6.0:
|
||||
resolution: {integrity: sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==}
|
||||
engines: {node: '>= 6'}
|
||||
dependencies:
|
||||
inherits: 2.0.4
|
||||
string_decoder: 1.3.0
|
||||
util-deprecate: 1.0.2
|
||||
dev: false
|
||||
|
||||
/readable-web-to-node-stream/3.0.2:
|
||||
resolution: {integrity: sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==}
|
||||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
readable-stream: 3.6.0
|
||||
dev: false
|
||||
|
||||
/readdirp/3.6.0:
|
||||
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
|
||||
engines: {node: '>=8.10.0'}
|
||||
|
@ -2563,6 +2699,10 @@ packages:
|
|||
resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==}
|
||||
dev: false
|
||||
|
||||
/safe-buffer/5.2.1:
|
||||
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
|
||||
dev: false
|
||||
|
||||
/scheduler/0.23.0:
|
||||
resolution: {integrity: sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==}
|
||||
dependencies:
|
||||
|
@ -2645,6 +2785,12 @@ packages:
|
|||
es-abstract: 1.20.1
|
||||
dev: true
|
||||
|
||||
/string_decoder/1.3.0:
|
||||
resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
|
||||
dependencies:
|
||||
safe-buffer: 5.2.1
|
||||
dev: false
|
||||
|
||||
/strip-ansi/6.0.1:
|
||||
resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
|
||||
engines: {node: '>=8'}
|
||||
|
@ -2662,6 +2808,14 @@ packages:
|
|||
engines: {node: '>=8'}
|
||||
dev: true
|
||||
|
||||
/strtok3/7.0.0-alpha.9:
|
||||
resolution: {integrity: sha512-G8WxjBFjTZ77toVElv1i7k3jCXNkBB14FVaZ/6LIOka/WGo4La5XHLrU7neFVLdKbXESZf4BejVKZu5maOmocA==}
|
||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||
dependencies:
|
||||
'@tokenizer/token': 0.3.0
|
||||
peek-readable: 5.0.0-alpha.5
|
||||
dev: false
|
||||
|
||||
/styled-jsx/5.0.2_react@18.2.0:
|
||||
resolution: {integrity: sha512-LqPQrbBh3egD57NBcHET4qcgshPks+yblyhPlH2GY8oaDgKs8SK4C3dBh3oSJjgzJ3G5t1SYEZGHkP+QEpX9EQ==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
|
@ -2772,10 +2926,22 @@ packages:
|
|||
is-number: 7.0.0
|
||||
dev: true
|
||||
|
||||
/token-types/5.0.0-alpha.2:
|
||||
resolution: {integrity: sha512-EsG9UxAW4M6VATrEEjhPFTKEUi1OiJqTUMIZOGBN49fGxYjZB36k0p7to3HZSmWRoHm1QfZgrg3e02fpqAt5fQ==}
|
||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||
dependencies:
|
||||
'@tokenizer/token': 0.3.0
|
||||
ieee754: 1.2.1
|
||||
dev: false
|
||||
|
||||
/tr46/0.0.3:
|
||||
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
|
||||
dev: false
|
||||
|
||||
/ts-mixer/6.0.1:
|
||||
resolution: {integrity: sha512-hvE+ZYXuINrx6Ei6D6hz+PTim0Uf++dYbK9FFifLNwQj+RwKquhQpn868yZsCtJYiclZF1u8l6WZxxKi+vv7Rg==}
|
||||
dev: false
|
||||
|
||||
/ts-node/10.9.1_98dbbc3ccd2e3c16131a870f4ea5cdb2:
|
||||
resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==}
|
||||
hasBin: true
|
||||
|
@ -2861,6 +3027,11 @@ packages:
|
|||
which-boxed-primitive: 1.0.2
|
||||
dev: true
|
||||
|
||||
/undici/5.8.0:
|
||||
resolution: {integrity: sha512-1F7Vtcez5w/LwH2G2tGnFIihuWUlc58YidwLiCv+jR2Z50x0tNXpRRw7eOIJ+GvqCqIkg9SB7NWAJ/T9TLfv8Q==}
|
||||
engines: {node: '>=12.18'}
|
||||
dev: false
|
||||
|
||||
/update-browserslist-db/1.0.5_browserslist@4.21.2:
|
||||
resolution: {integrity: sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q==}
|
||||
hasBin: true
|
||||
|
@ -2888,7 +3059,6 @@ packages:
|
|||
|
||||
/util-deprecate/1.0.2:
|
||||
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
|
||||
dev: true
|
||||
|
||||
/uuid/8.3.2:
|
||||
resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==}
|
||||
|
@ -2940,6 +3110,19 @@ packages:
|
|||
/wrappy/1.0.2:
|
||||
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
|
||||
|
||||
/ws/8.8.1:
|
||||
resolution: {integrity: sha512-bGy2JzvzkPowEJV++hF07hAD6niYSr0JzBNo/J29WsB57A2r7Wlc1UFcTR9IzrPvuNVO4B8LGqF8qcpsVOhJCA==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
peerDependencies:
|
||||
bufferutil: ^4.0.1
|
||||
utf-8-validate: ^5.0.2
|
||||
peerDependenciesMeta:
|
||||
bufferutil:
|
||||
optional: true
|
||||
utf-8-validate:
|
||||
optional: true
|
||||
dev: false
|
||||
|
||||
/xtend/4.0.2:
|
||||
resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==}
|
||||
engines: {node: '>=0.4'}
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
-- RedefineTables
|
||||
PRAGMA foreign_keys=OFF;
|
||||
CREATE TABLE "new_ComicPage" (
|
||||
"id" INTEGER NOT NULL,
|
||||
"comicId" INTEGER NOT NULL,
|
||||
"title" TEXT NOT NULL,
|
||||
"url" TEXT NOT NULL,
|
||||
"imageUrl" TEXT NOT NULL,
|
||||
"width" INTEGER NOT NULL,
|
||||
"height" INTEGER NOT NULL,
|
||||
"lastEditedById" TEXT,
|
||||
|
||||
PRIMARY KEY ("comicId", "id"),
|
||||
CONSTRAINT "ComicPage_comicId_fkey" FOREIGN KEY ("comicId") REFERENCES "Comic" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
|
||||
CONSTRAINT "ComicPage_lastEditedById_fkey" FOREIGN KEY ("lastEditedById") REFERENCES "User" ("id") ON DELETE SET NULL ON UPDATE CASCADE
|
||||
);
|
||||
INSERT INTO "new_ComicPage" ("comicId", "height", "id", "imageUrl", "title", "url", "width") SELECT "comicId", "height", "id", "imageUrl", "title", "url", "width" FROM "ComicPage";
|
||||
DROP TABLE "ComicPage";
|
||||
ALTER TABLE "new_ComicPage" RENAME TO "ComicPage";
|
||||
CREATE UNIQUE INDEX "ComicPage_comicId_id_key" ON "ComicPage"("comicId", "id");
|
||||
PRAGMA foreign_key_check;
|
||||
PRAGMA foreign_keys=ON;
|
|
@ -21,40 +21,43 @@ model Comic {
|
|||
|
||||
model ComicPage {
|
||||
id Int
|
||||
comic Comic @relation(fields: [comicId], references: [id])
|
||||
comic Comic @relation(fields: [comicId], references: [id])
|
||||
comicId Int
|
||||
title String
|
||||
url String
|
||||
imageUrl String
|
||||
width Int
|
||||
height Int
|
||||
bubbles ComicBubble[]
|
||||
bubbles ComicBubble[]
|
||||
|
||||
lastEditedBy User? @relation(fields: [lastEditedById], references: [id])
|
||||
lastEditedById String?
|
||||
|
||||
@@id([comicId, id])
|
||||
@@unique([comicId, id])
|
||||
}
|
||||
|
||||
model ComicCharacter {
|
||||
id Int @id @default(autoincrement())
|
||||
comic Comic @relation(fields: [comicId], references: [id])
|
||||
id Int @id @default(autoincrement())
|
||||
comic Comic @relation(fields: [comicId], references: [id])
|
||||
comicId Int
|
||||
shortName String
|
||||
longName String @unique
|
||||
bubbles ComicBubble[]
|
||||
longName String @unique
|
||||
bubbles ComicBubble[]
|
||||
}
|
||||
|
||||
model ComicBubble {
|
||||
comicId Int
|
||||
page ComicPage @relation(fields: [comicId, pageId], references: [comicId, id])
|
||||
pageId Int
|
||||
idx Int
|
||||
comicId Int
|
||||
page ComicPage @relation(fields: [comicId, pageId], references: [comicId, id])
|
||||
pageId Int
|
||||
idx Int
|
||||
|
||||
character ComicCharacter @relation(fields: [characterId], references: [id])
|
||||
character ComicCharacter @relation(fields: [characterId], references: [id])
|
||||
characterId Int
|
||||
|
||||
areaX Int
|
||||
areaY Int
|
||||
areaWidth Int
|
||||
areaX Int
|
||||
areaY Int
|
||||
areaWidth Int
|
||||
areaHeight Int
|
||||
|
||||
content String
|
||||
|
@ -94,11 +97,13 @@ model Session {
|
|||
}
|
||||
|
||||
model User {
|
||||
id String @id @default(cuid())
|
||||
id String @id @default(cuid())
|
||||
name String?
|
||||
email String? @unique
|
||||
email String? @unique
|
||||
emailVerified DateTime?
|
||||
image String?
|
||||
accounts Account[]
|
||||
sessions Session[]
|
||||
|
||||
lastEditedPages ComicPage[]
|
||||
}
|
||||
|
|
46
src/audit.ts
Normal file
46
src/audit.ts
Normal file
|
@ -0,0 +1,46 @@
|
|||
import { ComicBubble, ComicPage } from "@prisma/client";
|
||||
import { WebhookClient } from "discord.js";
|
||||
import { Session } from "next-auth";
|
||||
import { PageBubbles } from "../pages/api/submit-page-bubbles";
|
||||
import { createPatch } from 'diff';
|
||||
|
||||
type Page = ComicPage & { bubbles: ComicBubble[] };
|
||||
|
||||
const DJS_CLIENT = new WebhookClient({
|
||||
url: process.env.AUDIT_WEBHOOK!,
|
||||
}, {
|
||||
allowedMentions: {
|
||||
parse: [],
|
||||
},
|
||||
})
|
||||
|
||||
export async function auditPageUpdate(before: Page, after: Page, update: PageBubbles, user: Session['user']) {
|
||||
const beforeS = JSON.stringify(before, null, 2);
|
||||
const afterS = JSON.stringify(after, null, 2);
|
||||
if (beforeS === afterS) {
|
||||
return;
|
||||
}
|
||||
|
||||
const patch = createPatch(`/comic/${update.comicId}/${update.pageId}`, beforeS, afterS);
|
||||
await DJS_CLIENT.send({
|
||||
embeds: [
|
||||
{
|
||||
title: 'Page update',
|
||||
description: '```patch\n' + patch + '\n```',
|
||||
fields: [
|
||||
{
|
||||
name: 'Comic',
|
||||
value: update.comicId.toString(),
|
||||
},
|
||||
{
|
||||
name: 'Page',
|
||||
value: update.pageId.toString(),
|
||||
},
|
||||
],
|
||||
footer: {
|
||||
text: `${user.name} (${user.id})`,
|
||||
},
|
||||
}
|
||||
]
|
||||
})
|
||||
}
|
Loading…
Reference in a new issue