clonegur/src/routers/UserRouter.ts

95 lines
3.3 KiB
TypeScript
Raw Normal View History

2023-06-26 15:07:25 +00:00
import * as bcrypt from "https://deno.land/x/bcrypt@v0.4.1/mod.ts";
2023-06-27 03:18:19 +00:00
import { auth, body, rest, schema } from "../server/decorators.ts";
2023-06-26 14:06:38 +00:00
import HttpError from "../server/HttpError.ts";
2023-06-26 13:46:12 +00:00
import User from "../models/User.ts";
import AppRouter from "./AppRouter.ts";
2023-06-27 03:18:19 +00:00
import jwt from "../server/decorators/jwt.ts";
import JWT, { JWTPayload } from "../utils/JWT.ts";
2023-06-27 09:34:28 +00:00
import { now } from "../utils/utils.ts";
2023-06-29 14:26:15 +00:00
import { convert } from "../server/decorators/schema.ts";
import AppDatabase from "../AppDatabase.ts";
import { Visibility } from "../models/Image.ts";
2023-06-26 13:46:12 +00:00
export interface LoginRequest {
2023-06-26 13:46:12 +00:00
username: string;
password: string;
}
export default class UserRouter extends AppRouter {
2023-06-29 14:26:15 +00:00
public deserialize(user: User, self = false) {
let images = user.images;
if (!self) {
images = [];
Promise.all(user.images.map(async v => {
if ((await this.db.images.findOne({ _id: v }))?.visibility === Visibility.Public) {
images.push(v);
}
}));
}
return { username: user.username, images };
2023-06-29 10:00:29 +00:00
}
2023-06-27 03:18:19 +00:00
@rest('GET', '/')
2023-06-29 14:26:15 +00:00
async get(@schema('string') username: string, @jwt('salt', true) @auth() jwt: JWTPayload) {
const res = await this.db.users.findOne({ username });
2023-06-26 13:46:12 +00:00
if (res === undefined) throw new HttpError('User not found.');
2023-06-29 14:26:15 +00:00
return this.deserialize(res, jwt.name === username);
2023-06-26 13:46:12 +00:00
}
2023-06-27 03:18:19 +00:00
@rest('GET', '/self')
2023-06-29 14:26:15 +00:00
async self(@jwt('salt', true) @auth() auth: JWTPayload) {
2023-06-27 03:18:19 +00:00
if (auth === undefined) throw new HttpError('You are not logged in.');
2023-06-29 14:26:15 +00:00
const res = await this.db.users.findOne({ username: auth.name });
2023-06-27 03:18:19 +00:00
if (res === undefined) throw new HttpError('User not found.');
2023-06-29 14:26:15 +00:00
return this.deserialize(res);
2023-06-27 03:18:19 +00:00
}
2023-06-26 13:46:12 +00:00
@rest('POST', '/signup')
2023-06-29 14:26:15 +00:00
async signup(@body() raw: unknown) {
const body = await convert(raw, {
username: 'string',
password: 'string',
2023-06-29 14:26:15 +00:00
});
if (await this.db.users.countDocuments({ username: body.username }) > 0) {
2023-06-26 13:46:12 +00:00
throw new HttpError('User with the same username already exists.');
}
const password = await bcrypt.hash(body.password, this.salt);
2023-06-29 14:26:15 +00:00
await this.db.users.insertOne({
2023-06-26 13:46:12 +00:00
username: body.username,
password: password,
2023-06-27 09:34:28 +00:00
images: [],
2023-06-26 13:46:12 +00:00
});
return {};
}
@rest('POST', '/login')
2023-06-29 14:26:15 +00:00
async login(@body() raw: unknown) {
const body = await convert(raw, {
username: 'string',
password: 'string'
2023-06-29 14:26:15 +00:00
});
const res = await this.db.users.findOne({ username: body.username });
if (!res) throw new HttpError('Incorrect username or password.');
2023-06-27 03:55:56 +00:00
const hashed = await bcrypt.hash(body.password, this.salt);
2023-06-27 03:18:19 +00:00
if (res.password !== hashed) throw new HttpError('Incorrect username or password.');
const time = now();
return JWT.encode({
iat: time,
exp: time + 3600 * 12,
name: res.username,
}, this.salt);
}
2023-06-26 13:46:12 +00:00
2023-06-29 14:26:15 +00:00
public constructor(private salt: string, private db: AppDatabase) {
2023-06-26 13:46:12 +00:00
super();
2023-06-29 14:26:15 +00:00
db.users.createIndexes({ indexes: [ { key: { username: 1 }, name: 'Username Index' } ] });
2023-06-26 13:46:12 +00:00
}
}