Move software files one directory up, Readme

This commit is contained in:
2024-11-19 16:51:28 +01:00
parent 9fa2b753ec
commit b347df7c6e
329 changed files with 255 additions and 31 deletions

View File

@@ -0,0 +1,166 @@
import { Router, Request, Response } from "express";
import { Account } from "../models/user/account.model";
import { validateString } from "../scripts/validateHelper";
import { Address } from "../models/user/address.model";
import { Payment } from "../models/user/payment.model";
import { AccountRole } from "../models/user/accountRole.model";
import { Exercise } from "../models/exercises/exercise.model";
import { sequelize } from "../database";
import jwt from "jsonwebtoken"
import { verifyToken } from "../middlewares/auth.middleware";
export const account = Router()
account.get("/", (req: Request, res: Response) => {
Account.findAll({
include: [ AccountRole ]
})
.then(accounts => {
res.status(200).json(accounts)
})
})
// Login user
account.get("/login", async (req: Request, res: Response) => {
// Using raw SQL code for SQL injections!
const [results, metadata] =
await sequelize.query(
"SELECT * FROM Accounts " +
"WHERE (username='" + req.query.username +
"' AND password='" + req.query.password + "')"
)
if (results.length != 0) {
// Creating session token
const token = jwt.sign({ userId: results[0]["id"] }, 'sjcucjdkdf')
// Status: 200 OK
res.status(200).json({
success: true,
token: token
})
} else {
// Status: 401 Unauthorized
res.status(401).json({
code: 401,
message: "Unauthorized"
})
}
})
account.get("/account", verifyToken, async(req: Request, res: Response) => {
Account.findOne({
where: {
id: req["id"]
},
include: [ Address, AccountRole, Payment ]
})
.then(account => {
res.status(200).json(account)
})
})
// Creating a new user
account.post("/", async (req: Request, res: Response) => {
// Check if username is valid
if (!validateString(req.body.username, 4))
{
// Status: 400 Bad request
res.status(400).json({
code: 400,
message: "Username too short!"
})
}
// Check if password is valid
if (!validateString(req.body.password, 8))
{
// Status: 400 Bad request
res.status(400).json({
code: 400,
message: "Password too short!"
})
}
// Create account
await AccountRole.findOne({
where: {
name: "User"
}
})
.then(role => {
req.body["accountRoleId"] = role.id
})
Account.create(req.body)
.then(account => {
// Status: 201 Created
res.status(201).json(account)
}).catch(reason => {
// Status: 409 Conflict
res.status(409).json({
code: 409,
message: "Username already in use"
})
})
})
account.patch("/", verifyToken, (req: Request, res: Response) => {
Account.update(req.body,
{
where: { id: req.body.id }
})
.then(async result => {
for (let payment of req.body.payments) {
if (payment.id == undefined) {
payment["accountId"] = req.body.id
await Payment.create(payment)
} else {
await Payment.update(payment,
{
where: { id: payment.id }
}
)
}
}
for (let address of req.body.addresses) {
if (address.id == undefined) {
address["accountId"] = req.body.id
await Address.create(address)
} else {
await Address.update(address,
{
where: { id: address.id }
}
)
}
}
// Status: 200 OK
let accountData = await Account.findByPk(req.body.id, { include: [ Payment, AccountRole, Address ]})
res.status(200).json(accountData)
})
.catch(error => {
// Status: 400 Bad request
res.status(400).json({
code: 400,
message: error
})
})
})
account.delete("/:id", (req: Request, res: Response) => {
Account.destroy({
where: {
id: req.params.id
}
})
.then(account => {
res.status(200).send()
})
})

View File

@@ -0,0 +1,37 @@
import { Request, Response, NextFunction, Router } from 'express'
import { deleteAllTables, deleteExerciseProgressTables, prepopulateDatabase, prepopulateExerciseDatabase } from '../scripts/databaseHelper'
export const api = Router()
/**
* Status check endpoint
*/
api.get("/", (req: Request, res: Response, next: NextFunction) => {
res.status(200).send()
})
/**
* Reset the whole database to factory state
* Doesn't effect ExerciseTable and ExerciseGroupTable
*/
api.get("/resetdatabase", async (req: Request, res: Response, next: NextFunction) => {
// Step 1: Delete all data tables
deleteAllTables()
// Step 2: Prepopulate with default values
await prepopulateDatabase()
// Step 3: Send status back
res.status(200).send()
})
/**
* Reset ExerciseTable and ExerciseGroupTable to factory state
*/
api.get("/resetExerciseProgress", async (req: Request, res: Response, next: NextFunction) => {
deleteExerciseProgressTables()
await prepopulateExerciseDatabase()
res.status(200).send()
})

View File

@@ -0,0 +1,201 @@
import { Member } from "../models/acts/member.model";
import { Band } from "../models/acts/band.model";
import { Request, Response, Router } from "express";
import { Rating } from "../models/acts/rating.model";
import { Genre } from "../models/acts/genre.model";
import { Concert } from "../models/acts/concert.model";
import { Location } from "../models/locations/location.model";
import { City } from "../models/locations/city.model";
import { Op } from "sequelize";
import { calcOverallRating, calcRatingValues } from "../scripts/calcScripts";
import { sequelize } from "../database";
export const band = Router()
/**
* Get all bands
*/
band.get("/", (req: Request, res: Response) => {
let sort = req.query.sort
let count = req.query.count
Band.findAll({
include: [
{
model: Rating,
},
{
model: Genre,
attributes: {
exclude: [ "id" ]
}
},
Concert
]
})
.then(bands => {
for (let band of bands) {
band.dataValues["nrOfConcerts"] = band.dataValues.concerts.length
band.dataValues["rating"] = calcOverallRating(band.dataValues.ratings)
// Delete unnecessary Arrays
delete band.dataValues.ratings
for (let genre of band.dataValues.genres) {
delete genre.dataValues.BandGenre
}
}
// Sort ascending/descending by number of concerts
if (sort != undefined) {
bands.sort((band1, band2) => {
if (sort == "desc") {
return band2.dataValues.concerts.length - band1.dataValues.concerts.length
} else if (sort == "asc") {
return band1.dataValues.concerts.length - band2.dataValues.concerts.length
}
})
}
// Limit number of items
if (count != undefined) {
bands.splice(Number(count))
}
res.status(200).json(bands)
})
})
/**
* Get all information about one band
*/
band.get("/band/:name", (req: Request, res: Response) => {
Band.findOne({
where: {
name: { [Op.like]: req.params.name }
},
include: [
{
model: Member,
attributes: {
exclude: [ "bandId" ]
}
},
{
model: Rating,
attributes: {
exclude: [ "bandId" ]
}
},
{
model: Concert,
include: [
{
model: Location,
include: [ City ],
attributes: {
exclude: [ "id" ]
}
}
],
attributes: {
exclude: [ "tourId", "locationId" ]
}
},
Genre
],
attributes: {
exclude: [ "genreId" ]
}
})
.then(band => {
band.dataValues["rating"] = calcOverallRating(band.dataValues.ratings)
band.dataValues["ratingValues"] = calcRatingValues(band.dataValues.ratings)
// Delete unnecessary Arrays
delete band.dataValues.ratings
for (let genre of band.dataValues.genres) {
delete genre.dataValues.BandGenre
}
res.status(200).json(band)
})
.catch(e => {
res.status(404).send()
})
})
/**
* Band search
*/
band.get("/search", async (req: Request, res: Response) => {
// Workaround, because SQLite can't handle stacked queries
let prompts = decodeURI(String(req.query.value)).split(";")
// On stacked prompts, execute last prompt
if (prompts.length > 1) {
console.log(prompts[prompts.length - 2])
const [results, metadata] =
await sequelize.query(prompts[prompts.length - 2])
res.status(200).json(results)
} else {
Band.findAll({
where: {
name: {
[Op.substring]: req.query.value
},
},
include: [ Concert, Genre ]
})
.then(bands => {
res.status(200).json(bands)
})
}
})
/**
* Edit band
*/
band.patch("/", (req: Request, res: Response) => {
Band.update(req.body, {
where: {
id: req.body.id
}
})
.then(result => {
res.status(200).json(result)
})
})
/**
* New band
*/
band.post("/", (req: Request, res: Response) => {
Band.create(req.body)
.then(result => {
res.status(200).json(result)
})
})
/**
* Delete a band
*/
band.delete("/", (req: Request, res: Response) => {
Band.destroy({
where: {
id: req.body.id
}
})
.then(result => {
res.status(200).json(result)
})
.catch(error => {
res.status(500).send()
})
})

View File

@@ -0,0 +1,11 @@
import { City } from "../models/locations/city.model";
import { Request, Response, Router } from "express";
export const city = Router()
city.get("/", (req: Request, res: Response) => {
City.findAll()
.then(cities => {
res.status(200).json(cities)
})
})

View File

@@ -0,0 +1,157 @@
import { Location } from "../models/locations/location.model";
import { Concert } from "../models/acts/concert.model";
import { Request, Response, Router } from "express";
import { City } from "../models/locations/city.model";
import { SeatGroup } from "../models/locations/seatGroup.model";
import { SeatRow } from "../models/locations/seatRow.model";
import { Seat } from "../models/locations/seat.model";
import { Ticket } from "../models/ordering/ticket.model";
import { Band } from "../models/acts/band.model";
import { Op } from "sequelize";
export const concert = Router()
concert.get("/", (req: Request, res: Response) => {
let count = req.query.count
Concert.findAll({
include: [
{
model: Location,
include: [ City ]
},
Band
],
order: [
[ 'date', 'ASC' ]
]
})
.then(concerts => {
// Limit number of items
if (count != undefined) {
concerts.splice(Number(count))
}
res.status(200).json(concerts)
})
})
// Get all available data about a band by it's ID
concert.get("/concert/:id", (req: Request, res: Response) => {
Concert.findByPk(req.params.id, {
include: [
{
model: Band,
},
{
model: Location,
include: [
{
model: City
},
{
model: SeatGroup,
include: [
{
model: SeatRow,
include: [
{
model: Seat,
include: [
{
model: Ticket
}
]
}
]
}
]
}
],
attributes: {
exclude: [ "cityId" ]
}
}
],
attributes: {
exclude: [ "locationId", "tourId" ]
}
})
.then(concert => {
concert.dataValues["capacity"] = 0
// Go through every seat group
for (let seatGroup of concert.dataValues.location.dataValues.seatGroups) {
seatGroup.dataValues["occupied"] = 0
// Go through every seat row
for (let seatRow of seatGroup.dataValues.seatRows) {
for (let seat of seatRow.dataValues.seats) {
seat.dataValues["state"] = 0
concert.dataValues["inStock"] += 1
// Go through every seat
for (let ticket of seat.dataValues.tickets) {
// Check if the seat is occupied through a ticket
// If yes, reduce number of available seats
if (ticket.dataValues.concertId == req.params.id) {
seat.dataValues["state"] = 1
seatGroup.dataValues["occupied"] += 1
break
}
}
delete seat.dataValues.tickets
}
}
}
res.status(200).json(concert)
})
.catch(e => {
res.status(404).send()
})
})
// Concert search
concert.get("/search", (req: Request, res: Response) => {
Concert.findAll({
where: {
[Op.or]: [
// Search by name of concert
{
name: {
[Op.substring]: req.query.value
}
},
// Search by name of band
{
"$band.name$": {
[Op.substring]: req.query.value
}
},
// Search by name of city of location
{
"$location.city.name$": {
[Op.substring]: req.query.value
}
}
]
},
include: [
Band,
{
model: Location,
include: [ City ]
}
]
})
.then(concerts => {
res.status(200).json(concerts)
})
})

View File

@@ -0,0 +1,74 @@
import { Op } from "sequelize";
import { Exercise } from "../models/exercises/exercise.model";
import { ExerciseGroup } from "../models/exercises/exerciseGroup.model";
import { Request, Response, Router } from "express";
export const exercises = Router()
/**
* Get all Exercises grouped in ExerciseGroups
*/
exercises.get("/", (req: Request, res: Response) => {
Exercise.findAll({
include: [ ExerciseGroup ]
}).then(result => {
result.sort((a, b) => {
return (a.dataValues.exerciseGroup.dataValues.groupNr * 10 + a.dataValues.exerciseNr) > (b.dataValues.exerciseGroup.dataValues.groupNr * 10 + b.dataValues.exerciseNr) ? 1 : -1
})
res.status(200).json(result)
})
})
/**
* Update state of an Exercise
*
* @param groupNr Number of exercise group (not ID)
* @param exerciseNr Number of exercise (not ID)
* @param state New state boolean
*/
exercises.post("/:groupNr/:exerciseNr/:state", (req: Request, res: Response) => {
Exercise.findOne({
where: {
[Op.and] : [
{
exerciseNr: req.params.exerciseNr
},
{
"$exerciseGroup.groupNr$": req.params.groupNr
}
]
},
include: [ ExerciseGroup ]
})
.then(async exercise => {
let changed = false
if (exercise.dataValues.solved != (req.params.state == "1")) {
await exercise.update({ solved: req.params.state == "1" })
changed = true
}
res.status(200).json({
exercise: exercise,
changed: changed
})
})
// ExerciseGroup.findOne({
// where: { groupNr: req.params.groupNr }
// })
// .then(group => {
// Exercise.findOne({
// where: {
// exerciseNr: req.params.exerciseNr,
// exerciseGroupId: group.id
// }
// })
// .then(exercise => {
// exercise.update({ solved: req.params.state == "1"})
// res.status(200).send()
// })
// })
})

View File

@@ -0,0 +1,52 @@
import { Request, Response, NextFunction, Router } from 'express'
import fs from "fs"
import multer from "multer"
const upload = multer({ dest: './backend/images/' })
export const files = Router()
/**
* Get all folders
*/
files.get("/folders", async (req: Request, res: Response) => {
let dirNames = fs.readdirSync("./backend/images")
let result = []
dirNames.forEach(dir => {
result.push({
name: dir,
nrOfItems: fs.readdirSync("./backend/images/" + dir).length
})
})
res.status(200).json(result)
})
/**
* Get all uploaded file names by file name
*/
files.get("/:folder", async (req: Request, res: Response) => {
let result = []
let fileNames = fs.readdirSync("./backend/images/" + req.params.folder + "/")
fileNames.forEach(file => {
result.push({
name: file,
size: fs.statSync("./backend/images/" + req.params.folder + "/" + file).size,
url: "http://localhost:3000/static/" + req.params.folder + "/" + file
})
})
res.status(200).json(result)
})
/**
* Upload a file
*/
files.post("/", upload.single("file"), function (req: Request, res: Response, next: NextFunction) {
console.log(req.file)
res.status(200).send()
})

View File

@@ -0,0 +1,67 @@
import { Band } from "../models/acts/band.model";
import { Genre } from "../models/acts/genre.model";
import { Request, Response, Router } from "express";
export const genre = Router()
/**
* Get all available Genres
*/
genre.get("/", (req: Request, res: Response) => {
Genre.findAll({
include: [ Band ]
})
.then(genres => {
res.status(200).json(genres)
})
.catch(error => {
res.status(500).send()
})
})
/**
* Update a Genre entry
*/
genre.patch("/", (req: Request, res: Response) => {
Genre.update(req.body, {
where: {
id: req.body.id
}
})
.then(result => {
res.status(200).json(result)
})
.catch(error => {
res.status(500).send()
})
})
/**
* Create a new Genre entry
*/
genre.post("/", (req: Request, res: Response) => {
Genre.create(req.body)
.then(result => {
res.status(200).json(result)
})
.catch(error => {
res.status(500).send()
})
})
/**
* Delete a Genre entry
*/
genre.delete("/", (req: Request, res: Response) => {
Genre.destroy({
where: {
id: req.body.id
}
})
.then(result => {
res.status(200).json(result)
})
.catch(error => {
res.status(500).send()
})
})

View File

@@ -0,0 +1,136 @@
import { Concert } from "../models/acts/concert.model";
import { City } from "../models/locations/city.model";
import { Location } from "../models/locations/location.model";
import { Request, Response, Router } from "express";
import { Band } from "../models/acts/band.model";
import { SeatGroup } from "../models/locations/seatGroup.model";
import { Seat } from "../models/locations/seat.model";
import { SeatRow } from "../models/locations/seatRow.model";
import { Op } from "sequelize";
export const location = Router()
/**
* Get all available Locations
*
* @query sort Sort results ascending (asc) or descending (desc)
* @query count Limit number of results
*/
location.get("/", (req: Request, res: Response) => {
let sort = req.query.sort
let count = req.query.count
Location.findAll({
include: [
City,
{
model: Concert,
include: [ Band ],
}
],
attributes: {
exclude: [ "cityId" ]
}
})
.then(async locations => {
// Sort ascending/descending by number of concerts
if (sort != undefined) {
locations.sort((location1, location2) => {
if (sort == "desc") {
return location2.dataValues.concerts.length - location1.dataValues.concerts.length
} else if (sort == "asc") {
return location1.dataValues.concerts.length - location2.dataValues.concerts.length
}
})
}
for (let location of locations) {
location.dataValues["nrOfConcerts"] = location.dataValues.concerts.length
delete location.dataValues.concerts
}
// Limit number of items
if (count != undefined) {
locations.splice(Number(count))
}
res.status(200).json(locations)
})
})
/**
* Get all data about a specific location
*
* @param urlName UrlName of the band (e.g. Red Hot Chili Peppers => red-hot-chili-peppers)
*/
location.get("/location/:urlName", (req: Request, res: Response) => {
Location.findOne({
where: { urlName: req.params.urlName },
include: [
City,
{
model: Concert,
include: [ Band ],
},
{
model: SeatGroup,
include: [
{
model: SeatRow,
include: [ Seat ]
}
]
}
],
attributes: {
exclude: [ "cityId" ]
}
})
.then(async location => {
for (let seatGroup of location.dataValues.seatGroups) {
for (let seatRow of seatGroup.dataValues.seatRows) {
for (let seat of seatRow.dataValues.seats) {
seat.dataValues["state"] = 0
}
}
}
res.status(200).json(location)
})
.catch(e => {
res.status(404).send()
})
})
/**
* Search for Locations
*
* @query value Search term to look for
*/
location.get("/search", (req: Request, res: Response) => {
Location.findAll({
where: {
[Op.or]: [
// Search by name of location
{
name: {
[Op.substring]: req.query.value
},
},
// Search by name of city
{
"$city.name$": {
[Op.substring]: req.query.value
}
}
]
},
include: [ City, Concert ]
})
.then(locations => {
res.status(200).json(locations)
})
})

View File

@@ -0,0 +1,119 @@
import { Router, Request, Response } from "express";
import { Order } from "../models/ordering/order.model";
import { Concert } from "../models/acts/concert.model";
import { Ticket } from "../models/ordering/ticket.model";
import { Payment } from "../models/user/payment.model";
import { Address } from "../models/user/address.model";
import { Band } from "../models/acts/band.model";
import { Location } from "../models/locations/location.model";
import { City } from "../models/locations/city.model";
import { Seat } from "../models/locations/seat.model";
import { SeatRow } from "../models/locations/seatRow.model";
import { SeatGroup } from "../models/locations/seatGroup.model";
import { Account } from "../models/user/account.model";
import { Exercise } from "backend/models/exercises/exercise.model";
export const order = Router()
// Get all orders
order.get("/", (req: Request, res: Response) => {
Order.findAll({
include: [
Account,
Address,
{
model: Ticket,
include: [
{
model: Concert,
include: [
{
model: Band
},
{
model: Location,
include: [ City ]
}
]
}
]
}
]
})
.then(orders => {
res.status(200).json(orders)
})
})
// Get all orders of one account by it's user id
order.get("/:id", (req: Request, res: Response) => {
Order.findAll({
where: { accountId: req.params.id },
include: [
{
model: Ticket,
include: [
{
model: Concert,
include: [
{
model: Band
},
{
model: Location,
include: [ City ]
}
],
attributes: {
exclude: [
"categoryId",
"brandId"
]
}
},
{
model: Seat,
include: [
{
model: SeatRow,
include: [ SeatGroup ]
}
]
}
]
},
Payment,
Address
]
})
.then(orders => {
res.status(200).json(orders)
})
})
// Place a new order
order.post("/", (req: Request, res: Response) => {
Order.create(req.body)
.then(async order => {
for (let ticket of req.body.tickets) {
Ticket.create({
orderId: order.dataValues.id,
concertId: ticket.concertId,
orderPrice: ticket.orderPrice,
seatId: ticket.seatId
})
Concert.decrement(
"inStock",
{
by: 1,
where: { id: ticket.concertId }
}
)
}
// Created
res.status(201).json(order)
})
})