import * as bcrypt from "https://deno.land/x/bcrypt@v0.4.1/mod.ts"; import { auth, body, rest, schema } from "../server/decorators.ts"; import HttpError from "../server/HttpError.ts"; import User from "../models/User.ts"; import AppRouter from "./AppRouter.ts"; import jwt from "../server/decorators/jwt.ts"; import JWT, { JWTPayload } from "../utils/JWT.ts"; import { now } from "../utils/utils.ts"; import { convert } from "../server/decorators/schema.ts"; import AppDatabase from "../AppDatabase.ts"; import { Visibility } from "../models/Image.ts"; export interface LoginRequest { username: string; password: string; } export default class UserRouter extends AppRouter { 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 }; } @rest('GET', '/') async get(@schema('string') username: string, @jwt('salt', false) @auth() jwt?: JWTPayload) { const res = await this.db.users.findOne({ username }); if (res === undefined) throw new HttpError('User not found.'); return this.deserialize(res, jwt?.name === username); } @rest('GET', '/self') async self(@jwt('salt', true) @auth() auth: JWTPayload) { if (auth === undefined) throw new HttpError('You are not logged in.'); const res = await this.db.users.findOne({ username: auth.name }); if (res === undefined) throw new HttpError('User not found.'); return this.deserialize(res, true); } @rest('POST', '/signup') async signup(@body() raw: unknown) { const body = await convert(raw, { username: 'string', password: 'string', }); if (await this.db.users.countDocuments({ username: body.username }) > 0) { throw new HttpError('User with the same username already exists.'); } const password = await bcrypt.hash(body.password, this.salt); await this.db.users.insertOne({ username: body.username, password: password, images: [], likes: [], }); return {}; } @rest('POST', '/login') async login(@body() raw: unknown) { const body = await convert(raw, { username: 'string', password: 'string' }); const res = await this.db.users.findOne({ username: body.username }); if (!res) throw new HttpError('Incorrect username or password.'); const hashed = await bcrypt.hash(body.password, this.salt); 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); } public constructor(private salt: string, private db: AppDatabase) { super(); db.users.createIndexes({ indexes: [ { key: { username: 1 }, name: 'Username Index' } ] }); } }