This commit is contained in:
amy 2024-12-01 23:46:27 +03:30
parent f25011b10d
commit 63b6adfdbe
Signed by: amy
SSH key fingerprint: SHA256:Y6VEv6ZOxI6zqjjOF4luhfaCoY+zDK0w62P+qhQYie4
7 changed files with 209 additions and 15 deletions

1
.gitignore vendored
View file

@ -1,3 +1,4 @@
/node_modules/
/.idea/
.env
./src/uploads

View file

@ -13,7 +13,9 @@
"license": "ISC",
"packageManager": "pnpm@9.12.3+sha512.cce0f9de9c5a7c95bef944169cc5dfe8741abfb145078c0d508b868056848a87c81e626246cb60967cbd7fd29a6c062ef73ff840d96b3c86c40ac92cf4a813ee",
"dependencies": {
"@fastify/cookie": "^11.0.1",
"@fastify/multipart": "^9.0.1",
"@fastify/oauth2": "^8.1.0",
"@prisma/client": "6.0.0",
"dotenv": "^16.4.5",
"fastify": "^5.1.0",

121
pnpm-lock.yaml generated
View file

@ -8,9 +8,15 @@ importers:
.:
dependencies:
'@fastify/cookie':
specifier: ^11.0.1
version: 11.0.1
'@fastify/multipart':
specifier: ^9.0.1
version: 9.0.1
'@fastify/oauth2':
specifier: ^8.1.0
version: 8.1.0
'@prisma/client':
specifier: 6.0.0
version: 6.0.0(prisma@6.0.0)
@ -42,6 +48,9 @@ packages:
'@fastify/busboy@3.0.0':
resolution: {integrity: sha512-83rnH2nCvclWaPQQKvkJ2pdOjG4TZyEVuFDnlOF6KP08lDaaceVyw/W63mDuafQT+MKHCvXIPpE5uYWeM0rT4w==}
'@fastify/cookie@11.0.1':
resolution: {integrity: sha512-n1Ooz4bgQ5LcOlJQboWPfsMNxIrGV0SgU85UkctdpTlCQE0mtA3rlspOPUdqk9ubiiZn053ucnia4DjTquI4/g==}
'@fastify/deepmerge@2.0.0':
resolution: {integrity: sha512-fsaybTGDyQ5KpPsplQqb9yKdCf2x/pbNpMNk8Tvp3rRz7lVcupKysH4b2ELMN2P4Hak1+UqTYdTj/u4FNV2p0g==}
@ -57,6 +66,27 @@ packages:
'@fastify/multipart@9.0.1':
resolution: {integrity: sha512-vt2gOCw/O4EwpN4KlLVJxth4iQlDf7T5ggw2Db2C+UbO2WJBG7y0jEBvu/HT6JIW/lBYaqrrUy9MmTpCKgXEpw==}
'@fastify/oauth2@8.1.0':
resolution: {integrity: sha512-SMcvStTvhF+UcyH+7uLWvKrxM4g5evZafjtQWRAg02C/dlsOrqLK0s9/1aszhWY6PBklJ6jduFPpl+sB4l2DlQ==}
'@hapi/boom@10.0.1':
resolution: {integrity: sha512-ERcCZaEjdH3OgSJlyjVk8pHIFeus91CjKP3v+MpgBNp5IvGzP2l/bRiD78nqYcKPaZdbKkK5vDBVPd2ohHBlsA==}
'@hapi/bourne@3.0.0':
resolution: {integrity: sha512-Waj1cwPXJDucOib4a3bAISsKJVb15MKi9IvmTI/7ssVEm6sywXGjVJDhl6/umt1pK1ZS7PacXU3A1PmFKHEZ2w==}
'@hapi/hoek@11.0.7':
resolution: {integrity: sha512-HV5undWkKzcB4RZUusqOpcgxOaq6VOAH7zhhIr2g3G8NF/MlFO75SjOr2NfuSx0Mh40+1FqCkagKLJRykUWoFQ==}
'@hapi/hoek@9.3.0':
resolution: {integrity: sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==}
'@hapi/topo@5.1.0':
resolution: {integrity: sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==}
'@hapi/wreck@18.1.0':
resolution: {integrity: sha512-0z6ZRCmFEfV/MQqkQomJ7sl/hyxvcZM7LtuVqN3vdAO4vM9eBbowl0kaqQj9EJJQab+3Uuh1GxbGIBFy4NfJ4w==}
'@jridgewell/resolve-uri@3.1.2':
resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
engines: {node: '>=6.0.0'}
@ -91,6 +121,15 @@ packages:
'@prisma/get-platform@6.0.0':
resolution: {integrity: sha512-PS6nYyIm9g8C03E4y7LknOfdCw/t2KyEJxntMPQHQZCOUgOpF82Ma60mdlOD08w90I3fjLiZZ0+MadenR3naDQ==}
'@sideway/address@4.1.5':
resolution: {integrity: sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==}
'@sideway/formula@3.0.1':
resolution: {integrity: sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==}
'@sideway/pinpoint@2.0.0':
resolution: {integrity: sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==}
'@tsconfig/node10@1.0.11':
resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==}
@ -146,6 +185,15 @@ packages:
create-require@1.1.1:
resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==}
debug@4.3.7:
resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==}
engines: {node: '>=6.0'}
peerDependencies:
supports-color: '*'
peerDependenciesMeta:
supports-color:
optional: true
diff@4.0.2:
resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==}
engines: {node: '>=0.3.1'}
@ -202,6 +250,9 @@ packages:
resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==}
engines: {node: '>= 0.10'}
joi@17.13.3:
resolution: {integrity: sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==}
json-schema-ref-resolver@1.0.1:
resolution: {integrity: sha512-EJAj1pgHc1hxF6vo2Z3s69fMjO1INq6eGHXZ8Z6wCQeldCuwxGK9Sxf4/cScGn3FZubCVUehfWtcDM/PLteCQw==}
@ -214,6 +265,9 @@ packages:
make-error@1.3.6:
resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==}
ms@2.1.3:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
on-exit-leak-free@2.1.2:
resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==}
engines: {node: '>=14.0.0'}
@ -333,6 +387,9 @@ packages:
set-cookie-parser@2.7.1:
resolution: {integrity: sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==}
simple-oauth2@5.1.0:
resolution: {integrity: sha512-gWDa38Ccm4MwlG5U7AlcJxPv3lvr80dU7ARJWrGdgvOKyzSj1gr3GBPN1rABTedAYvC/LsGYoFuFxwDBPtGEbw==}
sonic-boom@4.2.0:
resolution: {integrity: sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==}
@ -394,6 +451,11 @@ snapshots:
'@fastify/busboy@3.0.0': {}
'@fastify/cookie@11.0.1':
dependencies:
cookie: 1.0.2
fastify-plugin: 5.0.1
'@fastify/deepmerge@2.0.0': {}
'@fastify/error@4.0.0': {}
@ -414,6 +476,34 @@ snapshots:
fastify-plugin: 5.0.1
secure-json-parse: 3.0.1
'@fastify/oauth2@8.1.0':
dependencies:
'@fastify/cookie': 11.0.1
fastify-plugin: 5.0.1
simple-oauth2: 5.1.0
transitivePeerDependencies:
- supports-color
'@hapi/boom@10.0.1':
dependencies:
'@hapi/hoek': 11.0.7
'@hapi/bourne@3.0.0': {}
'@hapi/hoek@11.0.7': {}
'@hapi/hoek@9.3.0': {}
'@hapi/topo@5.1.0':
dependencies:
'@hapi/hoek': 9.3.0
'@hapi/wreck@18.1.0':
dependencies:
'@hapi/boom': 10.0.1
'@hapi/bourne': 3.0.0
'@hapi/hoek': 11.0.7
'@jridgewell/resolve-uri@3.1.2': {}
'@jridgewell/sourcemap-codec@1.5.0': {}
@ -448,6 +538,14 @@ snapshots:
dependencies:
'@prisma/debug': 6.0.0
'@sideway/address@4.1.5':
dependencies:
'@hapi/hoek': 9.3.0
'@sideway/formula@3.0.1': {}
'@sideway/pinpoint@2.0.0': {}
'@tsconfig/node10@1.0.11': {}
'@tsconfig/node12@1.0.11': {}
@ -492,6 +590,10 @@ snapshots:
create-require@1.1.1: {}
debug@4.3.7:
dependencies:
ms: 2.1.3
diff@4.0.2: {}
dotenv@16.4.5: {}
@ -557,6 +659,14 @@ snapshots:
ipaddr.js@1.9.1: {}
joi@17.13.3:
dependencies:
'@hapi/hoek': 9.3.0
'@hapi/topo': 5.1.0
'@sideway/address': 4.1.5
'@sideway/formula': 3.0.1
'@sideway/pinpoint': 2.0.0
json-schema-ref-resolver@1.0.1:
dependencies:
fast-deep-equal: 3.1.3
@ -571,6 +681,8 @@ snapshots:
make-error@1.3.6: {}
ms@2.1.3: {}
on-exit-leak-free@2.1.2: {}
pg-cloudflare@1.1.1:
@ -677,6 +789,15 @@ snapshots:
set-cookie-parser@2.7.1: {}
simple-oauth2@5.1.0:
dependencies:
'@hapi/hoek': 11.0.7
'@hapi/wreck': 18.1.0
debug: 4.3.7
joi: 17.13.3
transitivePeerDependencies:
- supports-color
sonic-boom@4.2.0:
dependencies:
atomic-sleep: 1.0.0

View file

@ -0,0 +1,8 @@
/*
Warnings:
- The required column `apiToken` was added to the `User` table with a prisma-level default value. This is not possible if the table is not empty. Please add this column as optional, then populate it before making it required.
*/
-- AlterTable
ALTER TABLE "User" ADD COLUMN "apiToken" UUID NOT NULL;

View file

@ -18,8 +18,9 @@ model WhitelistedUsers {
}
model User {
id String @id
id String @id
discordToken String
apiToken String @default(uuid()) @db.Uuid
imagePosts FilePost[]
}
@ -27,5 +28,5 @@ model FilePost {
id String @id @default(uuid()) @db.Uuid
fileName String
userId String
user User @relation(fields: [userId], references: [id])
user User @relation(fields: [userId], references: [id])
}

View file

@ -2,7 +2,7 @@ import {DiscordUser} from "./types.ts";
import {FastifyRequest} from "fastify";
export function getAdmins(): string[] {
return process.env.ADMINS.split(":")
return process.env.ADMINS.split(",")
}
export async function isTokenValid(token: string):Promise<boolean> {
@ -17,6 +17,16 @@ export async function isTokenValid(token: string):Promise<boolean> {
});
}
export async function getUser(token: string): Promise<DiscordUser> {
const response = await fetch("https://discord.com/api/users/@me", {
headers: new Headers({
"Authorization": `Bearer ${token}`
})
});
return response.json();
}
export async function isAdmin(token: string): Promise<boolean> {
const response = await fetch("https://discord.com/api/users/@me", {
headers: new Headers({

View file

@ -1,12 +1,13 @@
import Fastify from 'fastify'
import { PrismaClient } from '@prisma/client'
import {PrismaClient} from '@prisma/client'
import fastifyMultipart from "@fastify/multipart";
import fastifyOauth2 from "@fastify/oauth2";
import fs from 'fs'
import path from 'path'
import dotenv from 'dotenv';
import { fileURLToPath } from 'url';
import {isAdmin, isTokenValid} from "./authHelper.ts";
import {fileURLToPath} from 'url';
import {checkadmin} from "./middleware.ts";
import {getUser} from "./authHelper.ts";
dotenv.config();
@ -19,17 +20,68 @@ export const __filename = fileURLToPath(import.meta.url);
export const __dirname = path.dirname(__filename);
const prisma = new PrismaClient()
const fastify = Fastify({ logger: true })
const fastify = Fastify({logger: true})
// Register fastify-multipart for file uploads
fastify.register(fastifyMultipart)
fastify.register(fastifyOauth2, {
name: 'discordOAuth2',
credentials: {
client: {
id: process.env.DISCORD_CLIENT,
secret: process.env.DISCORD_SECRET,
},
auth: fastifyOauth2.DISCORD_CONFIGURATION
},
scope: ["identify"],
startRedirectPath: '/login',
callbackUri: req => `${req.protocol}://${req.host}/login/callback`,
})
// Upload directory
const uploadDir = path.join(__dirname, 'uploads')
if (!fs.existsSync(uploadDir)) {
fs.mkdirSync(uploadDir)
}
fastify.get('/login/callback', async function (request, reply) {
// @ts-ignore
const {token} = await this.discordOAuth2?.getAccessTokenFromAuthorizationCodeFlow(request)
//this is funny
const discordAccount = await getUser(token.access_token)
if (!await prisma.whitelistedUsers.findUnique({
where: {
discordId: discordAccount.id
}
})) {
return reply.status(401).send("Not Authorized")
}
// @ts-ignore
const existingUser = await prisma.users.findUnique({
where: {id: discordAccount.id},
});
// @ts-ignore
const {token: refreshToken} = await this.discordOAuth2?.getNewAccessTokenUsingRefreshToken(token)
if (!existingUser) {
await prisma.user.create({
data: {
id: discordAccount.id,
discordToken: refreshToken.access_token,
}
})
reply.send({access_token: refreshToken.access_token})
reply.setCookie('access_token', refreshToken.access_token)
} else {
}
})
fastify.post('/api/admin/addwhitelist', async (request, reply) => {
const test = await checkadmin(request)
if (!test) return reply.status(401).send('Not Authorized');
@ -38,16 +90,15 @@ fastify.post('/api/admin/addwhitelist', async (request, reply) => {
reply.status(400).send('Bad Request');
}
await prisma.whitelistedUsers.create({
data: {
discordId: id,
}
data: {
discordId: id,
}
}).then(() => {
reply.status(200).send('User added successfully');
})
})
fastify.get('/', async (request, reply) => {
return reply.status(200).send('ok')
})
@ -75,11 +126,11 @@ fastify.post('/api/upload', async (request, reply) => {
// reply.code(201).send(newImagePost)
} catch (error) {
fastify.log.error(error)
reply.code(500).send({ error: 'Error uploading file or saving record' })
reply.code(500).send({error: 'Error uploading file or saving record'})
}
})
fastify.listen({ port: 3000 }, (err, address) => {
fastify.listen({port: 3000}, (err, address) => {
if (err) {
fastify.log.error(err)
process.exit(1)