From 51a95f9bf1c80e473851e28a60d62ee613f50b52 Mon Sep 17 00:00:00 2001 From: Yanis Rigaudeau Date: Thu, 10 Nov 2022 00:30:52 +0100 Subject: [PATCH] WIP cards --- api/src/app.ts | 9 ++- api/src/entities/card.ts | 30 ++++++++++ api/src/entities/user.ts | 5 ++ api/src/framework/express/card.ts | 66 +++++++++++++++++++++ api/src/framework/express/middleware.ts | 9 ++- api/src/framework/express/router.ts | 2 + api/src/framework/express/schema/card.ts | 29 ++++++++++ api/src/framework/express/schema/user.ts | 18 +++--- api/src/framework/express/server.ts | 11 ++-- api/src/framework/express/user.ts | 27 ++++++++- api/src/framework/mongo/card.ts | 74 ++++++++++++++++++++++++ api/src/framework/mongo/user.ts | 12 +++- api/src/functions/card.ts | 36 ++++++++++++ api/src/functions/user.ts | 11 ++++ core/src/alteration.ts | 12 ++++ core/src/card.ts | 40 +++++++++++++ core/src/index.ts | 2 + core/src/user.ts | 2 + 18 files changed, 372 insertions(+), 23 deletions(-) create mode 100644 api/src/entities/card.ts create mode 100644 api/src/framework/express/card.ts create mode 100644 api/src/framework/express/schema/card.ts create mode 100644 api/src/framework/mongo/card.ts create mode 100644 api/src/functions/card.ts create mode 100644 core/src/alteration.ts create mode 100644 core/src/card.ts diff --git a/api/src/app.ts b/api/src/app.ts index 7ee06ff..82aaa8a 100644 --- a/api/src/app.ts +++ b/api/src/app.ts @@ -4,10 +4,12 @@ import ip from 'ip'; import Server from './framework/express/server'; import UserModel from './framework/mongo/user'; +import CardModel from './framework/mongo/card'; import { Config } from './config'; export type Services = { userModel: UserModel; + cardModel: CardModel; }; const configFile = process.env.CONFIGFILE; @@ -23,6 +25,7 @@ const db = mongo.db(config.mongo.dbName); const services: Services = { userModel: new UserModel(db), + cardModel: new CardModel(db), }; const server = new Server(config.server, config.mongo, services); @@ -34,10 +37,10 @@ process.on('SIGINT', async () => { process.exit(); }); -server.start(() => +server.start().then(() => { console.log( `Running on http://127.0.0.1:${config.server.port} http://${ip.address()}:${ config.server.port }`, - ), -); + ); +}); diff --git a/api/src/entities/card.ts b/api/src/entities/card.ts new file mode 100644 index 0000000..07b5ac0 --- /dev/null +++ b/api/src/entities/card.ts @@ -0,0 +1,30 @@ +import { CardCtor, CardInfo, CardRarity, CombatStats } from '@core'; +import { Entity } from './entity'; + +export class Card extends Entity implements CardInfo { + name: string; + rarity: keyof typeof CardRarity; + stats: CombatStats; + thumbnail: string; + model: string; + + constructor(raw: CardCtor) { + super(raw); + this.name = raw.name || ''; + this.rarity = raw.rarity || CardRarity.COMMON; + stats: raw.stats || {}; + this.thumbnail = raw.thumbnail || ''; + this.model = raw.model || ''; + } + + Info(): CardInfo { + return { + uuid: this.uuid, + name: this.name, + rarity: this.rarity, + stats: this.stats, + thumbnail: this.thumbnail, + model: this.model, + }; + } +} diff --git a/api/src/entities/user.ts b/api/src/entities/user.ts index 5045d78..23b1b6f 100644 --- a/api/src/entities/user.ts +++ b/api/src/entities/user.ts @@ -1,4 +1,5 @@ import { + CardInfo, UserCtor, UserInfo, UserInfoWithPassword, @@ -11,12 +12,14 @@ import { Entity } from './entity'; export class User extends Entity implements UserInfo { username: string; role: keyof typeof UserRoles; + cards: CardInfo[]; constructor(raw: UserCtor) { super(raw); this.username = raw.username || ''; this.role = raw.role || UserRoles.USER; + this.cards = this.cards || ''; } Info(): UserInfo { @@ -24,6 +27,7 @@ export class User extends Entity implements UserInfo { uuid: this.uuid, username: this.username, role: this.role, + cards: this.cards, }; } } @@ -42,6 +46,7 @@ export class UserWithPassword extends User implements UserInfoWithPassword { uuid: this.uuid, username: this.username, role: this.role, + cards: this.cards, password: this.password, }; } diff --git a/api/src/framework/express/card.ts b/api/src/framework/express/card.ts new file mode 100644 index 0000000..6b559c9 --- /dev/null +++ b/api/src/framework/express/card.ts @@ -0,0 +1,66 @@ +import { RequestHandler, Router } from 'express'; +import { CreateCard, ListCards, ReadCard } from '../../functions/card'; +import { Services } from '../../app'; +import { CheckPermissions, getRequestId, ValidateSchema } from './middleware'; +import { CreateCardSchema, ReadCardSchema } from './schema/card'; + +function CreateHandler(services: Services): RequestHandler { + const createCard = CreateCard(services); + + return async (req, res, next) => { + try { + const card = await createCard(getRequestId(req), req.body); + res.status(200).send(card); + } catch (error) { + next({ status: 409, message: 'Card Already Exists' }); + } + }; +} + +function ReadHandler(services: Services): RequestHandler { + const readCard = ReadCard(services); + + return async (req, res, next) => { + try { + const card = await readCard(getRequestId(req), req.params.uuid); + res.status(200).send(card); + } catch (error) { + next({ status: 404, message: 'Card Not Found' }); + } + }; +} + +function ListHandler(services: Services): RequestHandler { + const listCards = ListCards(services); + + return async (req, res, next) => { + try { + const cards = await listCards(getRequestId(req)); + res.status(200).send(cards); + } catch (error) { + next({ message: 'Unknown Error' }); + } + }; +} + +export function Routes(services: Services) { + const router = Router(); + + router.post( + '/create', + CheckPermissions(), + CreateCardSchema(), + ValidateSchema(), + CreateHandler(services), + ); + router.get( + '/read/:uuid', + CheckPermissions(), + ReadCardSchema(), + ValidateSchema(), + ReadHandler(services), + ); + router.get('/list', CheckPermissions(), ListHandler(services)); + + return router; +} diff --git a/api/src/framework/express/middleware.ts b/api/src/framework/express/middleware.ts index 11b8605..04dd63f 100644 --- a/api/src/framework/express/middleware.ts +++ b/api/src/framework/express/middleware.ts @@ -56,8 +56,6 @@ export function CheckPermissions(): RequestHandler { export function ValidateSchema(): RequestHandler { return (req, res, next) => { - const error = validationResult(req); - const oldBody = req.body; req.body = matchedData(req, { locations: ['body'] }); @@ -67,6 +65,7 @@ export function ValidateSchema(): RequestHandler { message: 'Unprocessable Entity', }); + const error = validationResult(req); error.isEmpty() ? next() : next({ @@ -76,6 +75,12 @@ export function ValidateSchema(): RequestHandler { }; } +export function NotFoundHandler(): RequestHandler { + return (req, res, next) => { + next({ status: 404, message: 'Not Found' }); + }; +} + export function ErrorHandler(): ErrorRequestHandler { return (error, req, res, next) => { error.status diff --git a/api/src/framework/express/router.ts b/api/src/framework/express/router.ts index f60b70d..a53b0d2 100644 --- a/api/src/framework/express/router.ts +++ b/api/src/framework/express/router.ts @@ -1,11 +1,13 @@ import { Router } from 'express'; import { Services } from '../../app'; import * as user from './user'; +import * as card from './card'; export function Routes(services: Services) { const router = Router(); router.use('/user', user.Routes(services)); + router.use('/card', card.Routes(services)); return router; } diff --git a/api/src/framework/express/schema/card.ts b/api/src/framework/express/schema/card.ts new file mode 100644 index 0000000..e9ececc --- /dev/null +++ b/api/src/framework/express/schema/card.ts @@ -0,0 +1,29 @@ +import { checkSchema } from 'express-validator'; +import { CardRarity } from '@core'; + +export const CreateCardSchema = () => + checkSchema({ + name: { + isString: true, + }, + rarity: { + isIn: { + options: [Object.values(CardRarity)], + }, + }, + thumbnail: { + isString: true, + }, + model: { + isString: true, + }, + }); + +export const ReadCardSchema = () => + checkSchema({ + uuid: { + isUUID: { + options: 4, + }, + }, + }); diff --git a/api/src/framework/express/schema/user.ts b/api/src/framework/express/schema/user.ts index 11d134d..4364b3f 100644 --- a/api/src/framework/express/schema/user.ts +++ b/api/src/framework/express/schema/user.ts @@ -1,15 +1,6 @@ import { checkSchema } from 'express-validator'; import { UserRoles } from '@core'; -export const ReadUserSchema = () => - checkSchema({ - uuid: { - isUUID: { - options: 4, - }, - }, - }); - export const LoginUserSchema = () => checkSchema({ username: { @@ -37,6 +28,15 @@ export const CreateUserSchema = () => }, }); +export const ReadUserSchema = () => + checkSchema({ + uuid: { + isUUID: { + options: 4, + }, + }, + }); + export const UpdateUserSchema = () => checkSchema({ username: { diff --git a/api/src/framework/express/server.ts b/api/src/framework/express/server.ts index 7587eb6..f0df475 100644 --- a/api/src/framework/express/server.ts +++ b/api/src/framework/express/server.ts @@ -6,7 +6,7 @@ import { randomUUID } from 'crypto'; import * as http from 'http'; import { Routes } from './router'; -import { RequestId, ErrorHandler } from './middleware'; +import { RequestId, ErrorHandler, NotFoundHandler } from './middleware'; import { Services } from '../../app'; import { MongoConfig, ServerConfig } from '../../config'; @@ -32,7 +32,6 @@ class Server { origin: this.config.origin, }), ); - this.app.use(RequestId()); this.app.use( session({ proxy: process.env.NODE_ENV === 'production', @@ -52,12 +51,16 @@ class Server { saveUninitialized: false, }), ); + this.app.use(RequestId()); this.app.use(Routes(services)); + this.app.use(NotFoundHandler()); this.app.use(ErrorHandler()); } - start(func: () => void): void { - this.server = this.app.listen(this.config.port, func); + async start(): Promise { + return new Promise((resolve, reject) => { + this.server = this.app.listen(this.config.port, resolve); + }); } async close(): Promise { diff --git a/api/src/framework/express/user.ts b/api/src/framework/express/user.ts index ae75a14..521b16b 100644 --- a/api/src/framework/express/user.ts +++ b/api/src/framework/express/user.ts @@ -5,6 +5,7 @@ import { ReadUser, LoginUser, UpdateUser, + ListUsers, } from '../../functions/user'; import { LoginUserSchema, @@ -83,9 +84,23 @@ function UpdateHandler(services: Services): RequestHandler { }; } +function ListHandler(services: Services): RequestHandler { + const listUsers = ListUsers(services); + + return async (req, res, next) => { + try { + const users = await listUsers(getRequestId(req)); + res.status(200).send(users); + } catch (error) { + next({ message: 'Unknown Error' }); + } + }; +} + export function Routes(services: Services) { const router = Router(); + //User router.post( '/login', LoginUserSchema(), @@ -105,7 +120,6 @@ export function Routes(services: Services) { ValidateSchema(), ReadHandler(services), ); - router.post( '/create', CheckPermissions(), @@ -113,7 +127,6 @@ export function Routes(services: Services) { ValidateSchema(), CreateHandler(services), ); - router.patch( '/update/:uuid', CheckPermissions(), @@ -121,6 +134,16 @@ export function Routes(services: Services) { ValidateSchema(), UpdateHandler(services), ); + router.get('/list', CheckPermissions(), ListHandler(services)); + + //Alteration + router.post( + '/givecard', + CheckPermissions(), + GiveCardSchema(), + ValidateSchema(), + GiveCardHandler(), + ); return router; } diff --git a/api/src/framework/mongo/card.ts b/api/src/framework/mongo/card.ts new file mode 100644 index 0000000..8ad2384 --- /dev/null +++ b/api/src/framework/mongo/card.ts @@ -0,0 +1,74 @@ +import { Collection, Db } from 'mongodb'; +import { UpdateCardBody } from '@core'; +import { Card } from '../../entities/card'; +import { log } from '../../functions/logger'; + +class CardModel { + private collection: Collection; + + constructor(db: Db) { + this.collection = db.collection('cards'); + } + + public async create(tracker: string, cardInfo: Card): Promise { + const checkCard = await this.collection.findOne({ + uuid: cardInfo.uuid, + }); + if (checkCard) { + log(tracker, 'Card Already Exists'); + throw new Error(); + } + + await this.collection.insertOne(cardInfo); + + const card = await this.read(tracker, cardInfo.uuid); + log(tracker, 'CREATE USER', card); + return card; + } + + public async read(tracker: string, uuid: string): Promise { + const cardDocument = await this.collection.findOne({ uuid }); + if (!cardDocument) { + log(tracker, 'Card Not Found'); + throw new Error(); + } + + const card = new Card(cardDocument); + log(tracker, 'READ CARD', card); + return card; + } + + public async update( + tracker: string, + uuid: string, + cardInfo: UpdateCardBody, + ): Promise { + const checkCard = await this.collection.findOne({ uuid }); + if (!checkCard) { + log(tracker, 'Card Does Not Exist'); + throw new Error(); + } + + await this.collection.updateOne( + { uuid }, + { + $set: { + ...cardInfo, + updatedAt: new Date(), + }, + }, + ); + + const card = await this.read(tracker, uuid); + log(tracker, 'UPDATE CARD', card); + return card; + } + + public async list(tracker: string): Promise { + const cardCollection = await this.collection.find().toArray(); + log(tracker, 'LIST CARDS'); + return cardCollection.map((cardDocument) => new Card(cardDocument)); + } +} + +export default CardModel; diff --git a/api/src/framework/mongo/user.ts b/api/src/framework/mongo/user.ts index e065e61..6b21964 100644 --- a/api/src/framework/mongo/user.ts +++ b/api/src/framework/mongo/user.ts @@ -11,9 +11,9 @@ class UserModel { this.collection = db.collection('users'); } - public async login(tracker: string, data: LoginUserBody): Promise { + public async login(tracker: string, userInfo: LoginUserBody): Promise { const checkUser = await this.collection.findOne({ - username: data.username, + username: userInfo.username, }); if (!checkUser) { log(tracker, 'User Not Found'); @@ -22,7 +22,7 @@ class UserModel { const userDocument = await this.collection.findOne({ uuid: checkUser.uuid, - password: getHashedPassword(checkUser.uuid, data.password), + password: getHashedPassword(checkUser.uuid, userInfo.password), }); if (!userDocument) { log(tracker, 'Wrong Password'); @@ -93,6 +93,12 @@ class UserModel { log(tracker, 'UPDATE USER', user); return user; } + + public async list(tracker: string): Promise { + const userCollection = await this.collection.find().toArray(); + log(tracker, 'LIST USERS'); + return userCollection.map((userDocument) => new User(userDocument)); + } } export default UserModel; diff --git a/api/src/functions/card.ts b/api/src/functions/card.ts new file mode 100644 index 0000000..e82cda7 --- /dev/null +++ b/api/src/functions/card.ts @@ -0,0 +1,36 @@ +import { CardInfo, CreateCardBody } from '@core'; +import { Card } from '../entities/card'; +import { Services } from '../app'; + +export function CreateCard( + services: Services, +): (tracker: string, raw: CreateCardBody) => Promise { + const { cardModel } = services; + + return async (tracker, raw) => { + const card = await cardModel.create(tracker, new Card(raw)); + return card.Info(); + }; +} + +export function ReadCard( + services: Services, +): (tracker: string, uuid: string) => Promise { + const { cardModel } = services; + + return async (tracker, uuid) => { + const card = await cardModel.read(tracker, uuid); + return card.Info(); + }; +} + +export function ListCards( + services: Services, +): (tracker: string) => Promise { + const { cardModel } = services; + + return async (tracker) => { + const cards = await cardModel.list(tracker); + return cards.map((card) => card.Info()); + }; +} diff --git a/api/src/functions/user.ts b/api/src/functions/user.ts index 2fe1ba2..cb7cf81 100644 --- a/api/src/functions/user.ts +++ b/api/src/functions/user.ts @@ -45,3 +45,14 @@ export function UpdateUser( return user.Info(); }; } + +export function ListUsers( + services: Services, +): (tracker: string) => Promise { + const { userModel } = services; + + return async (tracker) => { + const users = await userModel.list(tracker); + return users.map((user) => user.Info()); + }; +} diff --git a/core/src/alteration.ts b/core/src/alteration.ts new file mode 100644 index 0000000..bb5be57 --- /dev/null +++ b/core/src/alteration.ts @@ -0,0 +1,12 @@ +import { EntityInfo } from './entity'; +import { CardInfo } from './card'; + +export type AlerationInfo = { + card: CardInfo; + level: number; + xp: number; +}; + +export type Ateration = AlerationInfo & EntityInfo; + +export type AlerationCtor = Partial; diff --git a/core/src/card.ts b/core/src/card.ts new file mode 100644 index 0000000..86e8bd8 --- /dev/null +++ b/core/src/card.ts @@ -0,0 +1,40 @@ +import { EntityInfo } from './entity'; + +export enum CardRarity { + COMMON = 'COMMON', + RARE = 'RARE', + EPIC = 'EPIC', + LEGENDARY = 'LEGENDARY', +} + +export type CombatStats = { + baseAtk: number; + baseDef: number; + baseCrit: number; + multAtk: number; + multDef: number; + multCrit: number; +}; + +export type CardInfo = { + uuid: string; + name: string; + rarity: keyof typeof CardRarity; + stats: CombatStats; + thumbnail: string; + model: string; +}; + +export type CreateCardBody = { + name: string; + rarity: keyof typeof CardRarity; + stats: CombatStats; + thumbnail: string; + model: string; +}; + +export type Card = CardInfo & EntityInfo; + +export type CardCtor = Partial; + +export type UpdateCardBody = Partial>; diff --git a/core/src/index.ts b/core/src/index.ts index 260ce72..549422d 100644 --- a/core/src/index.ts +++ b/core/src/index.ts @@ -1,2 +1,4 @@ +export * from './alteration'; +export * from './card'; export * from './user'; export * from './entity'; diff --git a/core/src/user.ts b/core/src/user.ts index 77a2528..0638072 100644 --- a/core/src/user.ts +++ b/core/src/user.ts @@ -1,4 +1,5 @@ import { EntityInfo } from './entity'; +import { CardInfo } from './card'; export enum UserRoles { ADMIN = 'ADMIN', @@ -9,6 +10,7 @@ export type UserInfo = { uuid: string; username: string; role: keyof typeof UserRoles; + cards: CardInfo[]; }; export type User = UserInfo & EntityInfo;