refactor: some minor changes

This commit is contained in:
TopchetoEU 2023-06-29 17:26:15 +03:00
parent cd04773504
commit d8cec0bc2d
5 changed files with 64 additions and 49 deletions

15
src/AppDatabase.ts Normal file
View File

@ -0,0 +1,15 @@
import { Collection, Database } from "https://deno.land/x/mongo@v0.31.2/mod.ts";
import Image from "./models/Image.ts";
import User from "./models/User.ts";
export default class AppDatabase {
public readonly db: Database;
public readonly users: Collection<User>;
public readonly images: Collection<Image>;
public constructor(db: Database) {
this.db = db;
this.users = db.collection('users');
this.images = db.collection('images');
}
}

View File

@ -1,6 +1,7 @@
import { MongoClient } from "https://deno.land/x/mongo@v0.31.2/mod.ts"; import { MongoClient } from "https://deno.land/x/mongo@v0.31.2/mod.ts";
import { RootRouter } from "./routers/RootRouter.ts"; import { RootRouter } from "./routers/RootRouter.ts";
import * as bcrypt from "https://deno.land/x/bcrypt@v0.4.1/mod.ts"; import * as bcrypt from "https://deno.land/x/bcrypt@v0.4.1/mod.ts";
import AppDatabase from "./AppDatabase.ts";
export default async function clonegur() { export default async function clonegur() {
let salt; let salt;
@ -17,7 +18,7 @@ export default async function clonegur() {
db: 'clonegur', db: 'clonegur',
servers: [ { host: '127.0.0.1', port: 27017 } ] servers: [ { host: '127.0.0.1', port: 27017 } ]
}); });
await new RootRouter(salt, db).attach(Deno.listen({ port: 4000, hostname: 'localhost' })); return new RootRouter(salt, new AppDatabase(db));
} }
await clonegur(); (await clonegur()).attach(Deno.listen({ port: 4000, hostname: 'localhost' }));

View File

@ -1,16 +1,15 @@
import { Collection } from "https://deno.land/x/mongo@v0.31.2/mod.ts";
import { UUID } from "https://deno.land/x/web_bson@v0.3.0/mod.js"; import { UUID } from "https://deno.land/x/web_bson@v0.3.0/mod.js";
import { auth, body, headers, jwt, page, rest, schema } from "../server/decorators.ts"; import { auth, body, headers, jwt, page, rest, schema } from "../server/decorators.ts";
import { JWTPayload } from "../utils/JWT.ts"; import { JWTPayload } from "../utils/JWT.ts";
import AppRouter from "./AppRouter.ts"; import AppRouter from "./AppRouter.ts";
import HttpError from "../server/HttpError.ts"; import HttpError from "../server/HttpError.ts";
import User from "../models/User.ts";
import Image, { Visibility } from "../models/Image.ts"; import Image, { Visibility } from "../models/Image.ts";
import { Page } from "../server/decorators/page.ts"; import { Page } from "../server/decorators/page.ts";
import { Headers } from "../server/RestRequest.ts"; import { Headers } from "../server/RestRequest.ts";
import { convert } from "../server/decorators/schema.ts"; import { convert } from "../server/decorators/schema.ts";
import { now } from "../utils/utils.ts"; import { now } from "../utils/utils.ts";
import AppDatabase from "../AppDatabase.ts";
export default class ImageRouter extends AppRouter { export default class ImageRouter extends AppRouter {
public static deserialize(image: Image) { public static deserialize(image: Image) {
@ -25,7 +24,7 @@ export default class ImageRouter extends AppRouter {
@rest('GET', '/') @rest('GET', '/')
async get(@schema('uuid') id: UUID, @jwt(v => v.salt, false) @auth() jwt?: JWTPayload) { async get(@schema('uuid') id: UUID, @jwt(v => v.salt, false) @auth() jwt?: JWTPayload) {
const image = await this.images.findOne({ _id: new UUID(id) }); const image = await this.db.images.findOne({ _id: new UUID(id) });
if ( if (
!image || !image ||
@ -36,14 +35,14 @@ export default class ImageRouter extends AppRouter {
} }
@rest('GET', '/feed') @rest('GET', '/feed')
async self(@page() page: Page) { async self(@page() page: Page) {
const res = await page.apply(this.images.find({})).toArray(); const res = await page.apply(this.db.images.find({})).toArray();
if (!res) throw new HttpError('User not found.'); if (!res) throw new HttpError('User not found.');
return res.map(v => ImageRouter.deserialize(v)); return res.map(v => ImageRouter.deserialize(v));
} }
@rest('POST', '/upload') @rest('POST', '/upload')
async upload(@body() body: Blob, @headers() headers: Headers, @jwt(v => v.salt, true) @auth() jwt: JWTPayload) { async upload(@body() body: Blob, @headers() headers: Headers, @jwt(v => v.salt, true) @auth() jwt: JWTPayload) {
const user = await this.users.findOne({ username: jwt.name }); const user = await this.db.users.findOne({ username: jwt.name });
if (!user) throw new HttpError("You don't exist."); if (!user) throw new HttpError("You don't exist.");
// Parse body // Parse body
@ -89,8 +88,8 @@ export default class ImageRouter extends AppRouter {
// Write to DB // Write to DB
try { try {
await this.images.insertOne(img); await this.db.images.insertOne(img);
await this.users.updateOne({ username: user.username }, { $push: { images: img._id } }); await this.db.users.updateOne({ username: user.username }, { $push: { images: img._id } });
return ImageRouter.deserialize(img); return ImageRouter.deserialize(img);
} }
catch (e) { catch (e) {
@ -101,18 +100,18 @@ export default class ImageRouter extends AppRouter {
@rest('POST', '/change') @rest('POST', '/change')
async change(@body() raw: unknown, @jwt(v => v.salt, true) @auth() jwt: JWTPayload) { async change(@body() raw: unknown, @jwt(v => v.salt, true) @auth() jwt: JWTPayload) {
const body = await convert(raw, { id: 'uuid', name: 'string?', visibility: 'number?' }); const body = await convert(raw, { id: 'uuid', name: 'string?', visibility: 'number?' });
const user = await this.users.findOne({ username: jwt.name }); const user = await this.db.users.findOne({ username: jwt.name });
if (!user) throw new HttpError("You don't exist."); if (!user) throw new HttpError("You don't exist.");
const img = await this.images.findOne({ _id: body.id }); const img = await this.db.images.findOne({ _id: body.id });
if (!img) throw new HttpError("Image doesn't exist."); if (!img) throw new HttpError("Image doesn't exist.");
if (user.username !== img.author) throw new HttpError("You don't own the image."); if (user.username !== img.author) throw new HttpError("You don't own the image.");
await this.images.updateOne({ _id: body.id }, { $set: { name: body.name, visibility: body.visibility } }); await this.db.images.updateOne({ _id: body.id }, { $set: { name: body.name, visibility: body.visibility } });
return ImageRouter.deserialize(img); return ImageRouter.deserialize(img);
} }
public constructor(private images: Collection<Image>, private users: Collection<User>) { public constructor(private db: AppDatabase) {
super(); super();
} }
} }

View File

@ -1,12 +1,10 @@
import { Database } from "https://deno.land/x/mongo@v0.31.2/mod.ts";
import UserRouter from "../routers/UserRouter.ts"; import UserRouter from "../routers/UserRouter.ts";
import ImageRouter from "./ImageRouter.ts"; import ImageRouter from "./ImageRouter.ts";
import AppRouter from "./AppRouter.ts"; import AppRouter from "./AppRouter.ts";
import RestResponse from "../server/RestResponse.ts"; import RestResponse from "../server/RestResponse.ts";
import { rest, route } from "../server/decorators.ts"; import { rest, route } from "../server/decorators.ts";
import User from "../models/User.ts";
import Image from "../models/Image.ts";
import { stream } from "../utils/utils.ts"; import { stream } from "../utils/utils.ts";
import AppDatabase from "../AppDatabase.ts";
export class RootRouter extends AppRouter { export class RootRouter extends AppRouter {
@route('users/*') users; @route('users/*') users;
@ -17,13 +15,10 @@ export class RootRouter extends AppRouter {
return new RestResponse().body(stream('Page not found :/')).status(404); return new RestResponse().body(stream('Page not found :/')).status(404);
} }
constructor(salt: string, db: Database) { constructor(salt: string, db: AppDatabase) {
super(); super();
const users = db.collection<User>('users'); this.users = new UserRouter(salt, db);
const images = db.collection<Image>('images'); this.images = new ImageRouter(db);
this.users = new UserRouter(salt, users);
this.images = new ImageRouter(images, users);
} }
} }

View File

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