refactor: some minor changes
This commit is contained in:
parent
cd04773504
commit
d8cec0bc2d
15
src/AppDatabase.ts
Normal file
15
src/AppDatabase.ts
Normal 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');
|
||||||
|
}
|
||||||
|
}
|
@ -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' }));
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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' } ] });
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user