diff --git a/.env.example b/.env.example index f9c3dc1..6c41318 100644 --- a/.env.example +++ b/.env.example @@ -4,4 +4,5 @@ DB_HOST=bla DB_PORT=bla DB_USER=bla DB_PASSWORD=bla -DB_NAME=bla \ No newline at end of file +DB_NAME=bla +NODE_ENV=production \ No newline at end of file diff --git a/src/app.ts b/src/app.ts index 873ff9f..6a3e801 100644 --- a/src/app.ts +++ b/src/app.ts @@ -3,24 +3,23 @@ import * as dotenvSafe from "dotenv-safe"; import { createExpressServer } from "routing-controllers"; import consola from "consola"; import loaders from "./loaders/index"; -import authchecker from "./authchecker"; -import { ErrorHandler } from './middlewares/ErrorHandler'; -// + dotenvSafe.config(); const PORT = process.env.APP_PORT || 4010; const app = createExpressServer({ - authorizationChecker: authchecker, - middlewares: [ErrorHandler], - development: false, - controllers: [`${__dirname}/controllers/*.ts`], + development: process.env.NODE_ENV === "production", + cors: true, + routePrefix: "/api", + controllers: [__dirname + "/controllers/*.ts"], }); -(async () => { - await loaders(app); - app.listen(PORT, () => { - consola.success( - `⚡️[server]: Server is running at http://localhost:${PORT}` - ); - }); -})(); \ No newline at end of file +async function main() { + await loaders(app); + app.listen(PORT, () => { + consola.success( + `⚡️[server]: Server is running at http://localhost:${PORT}` + ); + }); +} +main(); diff --git a/src/controllers/TrackController.ts b/src/controllers/TrackController.ts index 243a088..965fde9 100644 --- a/src/controllers/TrackController.ts +++ b/src/controllers/TrackController.ts @@ -1,64 +1,91 @@ -import { - JsonController, - Param, - Body, - Get, - Post, - Put, - Delete, - Authorized, -} from "routing-controllers"; -import { getConnectionManager, Repository } from "typeorm"; -import { EntityFromBody } from "typeorm-routing-controllers-extensions"; -import { ResponseSchema } from "routing-controllers-openapi"; -import { Track } from "../models/Track"; -import { IsInt, IsNotEmpty, IsPositive, IsString } from "class-validator"; +import { JsonController, Param, Body, Get, Post, Put, Delete, NotFoundError, OnUndefined, NotAcceptableError } from 'routing-controllers'; +import { getConnectionManager, Repository } from 'typeorm'; +import { EntityFromBody } from 'typeorm-routing-controllers-extensions'; +import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; +import { Track } from '../models/Track'; +import { IsInt, IsNotEmpty, IsPositive, IsString } from 'class-validator'; class CreateTrack { - @IsString() - @IsNotEmpty() - name: string; + @IsString() + @IsNotEmpty() + name: string; - @IsInt() - @IsPositive() - length: string; + @IsInt() + @IsPositive() + length: number; } -@JsonController("/track") -// @Authorized("TRACKS:read") +export class TrackNotFoundError extends NotFoundError { + constructor() { + super('Track not found!'); + } +} + +@JsonController('/tracks') export class TrackController { - private trackRepository: Repository; + private trackRepository: Repository; - constructor() { - this.trackRepository = getConnectionManager().get().getRepository(Track); - } + /** + * Gets the repository of this controller's model/entity. + */ + constructor() { + this.trackRepository = getConnectionManager().get().getRepository(Track); + } - @Get() - @ResponseSchema(Track, { isArray: true }) - getAll() { - return this.trackRepository.find(); - } + @Get() + @ResponseSchema(Track, { isArray: true }) + @OpenAPI({description: "Lists all tracks."}) + getAll() { + return this.trackRepository.find(); + } - @Get("/:id") - @ResponseSchema(Track) - getOne(@Param("id") id: number) { - return this.trackRepository.findOne({ id: id }); - } + @Get('/:id') + @ResponseSchema(Track) + @OnUndefined(TrackNotFoundError) + @OpenAPI({description: "Returns a track of a specified id (if it exists)"}) + getOne(@Param('id') id: number) { + return this.trackRepository.findOne({ id: id }); + } - @Authorized() - @Post() - post(@Body({ validate: true }) track: CreateTrack) { - return this.trackRepository.save(track); - } + @Post() + @ResponseSchema(Track) + @OpenAPI({description: "Create a new track object (id will be generated automagicly)."}) + post( + @Body({ validate: true }) + track: CreateTrack + ) { + return this.trackRepository.save(track); + } - @Authorized("TRACKS:write") - @Put("/:id") - put(@Param("id") id: number, @EntityFromBody() track: Track) { - return this.trackRepository.update({ id: id }, track); - } + @Put('/:id') + @ResponseSchema(Track) + @OpenAPI({description: "Update a track object (id can't be changed)."}) + async put(@Param('id') id: number, @EntityFromBody() track: Track) { + let oldTrack = await this.trackRepository.findOne({ id: id }); - @Delete("/:id") - remove(@Param("id") id: number) { - return this.trackRepository.delete({ id: id }); - } + if (!oldTrack) { + throw new TrackNotFoundError(); + } + + if(oldTrack.id != track.id){ + throw new NotAcceptableError("The id's don't match!"); + } + + await this.trackRepository.update(oldTrack, track); + return track; + } + + @Delete('/:id') + @ResponseSchema(Track) + @OpenAPI({description: "Delete a specified track (if it exists)."}) + async remove(@Param('id') id: number) { + let track = await this.trackRepository.findOne({ id: id }); + + if (!track) { + throw new TrackNotFoundError(); + } + + await this.trackRepository.delete(track); + return track; + } } diff --git a/src/loaders/express.ts b/src/loaders/express.ts index 87b4f4c..787497a 100644 --- a/src/loaders/express.ts +++ b/src/loaders/express.ts @@ -3,12 +3,6 @@ import bodyParser from 'body-parser'; import cors from 'cors'; export default async (app: Application) => { - app.get('/status', (req, res) => res.status(200).end()); app.enable('trust proxy'); - - app.use(cors()); - // app.use(bodyParser.urlencoded({ extended: false })); - - // more middlewares return app; }; diff --git a/src/loaders/openapi.ts b/src/loaders/openapi.ts index 963360e..9a0ff85 100644 --- a/src/loaders/openapi.ts +++ b/src/loaders/openapi.ts @@ -11,7 +11,9 @@ export default async (app: Application) => { }); const spec = routingControllersToSpec( storage, - {}, + { + routePrefix: "/api" + }, { components: { schemas, @@ -27,11 +29,11 @@ export default async (app: Application) => { explorer: true, }; app.use( - "/docs", + "/api/docs", swaggerUiExpress.serve, swaggerUiExpress.setup(spec, options) ); - app.get(["/openapi.json", "/swagger.json"], (req, res) => { + app.get(["/api/openapi.json", "/api/swagger.json"], (req, res) => { res.json(spec); }); return app; diff --git a/src/models/Track.ts b/src/models/Track.ts index dc83239..b0a789b 100644 --- a/src/models/Track.ts +++ b/src/models/Track.ts @@ -7,6 +7,12 @@ import { IsString, } from "class-validator"; +/** + * @classdesc Defines a track of given length. + * @property {number} id - Autogenerated unique id + * @property {string} name - The track's name + * @property {number} lenth - The track's length in meters +*/ @Entity() export class Track { @PrimaryGeneratedColumn() @@ -22,5 +28,5 @@ export class Track { @Column() @IsInt() @IsPositive() - length: string; + length: number; }