feat: some user apis and storage stuff
All checks were successful
Lint Codebase / lint (push) Successful in 1m24s
All checks were successful
Lint Codebase / lint (push) Successful in 1m24s
This commit is contained in:
parent
4e7c2a1df2
commit
c6881a16da
25
docs/cdn.md
Normal file
25
docs/cdn.md
Normal 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`
|
|
@ -14,7 +14,9 @@ export interface User {
|
||||||
|
|
||||||
accentColor?: number;
|
accentColor?: number;
|
||||||
|
|
||||||
discordId: BigInt;
|
discordId?: BigInt;
|
||||||
|
|
||||||
|
subscription: number;
|
||||||
|
|
||||||
permissions: PermissionNamed[];
|
permissions: PermissionNamed[];
|
||||||
badges: BadgeNamed[];
|
badges: BadgeNamed[];
|
||||||
|
|
|
@ -58,6 +58,8 @@ class Database {
|
||||||
|
|
||||||
discordId: row.discord_id as BigInt,
|
discordId: row.discord_id as BigInt,
|
||||||
|
|
||||||
|
subscription: row.subscription,
|
||||||
|
|
||||||
permissions: getPermissions(row.permissions),
|
permissions: getPermissions(row.permissions),
|
||||||
badges: getBadges(row.badges),
|
badges: getBadges(row.badges),
|
||||||
|
|
||||||
|
@ -96,6 +98,8 @@ class Database {
|
||||||
|
|
||||||
discordId: row.discord_id as BigInt,
|
discordId: row.discord_id as BigInt,
|
||||||
|
|
||||||
|
subscription: row.subscription,
|
||||||
|
|
||||||
permissions: getPermissions(row.permissions),
|
permissions: getPermissions(row.permissions),
|
||||||
badges: getBadges(row.badges),
|
badges: getBadges(row.badges),
|
||||||
|
|
||||||
|
@ -127,6 +131,8 @@ class Database {
|
||||||
|
|
||||||
discordId: row.discord_id as BigInt,
|
discordId: row.discord_id as BigInt,
|
||||||
|
|
||||||
|
subscription: row.subscription,
|
||||||
|
|
||||||
permissions: getPermissions(row.permissions),
|
permissions: getPermissions(row.permissions),
|
||||||
badges: getBadges(row.badges),
|
badges: getBadges(row.badges),
|
||||||
|
|
||||||
|
@ -170,6 +176,8 @@ class Database {
|
||||||
|
|
||||||
discordId: user.discord as BigInt,
|
discordId: user.discord as BigInt,
|
||||||
|
|
||||||
|
subscription: user.subscription,
|
||||||
|
|
||||||
permissions: getPermissions(user.permissions),
|
permissions: getPermissions(user.permissions),
|
||||||
badges: getBadges(user.badges),
|
badges: getBadges(user.badges),
|
||||||
|
|
||||||
|
@ -223,6 +231,20 @@ class Database {
|
||||||
return token;
|
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 ----------------
|
// Meta ----------------
|
||||||
|
|
||||||
async getTeam(): Promise<(TeamMember | undefined)[]> {
|
async getTeam(): Promise<(TeamMember | undefined)[]> {
|
||||||
|
|
38
lib/Storage.ts
Normal file
38
lib/Storage.ts
Normal 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;
|
|
@ -1,4 +1,6 @@
|
||||||
import Database from '@/lib/Database';
|
import Database from '@/lib/Database';
|
||||||
|
import Discord from '@/lib/Discord';
|
||||||
|
import S3Storage from '@/lib/Storage';
|
||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
|
|
||||||
export default async function handler(
|
export default async function handler(
|
||||||
|
@ -6,6 +8,7 @@ export default async function handler(
|
||||||
res: NextApiResponse<any>,
|
res: NextApiResponse<any>,
|
||||||
) {
|
) {
|
||||||
const db = new Database();
|
const db = new Database();
|
||||||
|
const s3 = new S3Storage();
|
||||||
|
|
||||||
const { code, state } = req.query;
|
const { code, state } = req.query;
|
||||||
|
|
||||||
|
@ -29,7 +32,7 @@ export default async function handler(
|
||||||
});
|
});
|
||||||
|
|
||||||
const json = await data.json();
|
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) {
|
if (!token) {
|
||||||
return res.status(500).json({
|
return res.status(500).json({
|
||||||
|
@ -41,6 +44,29 @@ export default async function handler(
|
||||||
res.status(200).json({
|
res.status(200).json({
|
||||||
token
|
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) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
|
|
||||||
|
|
45
pages/api/v1/user/@me.ts
Normal file
45
pages/api/v1/user/@me.ts
Normal 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);
|
||||||
|
}
|
0
pages/api/v1/user/@me/session/[id].ts
Normal file
0
pages/api/v1/user/@me/session/[id].ts
Normal file
0
pages/api/v1/user/@me/sessions.ts
Normal file
0
pages/api/v1/user/@me/sessions.ts
Normal file
53
pages/api/v1/user/[id].ts
Normal file
53
pages/api/v1/user/[id].ts
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue