feat: some user apis and storage stuff
All checks were successful
Lint Codebase / lint (push) Successful in 1m24s

This commit is contained in:
TheClashFruit 2024-09-07 16:08:27 +02:00
parent 4e7c2a1df2
commit c6881a16da
Signed by: TheClashFruit
GPG key ID: 09BB24C34C2F3204
9 changed files with 213 additions and 2 deletions

25
docs/cdn.md Normal file
View file

@ -0,0 +1,25 @@
# CDN Url Structure
Base URL: `https://crss.fra1.cdn.digitaloceanspaces.com/`
## Users
### Avatars
`users/[id]/avatar/[hash].png`
### Banners
`users/[id]/banner/[hash].png`
## Nations
### Flags
`nations/[id]/flag/[hash].svg`
## Gallery
### Images
`gallery/[id]/[hash].png`

View file

@ -14,7 +14,9 @@ export interface User {
accentColor?: number;
discordId: BigInt;
discordId?: BigInt;
subscription: number;
permissions: PermissionNamed[];
badges: BadgeNamed[];

View file

@ -58,6 +58,8 @@ class Database {
discordId: row.discord_id as BigInt,
subscription: row.subscription,
permissions: getPermissions(row.permissions),
badges: getBadges(row.badges),
@ -96,6 +98,8 @@ class Database {
discordId: row.discord_id as BigInt,
subscription: row.subscription,
permissions: getPermissions(row.permissions),
badges: getBadges(row.badges),
@ -127,6 +131,8 @@ class Database {
discordId: row.discord_id as BigInt,
subscription: row.subscription,
permissions: getPermissions(row.permissions),
badges: getBadges(row.badges),
@ -170,6 +176,8 @@ class Database {
discordId: user.discord as BigInt,
subscription: user.subscription,
permissions: getPermissions(user.permissions),
badges: getBadges(user.badges),
@ -223,6 +231,20 @@ class Database {
return token;
}
async getSession(token: string): Promise<any | undefined> {
const [ rows ] = await this.mysqlPool!.query('SELECT * FROM user_sessions WHERE token = ?', [ token ]);
if((rows as any[]).length === 0)
return undefined;
const row = (rows as any[])[0];
if(new Date(row.expires_at) < new Date())
return undefined;
return row;
}
// Meta ----------------
async getTeam(): Promise<(TeamMember | undefined)[]> {

38
lib/Storage.ts Normal file
View file

@ -0,0 +1,38 @@
import { ObjectCannedACL, PutObjectCommand, PutObjectCommandOutput, S3Client } from '@aws-sdk/client-s3';
import { Readable } from 'node:stream';
class S3Storage {
private client;
// change this if it's different for you :3
private bucket: string = 'crss';
constructor() {
this.client = new S3Client({
forcePathStyle: false,
region: process.env.S3_REGION!,
endpoint: process.env.S3_ENDPOINT!,
credentials: {
accessKeyId: process.env.S3_ACCESS_KEY!,
secretAccessKey: process.env.S3_SECRET_KEY!
}
});
}
async uploadFile(key: string, file: (string | Uint8Array | Buffer | Readable), contetnType: string): Promise<PutObjectCommandOutput> {
const res = await this.client.send(new PutObjectCommand({
Bucket: this.bucket,
Key: key,
Body: file,
ContentType: contetnType,
ACL: ObjectCannedACL.public_read
}));
return res;
}
}
export default S3Storage;

View file

@ -1,4 +1,6 @@
import Database from '@/lib/Database';
import Discord from '@/lib/Discord';
import S3Storage from '@/lib/Storage';
import type { NextApiRequest, NextApiResponse } from 'next';
export default async function handler(
@ -6,6 +8,7 @@ export default async function handler(
res: NextApiResponse<any>,
) {
const db = new Database();
const s3 = new S3Storage();
const { code, state } = req.query;
@ -29,7 +32,7 @@ export default async function handler(
});
const json = await data.json();
const token = await db.newSession(json, req.headers['user-agent']!, req.socket.remoteAddress!);
const token = await db.newSession(json, req.headers['user-agent']!, (req.headers['x-forwarded-for'] as string || req.socket.remoteAddress!));
if (!token) {
return res.status(500).json({
@ -41,6 +44,29 @@ export default async function handler(
res.status(200).json({
token
});
try {
const dc = new Discord(json.access_token);
const dcUser = await dc.user();
const user = await db.getUser((await db.getSession(token)).user_id);
if (dcUser.data.avatar) {
const fetchData = await fetch(`https://cdn.discordapp.com/avatars/${dcUser.data.id}/${dcUser.data.avatar}.png?size=1024`);
const buffer = await fetchData.arrayBuffer();
await s3.uploadFile(`users/${user?.id}/avatar/${dcUser.data.avatar}.png`, Buffer.from(buffer), 'image/png');
}
if (dcUser.data.banner) {
const fetchData = await fetch(`https://cdn.discordapp.com/banners/${dcUser.data.id}/${dcUser.data.banner}.png?size=1024`);
const buffer = await fetchData.arrayBuffer();
await s3.uploadFile(`users/${user?.id}/banner/${dcUser.data.banner}.png`, Buffer.from(buffer), 'image/png');
}
} catch (e) {
console.error(e);
}
} catch (e) {
console.error(e);

45
pages/api/v1/user/@me.ts Normal file
View file

@ -0,0 +1,45 @@
import { ErrorResponse, User } from '@/interfaces';
import Database from '@/lib/Database';
import type { NextApiRequest, NextApiResponse } from 'next';
export default async function handler(
req: NextApiRequest,
res: NextApiResponse<User | ErrorResponse>,
) {
const db = new Database();
const { authorization } = req.headers;
const token = authorization?.split(' ')!;
if (!token)
return res.status(401).json({
code: 401,
message: 'Unauthorized'
});
if (token[0] !== 'Bearer' || !token[1])
return res.status(401).json({
code: 401,
message: 'Unauthorized'
});
const session = await db.getSession(token[1]);
if (!session)
return res.status(401).json({
code: 401,
message: 'Unauthorized'
});
const user = await db.getUser(session.user_id);
if (!user)
return res.status(401).json({
code: 401,
message: 'Unauthorized'
});
res.status(200).json(user);
}

View file

View file

53
pages/api/v1/user/[id].ts Normal file
View file

@ -0,0 +1,53 @@
import { ErrorResponse, User } from '@/interfaces';
import Database from '@/lib/Database';
import type { NextApiRequest, NextApiResponse } from 'next';
export default async function handler(
req: NextApiRequest,
res: NextApiResponse<User | ErrorResponse>,
) {
const db = new Database();
const { id } = req.query;
const { authorization } = req.headers;
if ((/^\d+$/).test(id as string)) {
let user = await db.getUser((id as string));
if (!user)
return res.status(404).json({
code: 404,
message: 'User Not Found'
});
// TODO: check if user is admin or itself and show email and discordId
user = {
...user,
email: undefined,
discordId: undefined,
};
res.status(200).json(user);
} else {
let user: any = await db.getUserUsername(id as string);
if (!user)
return res.status(404).json({
code: 404,
message: 'User Not Found'
});
// TODO: check if user is admin or itself and show email and discordId
user = {
...user,
email: undefined,
discordId: undefined
};
res.status(200).json(user);
}
}