168 Commits

Author SHA1 Message Date
a58adbcc8d Bugfix file manager 2024-12-05 12:29:05 +01:00
8a18b95031 Redesign account pages, split payments and addresses to single pages, new dashboard 2024-11-29 13:38:20 +01:00
c867d9d51f Add hint for inputs in Welcome Dialog 2024-11-29 11:34:35 +01:00
4905ef607b Remove test script 2024-11-28 19:48:05 +01:00
2c18c59f51 Rewrite solution code generation 2024-11-28 19:46:02 +01:00
d622fda7a9 Finish order admin page with detail dialog and actions 2024-11-28 13:10:54 +01:00
831a667a27 More server stability 2024-11-28 12:29:36 +01:00
947ed225b6 Remove Super-Admin role, bugfix if user enters buggy SQL injection on search field 2024-11-27 19:29:03 +01:00
b74da2dc3b Implement solution codes for exercises 2024-11-27 18:32:10 +01:00
da484b08a9 Adding new SQL-Injection exercise 2024-11-27 14:21:14 +01:00
22d7b2a11d Light mode improvements, empty state on search page 2024-11-26 20:58:10 +01:00
ac21e8d607 Global color schema, remove name and register number access in preferences, add alert in first startup wizard 2024-11-26 20:13:54 +01:00
7c78226864 More feedback on account creation, bugfix on account creation 2024-11-26 19:39:40 +01:00
fa2c7f2e8b Add more license information of images 2024-11-26 12:51:23 +01:00
07f486c72e Improve exercise solution of 2.1, 2.2, 2.3 and 2.5 2024-11-26 12:40:01 +01:00
b5364639a5 Adding "Test Environment" banner in the bottom right corner 2024-11-26 11:59:37 +01:00
2e15d4a960 License system implemented 2024-11-25 18:55:28 +01:00
1b0f48d374 First steps of license store system 2024-11-23 17:45:14 +01:00
b3ebbff732 Changelog 2024-11-21 09:42:53 +01:00
e8b50da142 Minor bugfixes, new logo 2024-11-20 19:20:22 +01:00
57358fa818 Implementing exercise 3.2 2024-11-20 14:07:41 +01:00
366f1060e3 Exercise 3.1 fully implemented 2024-11-20 12:27:41 +01:00
9d41a14926 Exercise 2.5 added 2024-11-20 11:34:44 +01:00
d4fbda26d7 Password encryption 2024-11-20 10:43:48 +01:00
e02f2d252e Developer window in Electron app, remove edit/delete options in Admin panel (currently not completed) 2024-11-19 18:41:26 +01:00
1dc5740f03 Move software files one directory up, Readme 2024-11-19 16:51:28 +01:00
baf763c4cb Rewrite SQL Injection exercises, change code for search field, exercises 0.1 - 3.1 implemented 2024-11-19 12:25:30 +01:00
7fdecdbc5d Implement exercise 2.1 2024-11-18 16:12:58 +01:00
f2bb4b6954 Add exercise group descriptions to help page, restructure timeline 2024-11-18 16:07:51 +01:00
2d77a793d8 Exercisegroup 0 and 1 complete implemented 2024-11-18 15:41:33 +01:00
7912e38932 Working on exercise 1.2 2024-11-16 16:56:20 +01:00
70e508ce7a Bugfix order process/account update 2024-11-15 11:36:17 +01:00
86acedc8aa Authentification Token 2024-11-14 15:32:28 +01:00
ae577dc023 Improve Snackbar 2024-11-14 09:33:45 +01:00
c0d110f386 Implement exercise 1.1 (open help page) 2024-11-14 08:09:06 +01:00
2b14f76d4b Improve UI for smaller screens 2024-11-14 07:25:06 +01:00
0911cdbe5c Rewrite database access for exercises 2024-11-13 13:56:44 +01:00
24f44e73f4 Redesign file browser, file upload (server) 2024-11-12 23:50:21 +01:00
e690fb984d Bugfix database creation 2024-11-12 05:09:54 +01:00
b97cc1af20 OrderDetailDialog 2024-11-11 08:44:17 +01:00
1b85d0eca9 Documentation 2024-11-11 08:15:21 +01:00
c58be89104 Admin Order Page, refresh ERM diagram of database 2024-11-09 15:19:08 +01:00
b84d542352 PDF Generator for Exercise progress 2024-11-08 20:02:37 +01:00
be1bc85f40 First startup dialog, factory reset 2024-11-08 13:45:09 +01:00
0a1d85a0fe Reduce DB creation time to 5,0 seconds 2024-11-07 19:29:59 +01:00
d10f84750c Massive improvement of database creation time (63s -> 7s) 2024-11-07 19:18:50 +01:00
47e045dde3 OrderStore 2024-11-07 17:18:49 +01:00
844898bb3c Docstrings, AdminDashboard button loading, new BannerStateEnums 2024-11-06 18:22:03 +01:00
9ec8e382cf Finish GenresAdminPanel 2024-11-06 17:55:18 +01:00
80dd2a0ae8 Startup dialog on first run 2024-11-06 16:45:36 +01:00
b7eca62403 File browser on admin page 2024-11-05 19:54:14 +01:00
ce097e2098 Error page 2024-11-05 18:43:47 +01:00
c3d0cc2879 Account itself deletable 2024-11-04 19:05:14 +01:00
ffccd9f2d4 LocationDetailPage: Seat not selectable, loading animation during fetching 2024-11-04 18:15:49 +01:00
a3e8d30b6c Small improvements on account pages, added payment info card 2024-11-04 12:04:15 +01:00
3359400494 Split concertsPage 2024-11-03 17:38:14 +01:00
8da0f01699 Split home page in sections 2024-11-03 17:30:07 +01:00
16fd40f11d Electron & Electron Builder 2024-11-02 18:09:44 +01:00
80a4dcfdc9 Redesign Band highlight section on HomePage, fix Footer spacing bug 2024-11-01 12:52:17 +01:00
ba4f4720d2 Brand logo 2024-11-01 11:06:10 +01:00
9052c59254 New "about" section on PreferencesPage, add new exercise descriptions/steps 2024-11-01 03:56:38 +01:00
b6205f374b Bugfix Carousel on HomePage 2024-11-01 03:08:52 +01:00
82cd2d0e98 License 2024-10-31 22:10:31 +01:00
0f78d3eecc AccountsAdminPage, ConcertsAdminPage 2024-10-30 05:58:53 +01:00
012c544bde Account Dashboard Card 2024-10-27 00:12:27 +02:00
33342345f9 LocationsAdminPage data table 2024-10-26 23:52:38 +02:00
c570a04052 Genre Admin page, new Genre store 2024-10-26 16:23:00 +02:00
cdb3f02156 Creating Band edit page 2024-10-26 14:35:33 +02:00
fedb821a72 Restructure translation files 2024-10-25 13:05:51 +02:00
10d6142fff Reactivate Admin Panel, implement exercise 2.1, add feedback if exercise solved 2024-10-24 20:23:28 +02:00
8de3ca481a Exercise store, mark exercise 0.2 as solved on ticket buy 2024-10-23 13:57:37 +02:00
3faa89a749 Display free tickets in standing areas 2024-10-23 13:36:21 +02:00
1f0933e2a9 Reimplement Carousel on homepage 2024-10-22 21:06:00 +02:00
b1eea15bac Filterbar on Concert page 2024-10-22 20:26:35 +02:00
70227329f2 Band filter by genre 2024-10-22 20:11:09 +02:00
386800f31a Enhance search for band names of concerts 2024-10-22 19:16:30 +02:00
36f87bdbd3 Slight changes on TicketListItem append section 2024-10-22 19:08:52 +02:00
4e6be355ea Streamlined stores 2024-10-22 18:47:27 +02:00
3e53a606a6 Slight changes seat rows on locations 2024-10-22 12:19:15 +02:00
40161a136a Fix order process 2024-10-21 14:57:03 +02:00
7880a444b1 Fixed Account pages 2024-10-21 14:02:51 +02:00
59470f5396 Add addressbar 2024-10-17 19:10:21 +02:00
41106a8686 Update configuration 2024-10-17 18:42:35 +02:00
c8d87f6643 Remove EventModel in frontend 2024-10-12 21:00:42 +02:00
6c33de3d87 New page for all concerts 2024-10-12 19:40:12 +02:00
f8bdb54c33 Simplified json import 2024-10-12 16:22:14 +02:00
203f8428a7 Remove EventTable in database, redesign frontend URL paths 2024-10-12 15:54:03 +02:00
1d4daac9ae Finish search page 2024-10-11 18:59:19 +02:00
0ec11aacf7 Atomize model classes 2024-10-11 17:42:21 +02:00
cfb8fb9d7d Implement global search 2024-10-11 12:59:21 +02:00
49b436d588 UI Bugfixes 2024-10-10 19:23:13 +02:00
913e067ad2 Improve UI of concertListItem and eventListItem 2024-10-10 18:43:38 +02:00
4b745eef99 Bugfixes, reset exercise progress from frontend 2024-10-10 14:29:45 +02:00
58c7282701 Redesign seat map layout 2024-10-10 14:11:09 +02:00
fe2cea5529 Add more locations, add all location images, adjust seatGroups in locations 2024-10-08 20:04:56 +02:00
41a7cbc9da Implement URL XSS attack 2024-10-08 14:30:39 +02:00
3dd7b1d4c6 Implementing Exercise system in database with API and frontend visualization 2024-10-07 13:15:16 +02:00
4b2764e33c Ticket Component 2024-10-06 19:30:12 +02:00
10c0d0838f Redesign and reimplementing account page 2024-10-05 21:00:39 +02:00
d1cdf1f8fb Tickets moveable to basket, Basket shows tickets, removable 2024-10-05 20:23:13 +02:00
e48782b897 SeatSelection page 2024-10-04 20:15:16 +02:00
8165f17fc8 More skeleton loader, repair bandDetailPage 2024-10-04 18:01:37 +02:00
bfffd72a4a More skelton loader, add optional parameters to /locations and /events 2024-10-04 15:20:40 +02:00
e2f6fb9c52 Skeleton loader 2024-10-04 13:16:05 +02:00
17e6b08129 Loading feedback with indeterminate circle 2024-10-03 20:19:03 +02:00
67ed71858c Improve filterBar on eventsPage, improve API access from frontend 2024-10-03 19:56:44 +02:00
14766fb39b Start moving data server handling from pinia store to server 2024-10-03 19:03:36 +02:00
e177cf53e6 Add more artist images 2024-10-03 16:51:30 +02:00
bd53b8edfc Adding Seat plan component and database tables 2024-10-01 15:37:08 +02:00
d6997229c4 New locationDetailPage displays concert in a location, new datasets, images, URL path changed 2024-09-29 21:42:20 +02:00
be5cc090fe Split band detail page in section files 2024-09-29 18:43:37 +02:00
907e0082e1 Creating whole Band details page 2024-09-29 16:56:43 +02:00
a6ca7eedde Filterable tours 2024-09-29 14:28:29 +02:00
0616409f14 Display concerts with card views on "All concerts" page, adding image property for tours 2024-09-28 21:18:25 +02:00
8d0b141217 Display all bands grouped by genre, create m:n association between Band and Genre in database 2024-09-27 23:25:24 +02:00
513c73c5c1 Location page displays city groups with all available concert locations 2024-09-27 20:40:59 +02:00
ef0c48ae17 Redesign home page 2024-09-27 15:52:22 +02:00
c6c8cf0ae8 Move Navigation from NavDrawer to AppBar, redesign page structure and routes 2024-09-27 13:08:43 +02:00
e2dd49e21b Refactor frontend, display tours with cards on ToursPage 2024-09-26 16:06:20 +02:00
169fcdf03c Add ToursTable, update API documentation 2024-09-26 14:40:41 +02:00
da98fc73c0 Rewriting database and API to transform to a ticket shop 2024-09-26 11:04:27 +02:00
d36dbced8e Data tables for brand and category added to admin pages 2024-09-25 15:54:45 +02:00
0856540441 Creating admin pages, new brand API endpoint 2024-09-25 15:42:05 +02:00
6dd49f630d Connect Orders database table with Payments and Addresses, visualize it in the frontend 2024-09-24 23:41:35 +02:00
531f964841 Better validation on text fields, change AlertBanner to Snackbar 2024-09-24 22:18:27 +02:00
3dc4c7af1e Payments and Addresses add- and removeable 2024-09-24 20:53:46 +02:00
fd4c1d5a65 Add more user feedback (loading buttons, empty states) 2024-09-24 16:36:30 +02:00
8329a6ae09 Implement ordering process 2024-09-24 15:40:16 +02:00
5b8f1fbead Multiple addresses & payments of an account configurable in frontend 2024-09-24 13:55:48 +02:00
abe1b496a2 Matching UI with improved API 2024-09-24 13:12:44 +02:00
bc62174566 Readme 2024-09-23 21:27:39 +02:00
b245e3803a Extend database with more tables, rewrite API doc, improve API endpoints 2024-09-23 21:22:45 +02:00
8b4db9ccc8 Add Score board page to visualize progress of exercises 2024-09-22 21:29:23 +02:00
564cf144ff Confirm dialog, fix language change bug, add bank accout information to users 2024-09-22 20:57:28 +02:00
d7eae540b1 New cardView component, add database reset confirm dialog 2024-09-22 15:46:33 +02:00
47fbb564b2 ProductCard redesigned, add property for number of items in stock for product 2024-09-22 15:06:10 +02:00
3863661b3c New products, productDetail dialog offers a specs category 2024-09-20 23:20:03 +02:00
89e91c3213 Fix order view 2024-09-20 15:30:40 +02:00
718dbe30b7 Redesign productDetail dialog 2024-09-20 15:08:17 +02:00
54d13686cf Improved user feedback system, improved Product detail view 2024-09-20 12:57:19 +02:00
ed264ff026 Move banner system to store, migrate login/register API handling to own file, display Account details on accountPage 2024-09-19 16:20:12 +02:00
5b3a753233 Readme 2024-09-19 15:04:09 +02:00
7b245da959 CategoryStore, API calls 2024-09-18 16:21:07 +02:00
9ee344f45f ProductStore, move API calls to separate file 2024-09-18 15:59:16 +02:00
2847bd940f Documenting, restructure and expand API 2024-09-13 12:07:33 +02:00
39ce77ea63 Setting fo ri18n-ally, progress stepper on order page 2024-09-12 18:38:56 +02:00
8594763fa6 Create OrdersPage, load orders from backend, move NavDrawer items to Component 2024-09-11 20:49:55 +02:00
55fd203c7f Account login possible 2024-09-11 14:54:33 +02:00
fd06b8a9a4 User registration completed 2024-09-10 20:28:24 +02:00
f6e4bfdf2f Add dialog to create new user 2024-09-10 18:50:47 +02:00
767269a7cf Moving image source from web to backend 2024-09-10 16:45:49 +02:00
f10c0ef4e9 Translation 2024-09-09 20:55:09 +02:00
7ebc3c1c77 New basket table, add empty state on basket page, new BasketItemModel 2024-09-09 19:47:46 +02:00
6ff577ece1 Store products in a basket, display list of products in basket 2024-09-09 14:33:29 +02:00
2d0dc274bf Moving exmple datasets to json files 2024-09-09 13:45:26 +02:00
20e8ce1024 Working on product detail dialog 2024-09-08 20:35:10 +02:00
f82f6ce9af Add empty state on productsPage 2024-09-08 19:20:54 +02:00
c18f0e0484 Product images 2024-09-06 18:49:22 +02:00
6af4388671 Filter products by category and discount, sort by price and name 2024-09-06 18:26:34 +02:00
babf1c77ce Create product cards, display all on products page 2024-09-06 17:10:21 +02:00
22cc811ae5 Login form 2024-09-05 18:12:08 +02:00
4d1eec023d VueRouter, Pinia state management, add pages, design preferences page 2024-09-05 17:47:02 +02:00
8b1a396f68 Fix db relations, add sample data, show categories in frontend menu 2024-09-05 16:19:23 +02:00
cfd1d29302 Add all database tables with relations 2024-09-04 17:06:41 +02:00
7338bb216a Add SQLite database to backend, interacting with the frontend 2024-09-04 16:42:37 +02:00
8af11151d3 Create a test backend server with ExpressJs 2024-09-03 19:10:18 +02:00
d4be64a0e9 Add Vuetify and basic layout for test purposes 2024-09-03 13:54:19 +02:00
e607a09631 Add Vue.js with TypeScript support 2024-09-03 13:37:46 +02:00
37 changed files with 868 additions and 3543 deletions

View File

@@ -1,30 +1,3 @@
# v.0.3.0 (Release Candidate 1)
## 🚀 Features
- Swagger Documentation
## 🐛 Bugfixes
- Bugfix on search page for Band datasets
# v.0.2.0 (Beta)
## 🚀 Features
- Adding "Test Environment" banner in the bottom right corner
- License handling system
- New SQL-Injection exercise 2.1
- Solution code based on Matrikelnummer and number of completed exercises
## 🌟 Enhancements
- Improve exercise solution of 2.1, 2.2, 2.3, 2.4 and 2.6
- Light mode improvements
- Global color schema
- More feedback through notifications
- More hints on text fields
- Redesign account pages, split payments and addresses, new dashboard
## 🐛 Bugfixes
- More server stability
- Bugfix file manager in Electron application
# v.0.1.0 (Alpha)
## 🚀 Features
- Frontend

539
README.md
View File

@@ -1,116 +1,30 @@
<p align="center">
<img src="misc/images/logo.png" width="300" />
</p>
# EventMaster
<h1 align="center">EventMaster</h1>
<h2 align="center">The most hackable Ticket-Shop!</h2>
![Hompage](/misc/images/homepage.png)
## About the project
EventMaster is a desktop application for simulating various hacking methods for web applications. It's the perfect tool to understand vulnerabilities in modern web applications.
The shop offers tickets for concerts of popular bands with a complete ordering system, account management and admin interface. The program has been deliberately provided with vulnerabilities for teaching purposes, e.g. SQL injections. If a task is solved and a vulnerability is identified, the system provides visual feedback. The progress can be viewed on an overview page.
This project is created during my Bachelor thesis. It will be used at Leibniz University Hannover for foundation lectures.
## Important notes!
- ⚠️ The project is under development, bugs may occur
- ⚠️ The software is developed for educational purposes, don't use the attack techniques without permission on other applications and systems!
- ⚠️ Works 100% offline, no data transmission to or from external services
## Features
- 13 exercises
- 3 attack techniques
- SQL-Injections
- Cross-Site-Scripting
- Broken Access Control
- Exercise control system - check the progress and get feedback for solved exercises!
- Full working ticket shop:
- Vuetify design
- Account management system
- Order process system
- Event locations with seat plan and seat reservation system during ordering process
- Global search
- Admin panel
- Product pages
- ExpressJs-Backend server
- REST-API
- SQLite Database
- URL simulation bar for electron application
- Works 100% offline
- Multi language support (German, English)
- Dark/Light-Mode
- Exercise progress PDF generation
- API-Documentation with Swagger
- Desktop application packed with Electron
- Database can be resetted complete or partially
### Techstack
- Axios
- Electron
- Electron Builder
- Express.js
- JsonWebToken
- jspdf
- Moment.js
- NPM
- Nodemon
- Pinia
- Sequelize
- SQLite 3
- Swagger
- TypeScript
- Vite
- Vue i18n
- Vue.js
- VueRouter
- Vuetify
### Planned features
- More attack techniques
- More exercises
- More languages
- Customize the offered set of exercises (for trainer)
- Image license view system (currently only in admin panel)
### Screenshots
#### Band Detail page
![Band Detail page](/misc/images/band-detail-seite.png)
#### Booking page
![Booking page](/misc/images/booking-concert.png)
#### Admin panel
![Admin panel](/misc/images/admin-panel.png)
#### Global search
![Global search](/misc/images/search.png)
#### Feedback on exercise solution
![Snackbar Feedback](/misc/images/snackbar-feedback.png)
The most hackable Web Shop!
## How to use
### Download pre-builded images
### Prepare development environment
Go to releases and download one of the pre-builded images for your operating system.
1. Install node.js
### Build yourself
```bash
sudo apt install npm
1. Download + extract the project
2. Open the root folder with VS Code (recommended)
3. Open the bash inside VS Code and install all necessary packages:
# If outdated version:
sudo npm install -g n
sudo n stable
```
2. Download + extract the project
3. Open the root folder with VS Code (recommended)
4. Open the bash inside VS Code and install all necessary packages:
```bash
npm i
```
#### Test/development
### Test/development
There are multiple commands to test parts or the whole project:
@@ -120,7 +34,7 @@ There are multiple commands to test parts or the whole project:
The frontend runs on `http://localhost:5173/` and the backend on `http://localhost:3000/`
#### Build
### Build
- `npm run vite:build`: Build Vue frontend only
- `npm run server:build`: Build ExpressJs backend only
@@ -134,6 +48,423 @@ The frontend runs on `http://localhost:5173/` and the backend on `http://localho
### Database
![database-erm](misc/images/database.png)
### Frontend-Backend-System
![frontend-backend-system](misc/images/frontend-backend-system.png)
### Backend API endpoints
The application host it's data in a SQLite database. The access is managed by an [ExpressJs](https://expressjs.com/) server which offers many REST-API endpoints for the frontend. The REST-API server runs on port 3000.
---
#### Listing existing
<details open>
<summary><code><span style="color:#70AFFD"><b>GET</b></span></code> <code><b>/accounts/</b></code> <code> (Get all Accounts)</code>
</summary>
##### Parameters
> None
##### Responses
> | http code | content-type | response |
> | :---: | --- | --- |
> | `200` | `application/json` | `Array<Account + AccountRole>` |
##### Example Response
```json
[
{
"id": 421,
"username": "hagemeister93",
"password": "Xjt3qb5t",
"email": "hagemeister93@gmail.com",
"firstName": "Laurin",
"lastName": "Hagemeister",
"accountRoleId": 2,
"accountRole": {
"id": 2,
"name": "Admin",
"privilegeBuy": true,
"privilegeAdminPanel": true
}
}
]
```
</details>
<details open>
<summary><code><span style="color:#70AFFD"><b>GET</b></span></code> <code><b>/api/files</b></code> <code> (Get all public files)</code>
</summary>
##### Parameters
> None
##### Responses
> | http code | content-type | response |
> | :---: | --- | --- |
> | `200` | `application/json` | `Array<{folder: String, files: Array<{name: String, size: Number, url: String}> }>` |
##### Example Response
```json
[
{
"folder": "artists",
"files": [
{
"name": "alex-turner.jpg",
"size": 56473,
"url": "http://localhost:3000/static/artists/alex-turner.jpg"
},
{
"name": "andy-nicholson.jpg",
"size": 68983,
"url": "http://localhost:3000/static/artists/andy-nicholson.jpg"
}
]
}
]
```
</details>
<details open>
<summary><code><span style="color:#70AFFD"><b>GET</b></span></code> <code><b>/bands/</b></code> <code> (Get all bands)</code>
</summary>
##### Parameters
> | name | type | data type | description |
> | :---: | --- | --- | --- |
> | `sort` | optional | string | Sort by number of concerts ascending (asc) or descending (desc) |
> | `count` | optional | number | Number of items to responde |
##### Responses
> | http code | content-type | response |
> | :---: | --- | --- |
> | `200` | `application/json` | `Array<>` |
##### Example Response
```json
[
{
"folder": "artists",
"files": [
{
"name": "alex-turner.jpg",
"size": 56473,
"url": "http://localhost:3000/static/artists/alex-turner.jpg"
},
{
"name": "andy-nicholson.jpg",
"size": 68983,
"url": "http://localhost:3000/static/artists/andy-nicholson.jpg"
}
]
}
]
```
</details>
<details>
<summary><code><span style="color:#70AFFD"><b>GET</b></span></code> <code><b>/events?city=cityName&genre=genreName&count=nrOfItems&sort=sortDirection</b></code> <code> (Get all events, filtered by city and genre)</code>
</summary>
##### Parameters
> | name | type | data type | description |
> | :---: | --- | --- | --- |
> | `cityName` | optional | string | Name of the city to filter for |
> | `genreName` | optional | string | Name of the genre to filter for |
> | `nrOfItems` | optional | number | Limits number of results |
> | `sortDirection` | optional | string | Sort by number of concerts, 'asc' or 'desc' |
##### Responses
> | http code | content-type | response |
> | :---: | --- | --- |
> | `200` | `application/json` | `Array<Event + Array<Concert + Location + City> + Band & Genre>` |
</details>
<details>
<summary><code><span style="color:#70AFFD"><b>GET</b></span></code> <code><b>/locations?count=nrOfItems&sort=sortDirection</b></code> <code> (Get all locations)</code>
</summary>
##### Parameters
> | name | type | data type | description |
> | :---: | --- | --- | --- |
> | `nrOfItems` | optional | number | Limits number of results |
> | `sortDirection` | optional | string | Sort by number of concerts, 'asc' or 'desc' |
##### Responses
> | http code | content-type | response |
> | :---: | --- | --- |
> | `200` | `application/json` | `Array<Location + City + Array<Concert + Event>>` |
</details>
Down here: todo!
<details>
<summary><code><span style="color:#70AFFD"><b>GET</b></span></code> <code><b>/bands</b></code> <code> (Get all bands)</code>
</summary>
##### Parameters
> None
##### Responses
> | http code | content-type | response |
> | :---: | --- | --- |
> | `200` | `application/json` | `Array<Band>` |
</details>
<details>
<summary><code><span style="color:#70AFFD"><b>GET</b></span></code> <code><b>/bands/:id</b></code> <code> (Get all information about one band)</code>
</summary>
##### Parameters
> | name | type | data type | description |
> | :---: | --- | --- | --- |
> | `id` | required | string | ID of product in the database |
##### Responses
> | http code | content-type | response |
> | :---: | --- | --- |
> | `200` | `application/json` | `Band` + `Array<Rating>` + `Array<Member>` |
</details>
<details>
<summary><code><span style="color:#70AFFD"><b>GET</b></span></code> <code><b>/shows/:id</b></code> <code> (Get all information about one show)</code>
</summary>
##### Parameters
> | name | type | data type | description |
> | :---: | --- | --- | --- |
> | `id` | required | string | ID of product in the database |
##### Responses
> | http code | content-type | response |
> | :---: | --- | --- |
> | `200` | `application/json` | `Show` + `Tour` + `Location` + `City` |
</details>
<details>
<summary><code><span style="color:#70AFFD"><b>GET</b></span></code> <code><b>/tours</b></code> <code> (Get all tours)</code>
</summary>
##### Parameters
> None
##### Responses
> | http code | content-type | response |
> | :---: | --- | --- |
> | `200` | `application/json` | `Array<Tours>` + `Band` + `Show` + `Location` + `City` |
</details>
<details>
<summary><code><span style="color:#70AFFD"><b>GET</b></span></code> <code><b>/genres</b></code> <code> (Get all genres)</code>
</summary>
##### Parameters
> None
##### Responses
> | http code | content-type | response |
> | :---: | --- | --- |
> | `200` | `application/json` | `Array<Genre>` |
</details>
<details>
<summary><code><span style="color:#70AFFD"><b>GET</b></span></code> <code><b>/orders/:id</b></code> <code> (Get all orders of an user)</code>
</summary>
##### Parameters
> | name | type | data type | description |
> | --- | --- | --- | --- |
> | `id` | required | string | ID of userAccount in the database |
##### Responses
> | http code | content-type | response |
> | :---: | --- | --- |
> | `200` | `application/json` | `ProductModel` + `Order`, `OrderItem`, `Product` |
</details>
<details>
<summary><code><span style="color:#70AFFD"><b>GET</b></span></code> <code><b>/categories/</b></code> <code> (Get all Categories)</code>
</summary>
##### Parameters
> None
##### Responses
> | http code | content-type | response |
> | :---: | --- | --- |
> | `200` | `application/json` | `Array<Categories>` |
</details>
<details>
<summary><code><span style="color:#70AFFD"><b>GET</b></span></code> <code><b>/brands</b></code> <code> (Get all Brands)</code>
</summary>
##### Parameters
> None
##### Responses
> | http code | content-type | response |
> | :---: | --- | --- |
> | `200` | `application/json` | `Array<Brand>` |
</details>
---
#### Creating new
<details>
<summary><code><span style="color:#69CA92"><b>POST</b></span></code> <code><b>/accounts/</b></code> <code> (Create a new account)</code>
</summary>
##### Parameters
> | name | type | data type | description |
> | :---: | --- | --- | --- |
> | None | required | object (JSON) | Model of an Account |
##### Responses
> | http code | content-type | response |
> | :---: | --- | --- |
> | `201` | `application/json` | `AccountModel` |
> | `400` | `application/json` | `{code: 400, message: "Username too short!"}` |
> | `400` | `application/json` | `{code: 400, message: "Password too short!"}` |
> | `409` | `application/json` | `{code: 409, message: "Username already in use"}` |
</details>
<details>
<summary><code><span style="color:#69CA92"><b>POST</b></span></code> <code><b>/orders/</b></code> <code> (Create a new order)</code>
</summary>
##### Parameters
> | name | type | data type | description |
> | :---: | --- | --- | --- |
> | None | required | object (JSON) | Model of an Order |
##### Responses
> | http code | content-type | response |
> | :---: | --- | --- |
> | `201` | `application/json` | `OrderModel` |
</details>
---
#### Updating existing
<details>
<summary><code><span style="color:#F3A63D"><b>PATCH</b></span></code> <code><b>/accounts/</b></code> <code> (Update data of an existing account)</code>
</summary>
##### Parameters
> | name | type | data type | description |
> | :---: | --- | --- | --- |
> | None | required | object (JSON) | Model of an Account |
##### Responses
> | http code | content-type | response |
> | :---: | --- | --- |
> | `200` | `application/json` | AccountModel |
> | `400` | `application/json` | `{code: 400, message: "..."}` |
</details>
---
#### Delete existing
<!-- <details>
<summary><code><span style="color:#EB5246"><b>DELETE</b></span></code> <code><b>/product/:id</b></code> <code> (Delete a product)</code>
</summary>
##### Parameters
> | name | type | data type | description |
> | :---: | --- | --- | --- |
> | `id` | required | string | ID of product in the database |
##### Responses
> | http code | content-type | response |
> | :---: | --- | --- |
> | `200` | `application/json` | `ProductModel` |
> | `400` | `application/json` | `{code: 400, message: "..."}` |
</details> -->
---
#### Miscs
<details>
<summary><code><span style="color:#70AFFD"><b>GET</b></span></code> <code><b>/api/</b></code> <code> (Check if server runs)</code>
</summary>
##### Parameters
> None
##### Responses
> | http code | content-type | response |
> | :---: | --- | --- |
> | `200` | None | None |
</details>
<details>
<summary><code><span style="color:#70AFFD"><b>GET</b></span></code> <code><b>/resetDatabase/</b></code> <code> (Reset the database to it's default values)</code>
</summary>
##### Parameters
> None
##### Responses
> | http code | content-type | response |
> | :---: | --- | --- |
> | `200` | None | None |
</details>
#### Validate
<details>
<summary><code><span style="color:#69CA92"><b>POST</b></span></code> <code><b>/accounts/login</b></code> <code> (Login for user)</code></summary>
##### Parameters
> | name | type | data type | description |
> | --- | --- | --- | --- |
> | username | required | string | Username of the account |
> | password | required | string | Password of the account |
##### Responses
> | http code | content-type | response |
> | :---: | --- | --- |
> | `200` | `application/json` | `AccountObject` + `Addresses`, `Payments`, `AccountRole` |
> | `400` | `application/json` | `{code: 400, message: "Bad Request"}` |
> | `401` | `application/json` | `{code: 401, message: "Unauthorized"}` |
</details>
---

View File

@@ -12,6 +12,9 @@ export class AccountRole extends Model {
@Column
privilegeAdminPanel: boolean
@Column
privilegeFileAccess: boolean
// Relations
@HasMany(() => Account)

View File

@@ -1,9 +1,3 @@
/**
* @swagger
* tags:
* name: Account
* description: API to manage accounts
*/
import { Router, Request, Response } from "express";
import { Account } from "../models/user/account.model";
import { validateString } from "../scripts/validateHelper";
@@ -17,38 +11,20 @@ import { encryptString } from "../scripts/encryptScripts";
export const account = Router()
/**
* @swagger
* /accounts/login:
* get:
* summary: Start login process
* tags: [Account]
* parameters:
* - in: query
* name: username
* schema:
* type: string
* required: true
* description: Username
* - in: query
* name: password
* schema:
* type: string
* required: true
* description: User password
* responses:
* 200:
* description: Login successful
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/loginResponse'
* 401:
* description: Wrong credentials
* 500:
* description: Internal server error
*/
account.get("/login", async (req: Request, res: Response) => {
account.get("/", verifyToken, (req: Request, res: Response) => {
Account.findAll({
include: [ AccountRole ]
})
.then(accounts => {
res.status(200).json(accounts)
})
.catch(error => {
res.status(500).send()
})
})
// Login user
account.get("/account/login", async (req: Request, res: Response) => {
const encryptedPassword = encryptString(String(req.query.password))
try {
@@ -71,7 +47,10 @@ account.get("/login", async (req: Request, res: Response) => {
})
} else {
// Status: 401 Unauthorized
res.status(401).send()
res.status(401).json({
code: 401,
message: "Unauthorized"
})
}
} catch (e) {
res.status(500).send()
@@ -79,35 +58,12 @@ account.get("/login", async (req: Request, res: Response) => {
})
/**
* @swagger
* /accounts/account:
* get:
* summary: Get all data about an user account
* tags: [Account]
* security:
* - JWT: []
* responses:
* 200:
* description: Success
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/useraccount'
* 401:
* description: Unauthorized
* 500:
* description: Internal server error
*/
account.get("/account", verifyToken, async(req: Request, res: Response) => {
account.get("/account/data", verifyToken, async(req: Request, res: Response) => {
Account.findOne({
where: {
id: req["id"]
},
include: [ Address, AccountRole, Payment ],
attributes: {
exclude: [ "accountRoleId" ]
}
include: [ Address, AccountRole, Payment ]
})
.then(account => {
res.status(200).json(account)
@@ -118,31 +74,7 @@ account.get("/account", verifyToken, async(req: Request, res: Response) => {
})
/**
* @swagger
* /accounts/account:
* post:
* summary: Create a new user account
* tags: [Account]
* requestBody:
* description: Minimal user data body
* required: true
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/minimalAccount'
* responses:
* 201:
* description: Created
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/useraccount'
* 400:
* description: Username/password too short
* 409:
* description: Username already in use
*/
// Creating a new user
account.post("/account", async (req: Request, res: Response) => {
// Check if username is valid
if (!validateString(req.body.username, 4))
@@ -189,28 +121,6 @@ account.post("/account", async (req: Request, res: Response) => {
})
})
/**
* @swagger
* /accounts/account:
* patch:
* summary: Update an user accounts data
* tags: [Account]
* security:
* - JWT: []
* responses:
* 200:
* description: Success
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/useraccount'
* 401:
* description: Unauthorized
* 500:
* description: Internal server error
*/
account.patch("/account", verifyToken, (req: Request, res: Response) => {
Account.update(req.body,
{
@@ -254,31 +164,7 @@ account.patch("/account", verifyToken, (req: Request, res: Response) => {
})
})
/**
* @swagger
* /accounts/account/{id}:
* delete:
* summary: Delete an user account
* tags: [Account]
* security:
* - JWT: []
* parameters:
* - in: path
* name: id
* schema:
* type: number
* required: true
* description: ID of user account
* responses:
* 200:
* description: Success
* 401:
* description: Unauthorized
* 500:
* description: Internal server error
*/
account.delete("/account/:id", verifyToken, (req: Request, res: Response) => {
account.delete("/account/:id", (req: Request, res: Response) => {
Account.destroy({
where: {
id: req.params.id
@@ -290,37 +176,4 @@ account.delete("/account/:id", verifyToken, (req: Request, res: Response) => {
.catch(error => {
res.status(500).send()
})
})
/**
* @swagger
* /accounts/:
* get:
* summary: Request all user accounts
* tags: [Account]
* security:
* - JWT: []
* responses:
* 200:
* description: Success
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/useraccount'
* 401:
* description: Unauthorized
* 500:
* description: Internal server error
*/
account.get("/", verifyToken, (req: Request, res: Response) => {
Account.findAll({
include: [ AccountRole ]
})
.then(accounts => {
res.status(200).json(accounts)
})
.catch(error => {
res.status(500).send()
})
})

View File

@@ -1,38 +1,18 @@
/**
* @swagger
* tags:
* name: Api
* description: Main API access point for misc events
*/
import { Request, Response, NextFunction, Router } from 'express'
import { deleteAllTables, deleteExerciseProgressTables, prepopulateDatabase, prepopulateExerciseDatabase } from '../scripts/databaseHelper'
export const api = Router()
/**
* @swagger
* /api:
* get:
* summary: Status check endpoint
* tags: [Api]
* responses:
* 200:
* description: Server is up and running
* Status check endpoint
*/
api.get("/", (req: Request, res: Response, next: NextFunction) => {
res.status(200).send()
})
/**
* @swagger
* /api/resetdatabase:
* get:
* summary: Reset the database to factory state
* description: Doesn't effect ExerciseTable and ExerciseGroupTable
* tags: [Api]
* responses:
* 200:
* description: Reset successful
* 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
@@ -46,15 +26,7 @@ api.get("/resetdatabase", async (req: Request, res: Response, next: NextFunction
})
/**
* @swagger
* /api/resetExerciseProgress:
* get:
* summary: Reset exercises to factory state
* description: Reset ExerciseTable and ExerciseGroupTable to factory state
* tags: [Api]
* responses:
* 200:
* description: Reset successful
* Reset ExerciseTable and ExerciseGroupTable to factory state
*/
api.get("/resetExerciseProgress", async (req: Request, res: Response, next: NextFunction) => {
deleteExerciseProgressTables()

View File

@@ -1,9 +1,3 @@
/**
* @swagger
* tags:
* name: Bands
* description: API to manage the bands
*/
import { Member } from "../models/acts/member.model";
import { Band } from "../models/acts/band.model";
import { Request, Response, Router } from "express";
@@ -19,33 +13,7 @@ import { sequelize } from "../database";
export const band = Router()
/**
* @swagger
* /bands:
* get:
* summary: Download all available bands
* tags: [Bands]
* parameters:
* - in: query
* name: sort
* schema:
* type: string
* required: false
* description: Sort bands by number of concerts ascending (asc) or descending (desc)
* - in: query
* name: count
* schema:
* type: number
* required: false
* description: Limit number of results
* responses:
* 200:
* description: List of band objects
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/band'
* 500:
* description: Internal server error
* Get all bands
*/
band.get("/", (req: Request, res: Response) => {
let sort = req.query.sort

View File

@@ -1,30 +1,8 @@
/**
* @swagger
* tags:
* name: Cities
* description: API to manage the cities
*/
import { City } from "../models/locations/city.model";
import { Request, Response, Router } from "express";
export const city = Router()
/**
* @swagger
* /cities:
* get:
* summary: Download all cities
* tags: [Cities]
* responses:
* 200:
* description: List of all cities as objects
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/city'
* 500:
* description: Internal server error
*/
city.get("/", (req: Request, res: Response) => {
City.findAll()
.then(cities => {

View File

@@ -1,9 +1,3 @@
/**
* @swagger
* tags:
* name: Concerts
* description: API to manage the concerts
*/
import { Location } from "../models/locations/location.model";
import { Concert } from "../models/acts/concert.model";
import { Request, Response, Router } from "express";
@@ -17,68 +11,18 @@ import { Op } from "sequelize";
export const concert = Router()
const concertStructure = [
{
model: Band
},
{
model: Location,
include: [
{
model: City
},
{
model: SeatGroup,
include: [
{
model: SeatRow,
include: [
{
model: Seat,
include: [
{
model: Ticket
}
]
}
]
}
]
}
],
attributes: {
exclude: [ "cityId" ]
}
}
]
/**
* @swagger
* /concerts:
* get:
* summary: Get all available concerts
* tags: [Concerts]
* parameters:
* - in: query
* name: count
* schema:
* type: number
* required: false
* description: Limit number of results
* responses:
* 200:
* description: OK
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/concert'
*/
concert.get("/", (req: Request, res: Response) => {
let count = req.query.count
Concert.findAll({
include: concertStructure,
include: [
{
model: Location,
include: [ City ]
},
Band
],
order: [
[ 'date', 'ASC' ]
]
@@ -97,31 +41,47 @@ concert.get("/", (req: Request, res: Response) => {
})
/**
* @swagger
* /concerts/concert/{id}:
* get:
* summary: Download all available informations about a specific concert
* tags: [Concerts]
* parameters:
* - in: path
* name: id
* schema:
* type: number
* required: true
* description: ID of concert in database
* responses:
* 200:
* description: Single concert object
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/concert'
* 404:
* description: Not found
*/
// 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: concertStructure })
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
@@ -159,29 +119,7 @@ concert.get("/concert/:id", (req: Request, res: Response) => {
})
/**
* @swagger
* /concerts/search:
* get:
* summary: Search for concerts
* tags: [Concerts]
* parameters:
* - in: query
* name: value
* schema:
* type: string
* required: true
* description: Search term
* responses:
* 200:
* description: List of concert objects
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/concert'
* 500:
* description: Internal server error
*/
// Concert search
concert.get("/search", (req: Request, res: Response) => {
Concert.findAll({
where: {

View File

@@ -1,9 +1,3 @@
/**
* @swagger
* tags:
* name: Exercises
* description: API to manage the exercise progress
*/
import { Op } from "sequelize";
import { Exercise } from "../models/exercises/exercise.model";
import { ExerciseGroup } from "../models/exercises/exerciseGroup.model";
@@ -12,28 +6,11 @@ import { Request, Response, Router } from "express";
export const exercises = Router()
/**
* @swagger
* /exercises:
* get:
* summary: Download all exercises
* tags: [Exercises]
* responses:
* 200:
* description: Array of all exercises
* type: array
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/exercise'
* 500:
* description: Internal server error
* Get all Exercises grouped in ExerciseGroups
*/
exercises.get("/", (req: Request, res: Response) => {
Exercise.findAll({
include: [ ExerciseGroup ],
attributes: {
exclude: [ "exerciseGroupId" ]
}
include: [ ExerciseGroup ]
})
.then(result => {
result.sort((a, b) => {
@@ -48,39 +25,11 @@ exercises.get("/", (req: Request, res: Response) => {
})
/**
* @swagger
* /exercises/{groupNr}/{exerciseNr}/{state}:
* post:
* summary: Update an exercise solved state
* tags: [Exercises]
* parameters:
* - in: path
* name: groupNr
* schema:
* type: number
* required: true
* description: Number of exercise group (not ID)
* - in: path
* name: exerciseNr
* schema:
* type: number
* required: true
* description: Number of exercise (not ID)
* - in: path
* name: state
* schema:
* type: number
* required: true
* description: 1 = Solved, 0 = Unsolved
* responses:
* 200:
* description: Edited exercise
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/exercise'
* 500:
* description: Internal server error
* 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({
@@ -94,10 +43,7 @@ exercises.post("/:groupNr/:exerciseNr/:state", (req: Request, res: Response) =>
}
]
},
include: [ ExerciseGroup ],
attributes: {
exclude: [ "exerciseGroupId" ]
}
include: [ ExerciseGroup ]
})
.then(async exercise => {
let changed = false

View File

@@ -1,11 +1,5 @@
/**
* @swagger
* tags:
* name: Files
* description: API for handling static files
*/
import { Request, Response, NextFunction, Router } from 'express'
import fs from "fs"
import fs, { createReadStream } from "fs"
import multer from "multer"
const upload = multer({ dest: './backend/images/' })
import licenses from "../data/licenses.json"
@@ -14,18 +8,7 @@ import path from 'path'
export const files = Router()
/**
* @swagger
* /files/folders:
* get:
* summary: Get all static folders
* tags: [Files]
* responses:
* 200:
* description: Login successful
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/folder'
* Get all folders
*/
files.get("/folders", async (req: Request, res: Response) => {
let dirNames = fs.readdirSync(path.resolve(__dirname, "../images"))
@@ -43,25 +26,9 @@ files.get("/folders", async (req: Request, res: Response) => {
/**
* @swagger
* /files/{folder}:
* get:
* summary: Get all files in one folder
* tags: [Files]
* parameters:
* - in: path
* name: folder
* schema:
* type: string
* required: true
* description: Name of folder
* responses:
* 200:
* description: Login successful
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/file'
* Get all uploaded file names by folder name
*
* @param folder Name of folder on server
*/
files.get("/:folder", async (req: Request, res: Response) => {
let result = []

View File

@@ -1,9 +1,3 @@
/**
* @swagger
* tags:
* name: Genres
* description: API to manage the music genres
*/
import { Band } from "../models/acts/band.model";
import { Genre } from "../models/acts/genre.model";
import { Request, Response, Router } from "express";
@@ -11,20 +5,7 @@ import { Request, Response, Router } from "express";
export const genre = Router()
/**
* @swagger
* /genres:
* get:
* summary: Get all available genres
* tags: [Genres]
* responses:
* 200:
* description: OK
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/genre'
* 500:
* description: Internal Server Error
* Get all available Genres
*/
genre.get("/", (req: Request, res: Response) => {
Genre.findAll({
@@ -38,22 +19,8 @@ genre.get("/", (req: Request, res: Response) => {
})
})
/**
* @swagger
* /genres:
* patch:
* summary: Update the dataset of a genre
* tags: [Genres]
* responses:
* 200:
* description: OK
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/genre'
* 500:
* description: Internal Server Error
* Update a Genre entry
*/
genre.patch("/", (req: Request, res: Response) => {
Genre.update(req.body, {
@@ -69,22 +36,8 @@ genre.patch("/", (req: Request, res: Response) => {
})
})
/**
* @swagger
* /genres:
* post:
* summary: Add a new dataset of a genre
* tags: [Genres]
* responses:
* 200:
* description: OK
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/genre'
* 500:
* description: Internal Server Error
* Create a new Genre entry
*/
genre.post("/", (req: Request, res: Response) => {
Genre.create(req.body)
@@ -96,22 +49,8 @@ genre.post("/", (req: Request, res: Response) => {
})
})
/**
* @swagger
* /genres:
* delete:
* summary: Delete the dataset of a genre
* tags: [Genres]
* responses:
* 200:
* description: OK
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/genre'
* 500:
* description: Internal Server Error
* Delete a Genre entry
*/
genre.delete("/", (req: Request, res: Response) => {
Genre.destroy({

View File

@@ -1,9 +1,3 @@
/**
* @swagger
* tags:
* name: Locations
* description: API to manage the event locations
*/
import { Concert } from "../models/acts/concert.model";
import { City } from "../models/locations/city.model";
import { Location } from "../models/locations/location.model";
@@ -16,57 +10,24 @@ import { Op } from "sequelize";
export const location = Router()
// Response include rules
const locationStructure = [
City,
{
model: Concert,
include: [ Band ]
},
{
model: SeatGroup,
include: [
{
model: SeatRow,
include: [ Seat ]
}
]
}
]
/**
* @swagger
* /locations:
* get:
* summary: Get all available locations
* tags: [Locations]
* parameters:
* - in: query
* name: sort
* schema:
* type: string
* required: false
* description: Sort locations by number of concerts ascending (asc) or descending (desc)
* - in: query
* name: count
* schema:
* type: number
* required: false
* description: Limit number of results
* responses:
* 200:
* description: OK
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/location'
* 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: locationStructure,
include: [
City,
{
model: Concert,
include: [ Band ],
}
],
attributes: {
exclude: [ "cityId" ]
}
@@ -99,32 +60,29 @@ location.get("/", (req: Request, res: Response) => {
/**
* @swagger
* /locations/{urlName}:
* get:
* summary: Download all available informations about a specific locations
* tags: [Locations]
* parameters:
* - in: path
* name: urlName
* schema:
* type: string
* required: true
* description: Url name of the location to request for
* responses:
* 200:
* description: Single of location objects
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/location'
* 500:
* description: Internal server error
* 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: locationStructure,
include: [
City,
{
model: Concert,
include: [ Band ],
},
{
model: SeatGroup,
include: [
{
model: SeatRow,
include: [ Seat ]
}
]
}
],
attributes: {
exclude: [ "cityId" ]
}
@@ -147,27 +105,9 @@ location.get("/location/:urlName", (req: Request, res: Response) => {
/**
* @swagger
* /locations/search:
* get:
* summary: Search for locations
* tags: [Locations]
* parameters:
* - in: query
* name: value
* schema:
* type: string
* required: true
* description: Search term
* responses:
* 200:
* description: List of band objects
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/location'
* 500:
* description: Internal server error
* Search for Locations
*
* @query value Search term to look for
*/
location.get("/search", (req: Request, res: Response) => {
Location.findAll({
@@ -188,7 +128,7 @@ location.get("/search", (req: Request, res: Response) => {
}
]
},
include: locationStructure
include: [ City, Concert ]
})
.then(locations => {
res.status(200).json(locations)

View File

@@ -1,9 +1,3 @@
/**
* @swagger
* tags:
* name: Orders
* description: API to manage orders
*/
import { Router, Request, Response } from "express";
import { Order } from "../models/ordering/order.model";
import { Concert } from "../models/acts/concert.model";
@@ -21,34 +15,8 @@ import { Account } from "../models/user/account.model";
export const order = Router()
/**
* @swagger
* /orders:
* get:
* summary: Get orders of an account or all available
* tags: [Orders]
* security:
* - JWT: []
* parameters:
* - in: query
* name: id
* schema:
* type: string
* required: false
* description: User account id to filter the orders
* responses:
* 200:
* description: OK
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/order'
* 500:
* description: Internal server error
*/
// Get all orders
order.get("/", verifyToken, (req: Request, res: Response) => {
const accountId = req.query.id
Order.findAll({
include: [
{
@@ -79,56 +47,69 @@ order.get("/", verifyToken, (req: Request, res: Response) => {
},
Address,
Payment,
Account,
],
attributes: {
exclude: [ "accountId", "addressId", "paymentId" ]
}
Account
]
})
.then(orders => {
if (accountId != undefined) {
let filteredOrders = orders.filter(order => {
return order.id == accountId
})
res.status(200).json(filteredOrders)
} else {
res.status(200).json(orders)
}
res.status(200).json(orders)
})
.catch(error => {
res.status(500).send()
})
})
/**
* @swagger
* /orders:
* post:
* summary: Place a new order
* tags: [Orders]
* security:
* - JWT: []
* parameters:
* - in: query
* name: id
* schema:
* type: string
* required: false
* description: User account id to filter the orders
* responses:
* 200:
* description: OK
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/order'
* 500:
* description: Internal server error
*/
order.post("/", verifyToken, (req: Request, res: Response) => {
req.body["accountId"] = req["id"]
// 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)
})
.catch(error => {
res.status(500).send()
})
})
// Place a new order
order.post("/", (req: Request, res: Response) => {
Order.create(req.body)
.then(async order => {
for (let ticket of req.body.tickets) {
@@ -156,30 +137,6 @@ order.post("/", verifyToken, (req: Request, res: Response) => {
})
})
/**
* @swagger
* /orders:
* patch:
* summary: Update an order
* tags: [Orders]
* parameters:
* - in: body
* name: order
* schema:
* type: object
* required: true
* description: Updated order object
* responses:
* 200:
* description: OK
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/order'
* 500:
* description: Internal server error
*/
order.patch("/", (req: Request, res: Response) => {
Order.update(req.body, {
where: {

View File

@@ -1,8 +1,6 @@
import express from 'express'
import cors from 'cors'
import bodyParser from 'body-parser'
import swaggerJsdoc from "swagger-jsdoc"
import swaggerUi from "swagger-ui-express"
import { api } from './routes/api.routes'
import { startDatabase } from './database'
import { order } from './routes/order.routes'
@@ -14,7 +12,6 @@ import { location } from './routes/location.routes'
import { city } from './routes/city.routes'
import { exercises } from './routes/exercise.routes'
import { files } from './routes/files.routes'
import swaggerFile from './swagger.json'
const app = express()
const port = 3000
@@ -49,17 +46,6 @@ app.use("/accounts", account)
app.use("/cities", city)
app.use("/concerts", concert)
// Swagger API documentation
const specs = swaggerJsdoc(swaggerFile);
app.use(
"/api-docs",
swaggerUi.serve,
swaggerUi.setup(specs, { explorer: true })
)
// Start server
const server = app.listen(port, () => {
console.log(`Server is running and listening to port ${port}`)

View File

@@ -1,796 +0,0 @@
{
"swagger": "2.0",
"definition": {
"openapi": "3.1.0",
"info": {
"title": "EventMaster API",
"version": "0.2.0",
"description": "Dokumentation über alle API-Endpunkte des Backends",
"license": {
"name": "MIT",
"url": "https://spdx.org/licenses/MIT.html"
}
},
"servers": [
{
"url": "http://localhost:3000"
}
],
"components": {
"securitySchemes": {
"JWT": {
"type": "apiKey",
"in": "header",
"name": "Json Web Token"
}
},
"schemas": {
"city": {
"type": "object",
"properties": {
"id": {
"type": "number",
"description": "The auto-generated id"
},
"name": {
"type": "string",
"description": "Name of the city"
},
"country": {
"type": "string",
"description": "Name of country of the city"
}
},
"example": {
"id": 2,
"name": "Hannover",
"country": "Germany"
}
},
"loginResponse": {
"type": "object",
"properties": {
"success": {
"type": "boolean",
"description": "Login successful state"
},
"token": {
"type": "string",
"description": "Individual created access token"
}
},
"example": {
"success": true,
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjI2MiwiaWF0IjoxNzMzNzYwOTY3fQ.I3rR71c-k2Y2WB0dkd1QEgHxsIRGl4s69YprBNuhX7w"
}
},
"minimalAccount": {
"type": "object",
"properties": {
"username": {
"type": "string",
"description": "Account username"
},
"password": {
"type": "string",
"description": "Encrypted password"
},
"email": {
"type": "string",
"description": "E-Mail address of user"
}
},
"example": {
"username": "maxmustermann",
"password": "supersecret",
"email": "tijjji@didjhli.de"
}
},
"minimalAccountResponse": {
"type": "object",
"properties": {
"id": {
"type": "number",
"description": "The auto-generated id"
},
"username": {
"type": "string",
"description": "Account username"
},
"password": {
"type": "string",
"description": "Encrypted password"
},
"email": {
"type": "string",
"description": "E-Mail address of user"
},
"accountRoleId": {
"type": "number",
"description": "ID of account role"
}
},
"example": {
"id": 263,
"username": "maxmustermann",
"password": "8746fb88adbae61ffa68193ee0bb8050",
"email": "tijjji@didjhli.de",
"accountRoleId": 1
}
},
"placeOrderBody": {
"type": "object",
"properties": {
"username": {
"type": "string",
"description": "Account username"
},
"password": {
"type": "string",
"description": "Encrypted password"
},
"email": {
"type": "string",
"description": "E-Mail address of user"
},
"accountRoleId": {
"type": "number",
"description": "ID of account role"
}
},
"example": {
"id": 263,
"username": "maxmustermann",
"password": "8746fb88adbae61ffa68193ee0bb8050",
"email": "tijjji@didjhli.de",
"accountRoleId": 1
}
},
"genre": {
"type": "object",
"properties": {
"id": {
"type": "number",
"description": "The auto-generated id"
},
"name": {
"type": "string",
"description": "Name of the genre"
},
"bands": {
"type": "object",
"description": "Bands with this genre object"
}
},
"example": [
{
"id": 562,
"name": "Funk Rock",
"bands": [
{
"images": [
"http://localhost:3000/static/bands/red-hot-chili-peppers-1.jpg",
"http://localhost:3000/static/bands/red-hot-chili-peppers-2.jpg",
"http://localhost:3000/static/bands/red-hot-chili-peppers-3.jpg",
"http://localhost:3000/static/bands/red-hot-chili-peppers-4.jpg"
],
"id": 265,
"name": "Red Hot Chili Peppers",
"foundingYear": 1983,
"descriptionEn": "The Red Hot Chili Peppers are an American rock band formed in Los Angeles in 1983, comprising vocalist Anthony Kiedis, bassist Flea, drummer Chad Smith, and guitarist John Frusciante. Their music incorporates elements of alternative rock, funk, punk rock, hard rock, hip hop, and psychedelic rock. Their eclectic range has influenced genres such as funk metal, rap metal, rap rock, and nu metal. With over 120 million records sold worldwide, the Red Hot Chili Peppers are one of the top-selling bands of all time.",
"descriptionDe": "Red Hot Chili Peppers (Abkürzung: RHCP) ist eine 1983 gegründete US-amerikanische Funk- und Alternative-Rockband. Sie zählt zu den kommerziell erfolgreichsten Vertretern des Crossover. Ihr Album Blood Sugar Sex Magik gilt als eines der bedeutendsten dieses Genres.",
"imageMembers": "http://localhost:3000/static/bands/red-hot-chili-peppers-members.jpg",
"logo": "http://localhost:3000/static/bands/red-hot-chili-peppers-logo.png",
"BandGenre": {
"id": 793,
"genreId": 562,
"bandId": 265
}
}
]
},
{
"id": 563,
"name": "Alternative Rock",
"bands": [
{
"images": [
"http://localhost:3000/static/bands/red-hot-chili-peppers-1.jpg",
"http://localhost:3000/static/bands/red-hot-chili-peppers-2.jpg",
"http://localhost:3000/static/bands/red-hot-chili-peppers-3.jpg",
"http://localhost:3000/static/bands/red-hot-chili-peppers-4.jpg"
],
"id": 265,
"name": "Red Hot Chili Peppers",
"foundingYear": 1983,
"descriptionEn": "The Red Hot Chili Peppers are an American rock band formed in Los Angeles in 1983, comprising vocalist Anthony Kiedis, bassist Flea, drummer Chad Smith, and guitarist John Frusciante. Their music incorporates elements of alternative rock, funk, punk rock, hard rock, hip hop, and psychedelic rock. Their eclectic range has influenced genres such as funk metal, rap metal, rap rock, and nu metal. With over 120 million records sold worldwide, the Red Hot Chili Peppers are one of the top-selling bands of all time.",
"descriptionDe": "Red Hot Chili Peppers (Abkürzung: RHCP) ist eine 1983 gegründete US-amerikanische Funk- und Alternative-Rockband. Sie zählt zu den kommerziell erfolgreichsten Vertretern des Crossover. Ihr Album Blood Sugar Sex Magik gilt als eines der bedeutendsten dieses Genres.",
"imageMembers": "http://localhost:3000/static/bands/red-hot-chili-peppers-members.jpg",
"logo": "http://localhost:3000/static/bands/red-hot-chili-peppers-logo.png",
"BandGenre": {
"id": 794,
"genreId": 563,
"bandId": 265
}
}
]
}
]
},
"location": {
"type": "object",
"properties": {
"id": {
"type": "number",
"description": "The auto-generated id"
},
"name": {
"type": "string",
"description": "Name of the genre"
},
"bands": {
"type": "object",
"description": "Bands with this genre object"
}
},
"example": {
"id": 562,
"name": "Funk Rock",
"bands": [
{
"images": [
"http://localhost:3000/static/bands/red-hot-chili-peppers-1.jpg",
"http://localhost:3000/static/bands/red-hot-chili-peppers-2.jpg",
"http://localhost:3000/static/bands/red-hot-chili-peppers-3.jpg",
"http://localhost:3000/static/bands/red-hot-chili-peppers-4.jpg"
],
"id": 265,
"name": "Red Hot Chili Peppers",
"foundingYear": 1983,
"descriptionEn": "The Red Hot Chili Peppers are an American rock band formed in Los Angeles in 1983, comprising vocalist Anthony Kiedis, bassist Flea, drummer Chad Smith, and guitarist John Frusciante. Their music incorporates elements of alternative rock, funk, punk rock, hard rock, hip hop, and psychedelic rock. Their eclectic range has influenced genres such as funk metal, rap metal, rap rock, and nu metal. With over 120 million records sold worldwide, the Red Hot Chili Peppers are one of the top-selling bands of all time.",
"descriptionDe": "Red Hot Chili Peppers (Abkürzung: RHCP) ist eine 1983 gegründete US-amerikanische Funk- und Alternative-Rockband. Sie zählt zu den kommerziell erfolgreichsten Vertretern des Crossover. Ihr Album Blood Sugar Sex Magik gilt als eines der bedeutendsten dieses Genres.",
"imageMembers": "http://localhost:3000/static/bands/red-hot-chili-peppers-members.jpg",
"logo": "http://localhost:3000/static/bands/red-hot-chili-peppers-logo.png",
"BandGenre": {
"id": 793,
"genreId": 562,
"bandId": 265
}
}
]
}
},
"order": {
"type": "object",
"properties": {
"id": {
"type": "number",
"description": "The auto-generated id"
},
"orderedAt": {
"type": "string",
"description": "Timestamp of order"
},
"tickets": {
"type": "array",
"description": "Array of Ticket objects"
},
"addresses": {
"type": "object",
"description": "Address object"
},
"payment": {
"type": "object",
"description": "Payment object"
},
"account": {
"type": "object",
"description": "Account object"
}
},
"example": {
"id": 112,
"orderedAt": "2024-11-29T12:38:36.381Z",
"shipped": false,
"tickets": [
{
"id": 144,
"orderId": 112,
"orderPrice": 184,
"concertId": 892,
"seatId": 106331,
"concert": {
"id": 892,
"date": "2024-11-30",
"name": "Unlimited Love",
"price": 92,
"image": "http://localhost:3000/static/concerts/unlimited-love-tour.jpg",
"inStock": 170,
"offered": true,
"bandId": 265,
"locationId": 834,
"band": {
"images": [
"http://localhost:3000/static/bands/red-hot-chili-peppers-1.jpg",
"http://localhost:3000/static/bands/red-hot-chili-peppers-2.jpg",
"http://localhost:3000/static/bands/red-hot-chili-peppers-3.jpg",
"http://localhost:3000/static/bands/red-hot-chili-peppers-4.jpg"
],
"id": 265,
"name": "Red Hot Chili Peppers",
"foundingYear": 1983,
"descriptionEn": "The Red Hot Chili Peppers are an American rock band formed in Los Angeles in 1983, comprising vocalist Anthony Kiedis, bassist Flea, drummer Chad Smith, and guitarist John Frusciante. Their music incorporates elements of alternative rock, funk, punk rock, hard rock, hip hop, and psychedelic rock. Their eclectic range has influenced genres such as funk metal, rap metal, rap rock, and nu metal. With over 120 million records sold worldwide, the Red Hot Chili Peppers are one of the top-selling bands of all time.",
"descriptionDe": "Red Hot Chili Peppers (Abkürzung: RHCP) ist eine 1983 gegründete US-amerikanische Funk- und Alternative-Rockband. Sie zählt zu den kommerziell erfolgreichsten Vertretern des Crossover. Ihr Album Blood Sugar Sex Magik gilt als eines der bedeutendsten dieses Genres.",
"imageMembers": "http://localhost:3000/static/bands/red-hot-chili-peppers-members.jpg",
"logo": "http://localhost:3000/static/bands/red-hot-chili-peppers-logo.png"
},
"location": {
"id": 834,
"urlName": "swiss-life-hall",
"name": "Swiss Life Hall",
"address": "Ferdinand-Wilhelm-Fricke-Weg 8",
"cityId": 246,
"imageIndoor": "http://localhost:3000/static/locations/swiss-life-hall-indoor.jpg",
"imageOutdoor": "http://localhost:3000/static/locations/swiss-life-hall-outdoor.jpg",
"layout": 2,
"capacity": 180,
"city": {
"id": 246,
"name": "Hannover",
"country": "Germany"
}
}
},
"seat": {
"id": 106331,
"seatNr": 1,
"seatRowId": 14701,
"seatRow": {
"id": 14701,
"row": 0,
"seatGroupId": 3872,
"seatGroup": {
"id": 3872,
"name": "A",
"surcharge": 30,
"capacity": 40,
"standingArea": true,
"locationId": 834
}
}
}
}
],
"address": {
"id": 342,
"accountId": 255,
"street": "Laportestraße",
"houseNumber": 22,
"postalCode": 30449,
"city": "Hannover"
},
"payment": {
"id": 247,
"accountId": 255,
"bankName": "Deutsche Bank",
"iban": "DE92500105175721645777"
},
"account": {
"id": 255,
"username": "hagemeister93",
"password": "e1e3981e5b0c009c018c5726a4be5eee",
"email": "hagemeister93@gmail.com",
"firstName": "Laurin",
"lastName": "Hagemeister",
"accountRoleId": 1
}
}
},
"useraccount": {
"type": "object",
"properties": {
"id": {
"type": "number",
"description": "The auto-generated id"
},
"username": {
"type": "string",
"description": "Account username"
},
"password": {
"type": "string",
"description": "Encrypted password"
},
"email": {
"type": "string",
"description": "E-Mail address of user"
},
"firstName": {
"type": "string",
"description": "First name of user"
},
"lastName": {
"type": "string",
"description": "Last name of user"
},
"addresses": {
"type": "array",
"description": "Array of Address objects"
},
"accountRole": {
"type": "object",
"description": "Account role object"
},
"payments": {
"type": "array",
"description": "Array of Payments objects"
}
},
"example": {
"id": 262,
"username": "max",
"password": "06f7a5f329fed099ad36026f9623e6ce",
"email": "titi@didi.de",
"firstName": "Max",
"lastName": "Mustermann",
"accountRoleId": 1,
"addresses": [
{
"id": 352,
"accountId": 262,
"street": "Musterstraße",
"houseNumber": 21,
"postalCode": 30167,
"city": "Hannover"
}
],
"accountRole": {
"id": 1,
"name": "User",
"privilegeBuy": true,
"privilegeAdminPanel": false
},
"payments": [
{
"id": 254,
"accountId": 262,
"bankName": "Deutsche Bank",
"iban": "DE293948484738383829"
}
]
}
},
"exercise": {
"type": "object",
"properties": {
"id": {
"type": "number",
"description": "The auto-generated id"
},
"nameDe": {
"type": "string",
"description": "German exercise name"
},
"nameEn": {
"type": "string",
"description": "English exercise name"
},
"exerciseNr": {
"type": "number",
"description": "Number of exercise in group"
},
"descriptionDe": {
"type": "string",
"description": "German description text"
},
"descriptionEn": {
"type": "string",
"description": "English description text"
},
"solved": {
"type": "boolean",
"description": "State of solved"
},
"exerciseGroup": {
"type": "object",
"description": "Exercise group object"
}
},
"example": {
"id": 350,
"nameDe": "Registrieren",
"nameEn": "Register",
"exerciseNr": 1,
"descriptionDe": "Wir richten uns einen gewöhnlichen Account auf der Plattform ein. Navigiere hierzu auf die Account-Seite und registriere dich.",
"descriptionEn": "Create a new account in the online shop",
"solved": true,
"exerciseGroup": {
"id": 113,
"nameDe": "Den Shop kennenlernen",
"nameEn": "Getting to know the shop",
"groupNr": 0,
"descriptionDe": "Vor einem Angriff ist es wichtig zu verstehen, wie die Webseite aufgebaut ist. Wie sind die URLs strukturiert? Wo befinden sich Eingabefelder welche im Backend eine SQL Abfrage stellen?",
"descriptionEn": "todo"
}
}
},
"folder": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Name of folder"
},
"nrOrItems": {
"type": "number",
"description": "Number of files in folder"
}
},
"example": {
"name": "artists",
"description": 41
}
},
"file": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Name of file"
},
"size": {
"type": "number",
"description": "File size in Bytes"
},
"content": {
"type": "string",
"description": "Text content, only for Text/Code files"
},
"url": {
"type": "string",
"description": "Resource URL"
},
"copyright": {
"type": "object",
"description": "Copyright object"
}
},
"example": {
"name": "alex-turner.jpg",
"size": 551625,
"content": "",
"url": "http://localhost:3000/static/artists/alex-turner.jpg",
"copyright": {
"image": "alex-turner.jpg",
"license": "CC BY 2.0",
"creator": "Raph_PH",
"url": "https://upload.wikimedia.org/wikipedia/commons/9/95/Alex_Turner%2C_Way_Out_West_2018.jpg"
}
}
},
"concert": {
"type": "object",
"properties": {
"id": {
"type": "number",
"description": "The auto-generated id"
},
"date": {
"type": "string",
"description": "Date of the concert"
},
"name": {
"type": "string",
"description": "Name of concert"
},
"price": {
"type": "string",
"description": "Lowest price of concert"
},
"image": {
"type": "number",
"description": "Concert image"
},
"inStock": {
"type": "string",
"description": "Amount of available tickets"
},
"offered": {
"type": "string",
"description": "Display concert in UI"
},
"band": {
"type": "object",
"description": "Band object"
},
"location": {
"type": "object",
"description": "Location object"
}
},
"example": [
{
"id": 892,
"date": "2024-11-30",
"name": "Unlimited Love",
"price": 92,
"image": "http://localhost:3000/static/concerts/unlimited-love-tour.jpg",
"inStock": 169,
"offered": true,
"bandId": 265,
"locationId": 834,
"band": {
"images": [
"http://localhost:3000/static/bands/red-hot-chili-peppers-1.jpg",
"http://localhost:3000/static/bands/red-hot-chili-peppers-2.jpg",
"http://localhost:3000/static/bands/red-hot-chili-peppers-3.jpg",
"http://localhost:3000/static/bands/red-hot-chili-peppers-4.jpg"
],
"id": 265,
"name": "Red Hot Chili Peppers",
"foundingYear": 1983,
"descriptionEn": "The Red Hot Chili Peppers are an American rock band formed in Los Angeles in 1983, comprising vocalist Anthony Kiedis, bassist Flea, drummer Chad Smith, and guitarist John Frusciante. Their music incorporates elements of alternative rock, funk, punk rock, hard rock, hip hop, and psychedelic rock. Their eclectic range has influenced genres such as funk metal, rap metal, rap rock, and nu metal. With over 120 million records sold worldwide, the Red Hot Chili Peppers are one of the top-selling bands of all time.",
"descriptionDe": "Red Hot Chili Peppers (Abkürzung: RHCP) ist eine 1983 gegründete US-amerikanische Funk- und Alternative-Rockband. Sie zählt zu den kommerziell erfolgreichsten Vertretern des Crossover. Ihr Album Blood Sugar Sex Magik gilt als eines der bedeutendsten dieses Genres.",
"imageMembers": "http://localhost:3000/static/bands/red-hot-chili-peppers-members.jpg",
"logo": "http://localhost:3000/static/bands/red-hot-chili-peppers-logo.png"
},
"location": {
"id": 834,
"urlName": "swiss-life-hall",
"name": "Swiss Life Hall",
"address": "Ferdinand-Wilhelm-Fricke-Weg 8",
"imageIndoor": "http://localhost:3000/static/locations/swiss-life-hall-indoor.jpg",
"imageOutdoor": "http://localhost:3000/static/locations/swiss-life-hall-outdoor.jpg",
"layout": 2,
"capacity": 180,
"city": {
"id": 246,
"name": "Hannover",
"country": "Germany"
},
"seatGroups": []
}
}
]
},
"band": {
"type": "object",
"properties": {
"id": {
"type": "integer",
"description": "The auto-generated id"
},
"name": {
"type": "string",
"description": "Name of the band"
},
"foundingYear": {
"type": "string",
"description": "Founding year of the band"
},
"descriptionEn": {
"type": "string",
"description": "English description text"
},
"descriptionDe": {
"type": "string",
"description": "German description text"
},
"imageMembers": {
"type": "string",
"description": "URL to image of band members"
},
"logo": {
"type": "string",
"description": "URL to image of band logo"
},
"genres": {
"type": "array",
"description": "Array of Genre objects which fits the bands music"
},
"concerts": {
"type": "array",
"description": "Array of Concert objects"
},
"nrOfConcerts": {
"type": "number",
"description": "Number of concerts"
},
"rating": {
"type": "number",
"description": "Average rating of the band"
}
},
"example": {
"images": [
"http://localhost:3000/static/bands/red-hot-chili-peppers-1.jpg",
"http://localhost:3000/static/bands/red-hot-chili-peppers-2.jpg",
"http://localhost:3000/static/bands/red-hot-chili-peppers-3.jpg",
"http://localhost:3000/static/bands/red-hot-chili-peppers-4.jpg"
],
"id": 265,
"name": "Red Hot Chili Peppers",
"foundingYear": 1983,
"descriptionEn": "The Red Hot Chili Peppers are an American rock band formed in Los Angeles in 1983, comprising vocalist Anthony Kiedis, bassist Flea, drummer Chad Smith, and guitarist John Frusciante. Their music incorporates elements of alternative rock, funk, punk rock, hard rock, hip hop, and psychedelic rock. Their eclectic range has influenced genres such as funk metal, rap metal, rap rock, and nu metal. With over 120 million records sold worldwide, the Red Hot Chili Peppers are one of the top-selling bands of all time.",
"descriptionDe": "Red Hot Chili Peppers (Abkürzung: RHCP) ist eine 1983 gegründete US-amerikanische Funk- und Alternative-Rockband. Sie zählt zu den kommerziell erfolgreichsten Vertretern des Crossover. Ihr Album Blood Sugar Sex Magik gilt als eines der bedeutendsten dieses Genres.",
"imageMembers": "http://localhost:3000/static/bands/red-hot-chili-peppers-members.jpg",
"logo": "http://localhost:3000/static/bands/red-hot-chili-peppers-logo.png",
"genres": [
{
"name": "Funk Rock"
},
{
"name": "Alternative Rock"
},
{
"name": "Crossover"
}
],
"concerts": [
{
"id": 892,
"date": "2024-11-30",
"name": "Unlimited Love",
"price": 92,
"image": "http://localhost:3000/static/concerts/unlimited-love-tour.jpg",
"inStock": 170,
"offered": true,
"bandId": 265,
"locationId": 834
},
{
"id": 893,
"date": "2024-12-07",
"name": "Unlimited Love",
"price": 92,
"image": "http://localhost:3000/static/concerts/unlimited-love-tour.jpg",
"inStock": 170,
"offered": true,
"bandId": 265,
"locationId": 834
},
{
"id": 894,
"date": "2024-12-11",
"name": "Unlimited Love",
"price": 119.9,
"image": "http://localhost:3000/static/concerts/unlimited-love-tour.jpg",
"inStock": 8736,
"offered": true,
"bandId": 265,
"locationId": 838
},
{
"id": 895,
"date": "2024-12-18",
"name": "Unlimited Love",
"price": 114.9,
"image": "http://localhost:3000/static/concerts/unlimited-love-tour.jpg",
"inStock": 2793,
"offered": true,
"bandId": 265,
"locationId": 842
},
{
"id": 896,
"date": "2024-12-30",
"name": "Unlimited Love",
"price": 124.9,
"image": "http://localhost:3000/static/concerts/unlimited-love-tour.jpg",
"inStock": 3079,
"offered": true,
"bandId": 265,
"locationId": 845
}
],
"nrOfConcerts": 5,
"rating": 4.428571428571429
}
}
}
}
},
"apis": [
"./backend/routes/*.ts"
]
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,84 +0,0 @@
<mxfile host="Electron" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/26.0.9 Chrome/128.0.6613.186 Electron/32.2.5 Safari/537.36" version="26.0.9">
<diagram name="Page-1" id="6ideKt-3XiqBCAVgAsyQ">
<mxGraphModel dx="1195" dy="689" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1169" pageHeight="827" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="KDqfIFqn06qLZiIO6-10-49" value="Frontend" style="rounded=0;whiteSpace=wrap;html=1;verticalAlign=top;" parent="1" vertex="1">
<mxGeometry x="40" y="90" width="400" height="220" as="geometry" />
</mxCell>
<mxCell id="KDqfIFqn06qLZiIO6-10-52" value="Subpage&lt;div&gt;&quot;All Bands&quot;&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;verticalAlign=top;" parent="1" vertex="1">
<mxGeometry x="50" y="120" width="150" height="180" as="geometry" />
</mxCell>
<mxCell id="KDqfIFqn06qLZiIO6-10-50" value="Backend" style="rounded=0;whiteSpace=wrap;html=1;verticalAlign=top;" parent="1" vertex="1">
<mxGeometry x="630" y="90" width="210" height="280" as="geometry" />
</mxCell>
<mxCell id="KDqfIFqn06qLZiIO6-10-55" value="API Endpoint" style="rounded=0;whiteSpace=wrap;html=1;verticalAlign=top;" parent="1" vertex="1">
<mxGeometry x="650" y="120" width="170" height="100" as="geometry" />
</mxCell>
<mxCell id="KDqfIFqn06qLZiIO6-10-46" value="bandStore" style="rounded=0;whiteSpace=wrap;html=1;verticalAlign=top;" parent="1" vertex="1">
<mxGeometry x="270" y="120" width="160" height="160" as="geometry" />
</mxCell>
<mxCell id="KDqfIFqn06qLZiIO6-10-70" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;strokeColor=#00CC00;" parent="1" source="KDqfIFqn06qLZiIO6-10-47" target="KDqfIFqn06qLZiIO6-10-65" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="KDqfIFqn06qLZiIO6-10-71" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;strokeColor=#00CC00;" parent="1" source="KDqfIFqn06qLZiIO6-10-47" target="KDqfIFqn06qLZiIO6-10-66" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="KDqfIFqn06qLZiIO6-10-73" value="&lt;font color=&quot;#00cc00&quot;&gt;Handles&lt;/font&gt;&lt;div&gt;&lt;font color=&quot;#00cc00&quot;&gt;changes&lt;/font&gt;&lt;/div&gt;" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;strokeColor=#00CC00;" parent="1" source="KDqfIFqn06qLZiIO6-10-47" target="KDqfIFqn06qLZiIO6-10-72" edge="1">
<mxGeometry x="0.168" y="24" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="KDqfIFqn06qLZiIO6-10-47" value="bands" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="290" y="230" width="120" height="30" as="geometry" />
</mxCell>
<mxCell id="KDqfIFqn06qLZiIO6-10-59" value="&lt;div style=&quot;&quot;&gt;&lt;font style=&quot;color: rgb(204, 0, 0);&quot;&gt;Stores&lt;/font&gt;&lt;/div&gt;&lt;div style=&quot;&quot;&gt;&lt;font style=&quot;color: rgb(204, 0, 0);&quot;&gt;results&lt;/font&gt;&lt;/div&gt;" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;align=left;fillColor=#e51400;strokeColor=#B20000;" parent="1" source="KDqfIFqn06qLZiIO6-10-48" target="KDqfIFqn06qLZiIO6-10-47" edge="1">
<mxGeometry x="-0.2" y="10" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="KDqfIFqn06qLZiIO6-10-67" value="&lt;font style=&quot;color: rgb(0, 29, 188);&quot;&gt;requests&lt;/font&gt;" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.25;exitDx=0;exitDy=0;entryX=0;entryY=0.25;entryDx=0;entryDy=0;verticalAlign=bottom;fillColor=#0050ef;strokeColor=#001DBC;" parent="1" source="KDqfIFqn06qLZiIO6-10-48" target="KDqfIFqn06qLZiIO6-10-56" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="KDqfIFqn06qLZiIO6-10-48" value="getBands()" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="290" y="150" width="120" height="30" as="geometry" />
</mxCell>
<mxCell id="KDqfIFqn06qLZiIO6-10-53" value="&lt;font color=&quot;#bd7000&quot;&gt;Call function&lt;/font&gt;" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.25;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;verticalAlign=bottom;fillColor=#f0a30a;strokeColor=#BD7000;" parent="1" source="KDqfIFqn06qLZiIO6-10-52" target="KDqfIFqn06qLZiIO6-10-48" edge="1">
<mxGeometry x="-0.2148" y="7" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="KDqfIFqn06qLZiIO6-10-68" value="&lt;font style=&quot;color: rgb(0, 87, 0);&quot;&gt;send http code &amp;amp;&lt;/font&gt;&lt;div&gt;&lt;span style=&quot;color: light-dark(rgb(0, 87, 0), rgb(0, 87, 0)); background-color: light-dark(#ffffff, var(--ge-dark-color, #121212));&quot;&gt;data sets&lt;/span&gt;&lt;/div&gt;" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.75;exitDx=0;exitDy=0;entryX=1;entryY=0.75;entryDx=0;entryDy=0;verticalAlign=top;fillColor=#008a00;strokeColor=#005700;" parent="1" source="KDqfIFqn06qLZiIO6-10-56" target="KDqfIFqn06qLZiIO6-10-48" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="KDqfIFqn06qLZiIO6-10-56" value="/bands" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="670" y="150" width="120" height="30" as="geometry" />
</mxCell>
<mxCell id="KDqfIFqn06qLZiIO6-10-63" value="&lt;font style=&quot;color: rgb(0, 153, 153);&quot;&gt;get raw&lt;/font&gt;&lt;div&gt;&lt;font color=&quot;#009999&quot;&gt;datasets&lt;/font&gt;&lt;/div&gt;" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.855;exitY=0;exitDx=0;exitDy=4.35;exitPerimeter=0;align=left;strokeColor=#009999;" parent="1" source="KDqfIFqn06qLZiIO6-10-61" edge="1">
<mxGeometry x="-0.2154" y="-9" relative="1" as="geometry">
<mxPoint as="offset" />
<mxPoint x="751" y="180" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="KDqfIFqn06qLZiIO6-10-61" value="DB" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;size=15;" parent="1" vertex="1">
<mxGeometry x="700" y="280" width="60" height="80" as="geometry" />
</mxCell>
<mxCell id="KDqfIFqn06qLZiIO6-10-62" value="&lt;font style=&quot;color: rgb(255, 0, 255);&quot;&gt;sends&lt;/font&gt;&lt;div&gt;&lt;font style=&quot;color: rgb(255, 0, 255);&quot;&gt;SQL&lt;/font&gt;&lt;/div&gt;&lt;div&gt;&lt;font style=&quot;color: rgb(255, 0, 255);&quot;&gt;command&lt;/font&gt;&lt;/div&gt;" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.321;exitY=1.017;exitDx=0;exitDy=0;entryX=0.145;entryY=0;entryDx=0;entryDy=4.35;entryPerimeter=0;align=right;strokeColor=#FF00FF;exitPerimeter=0;" parent="1" source="KDqfIFqn06qLZiIO6-10-56" target="KDqfIFqn06qLZiIO6-10-61" edge="1">
<mxGeometry x="0.2159" y="-9" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="KDqfIFqn06qLZiIO6-10-65" value="Coldplay" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="65" y="170" width="120" height="30" as="geometry" />
</mxCell>
<mxCell id="KDqfIFqn06qLZiIO6-10-66" value="Muse" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="65" y="210" width="120" height="30" as="geometry" />
</mxCell>
<mxCell id="KDqfIFqn06qLZiIO6-10-72" value="Radiohead" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="65" y="250" width="120" height="30" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

View File

@@ -0,0 +1,181 @@
<mxfile host="Electron" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/24.7.17 Chrome/128.0.6613.36 Electron/32.0.1 Safari/537.36" version="24.7.17">
<diagram name="Page-1" id="z4ePnmX8JLkG3N1GDPOr">
<mxGraphModel dx="1783" dy="1720" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="Fk8QGSgGJ1bhqam2HO4i-20" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="Fk8QGSgGJ1bhqam2HO4i-1" target="Fk8QGSgGJ1bhqam2HO4i-3" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Fk8QGSgGJ1bhqam2HO4i-21" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="Fk8QGSgGJ1bhqam2HO4i-1" target="Fk8QGSgGJ1bhqam2HO4i-15" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="fY-Wzga6seKQwhded4A5-12" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" target="fY-Wzga6seKQwhded4A5-7">
<mxGeometry relative="1" as="geometry">
<mxPoint x="-360" y="700" as="targetPoint" />
<mxPoint x="-720" y="240" as="sourcePoint" />
<Array as="points">
<mxPoint x="-720" y="540" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="fY-Wzga6seKQwhded4A5-16" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="Fk8QGSgGJ1bhqam2HO4i-1" target="Fk8QGSgGJ1bhqam2HO4i-13">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="fY-Wzga6seKQwhded4A5-17" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="Fk8QGSgGJ1bhqam2HO4i-1" target="fY-Wzga6seKQwhded4A5-8">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="fY-Wzga6seKQwhded4A5-22" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;" edge="1" parent="1" source="Fk8QGSgGJ1bhqam2HO4i-1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="-680" y="-20" as="targetPoint" />
<Array as="points">
<mxPoint x="-720" y="-20" />
<mxPoint x="-680" y="-20" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="fY-Wzga6seKQwhded4A5-23" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="Fk8QGSgGJ1bhqam2HO4i-1" target="fY-Wzga6seKQwhded4A5-1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="fY-Wzga6seKQwhded4A5-24" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="Fk8QGSgGJ1bhqam2HO4i-1" target="Fk8QGSgGJ1bhqam2HO4i-14">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="fY-Wzga6seKQwhded4A5-25" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="Fk8QGSgGJ1bhqam2HO4i-1" target="Fk8QGSgGJ1bhqam2HO4i-4">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Fk8QGSgGJ1bhqam2HO4i-1" value="Homepage&lt;div&gt;&lt;font face=&quot;Courier New&quot;&gt;&lt;b&gt;/&lt;/b&gt;&lt;/font&gt;&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="-800" y="-120" width="160" height="40" as="geometry" />
</mxCell>
<mxCell id="fY-Wzga6seKQwhded4A5-26" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="Fk8QGSgGJ1bhqam2HO4i-2" target="Fk8QGSgGJ1bhqam2HO4i-9">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="fY-Wzga6seKQwhded4A5-27" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="Fk8QGSgGJ1bhqam2HO4i-2" target="Fk8QGSgGJ1bhqam2HO4i-8">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="fY-Wzga6seKQwhded4A5-28" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="Fk8QGSgGJ1bhqam2HO4i-2" target="Fk8QGSgGJ1bhqam2HO4i-7">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="fY-Wzga6seKQwhded4A5-40" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;dashed=1;fillColor=#6d8764;strokeColor=#3A5431;" edge="1" parent="1" target="Fk8QGSgGJ1bhqam2HO4i-2">
<mxGeometry relative="1" as="geometry">
<mxPoint x="-679.6666666666667" y="70" as="targetPoint" />
<mxPoint x="-680" y="-20" as="sourcePoint" />
</mxGeometry>
</mxCell>
<mxCell id="fY-Wzga6seKQwhded4A5-43" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="Fk8QGSgGJ1bhqam2HO4i-2" target="fY-Wzga6seKQwhded4A5-42">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Fk8QGSgGJ1bhqam2HO4i-2" value="AccountHomePage&lt;div&gt;&lt;b style=&quot;font-family: &amp;quot;Courier New&amp;quot;;&quot;&gt;&lt;font style=&quot;font-size: 10px;&quot;&gt;/account&lt;/font&gt;&lt;/b&gt;&lt;br&gt;&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="-600" y="-40" width="160" height="40" as="geometry" />
</mxCell>
<mxCell id="fY-Wzga6seKQwhded4A5-31" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="Fk8QGSgGJ1bhqam2HO4i-3" target="Fk8QGSgGJ1bhqam2HO4i-5">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Fk8QGSgGJ1bhqam2HO4i-3" value="BandsPage&lt;div&gt;&lt;b style=&quot;font-family: &amp;quot;Courier New&amp;quot;;&quot;&gt;&lt;font style=&quot;font-size: 10px;&quot;&gt;/bands&lt;/font&gt;&lt;/b&gt;&lt;br&gt;&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="-600" y="280" width="160" height="40" as="geometry" />
</mxCell>
<mxCell id="Fk8QGSgGJ1bhqam2HO4i-4" value="PreferencesPage&lt;div&gt;&lt;b style=&quot;font-family: &amp;quot;Courier New&amp;quot;;&quot;&gt;&lt;font style=&quot;font-size: 10px;&quot;&gt;/preferences&lt;/font&gt;&lt;/b&gt;&lt;br&gt;&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="-600" y="600" width="160" height="40" as="geometry" />
</mxCell>
<mxCell id="fY-Wzga6seKQwhded4A5-10" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="Fk8QGSgGJ1bhqam2HO4i-5" target="fY-Wzga6seKQwhded4A5-5">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="-280" y="330" />
<mxPoint x="-280" y="330" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="Fk8QGSgGJ1bhqam2HO4i-5" value="BandDetailPage&lt;div&gt;&lt;b style=&quot;font-family: &amp;quot;Courier New&amp;quot;;&quot;&gt;&lt;font style=&quot;font-size: 10px;&quot;&gt;/bands/detail/:bandName&lt;/font&gt;&lt;/b&gt;&lt;br&gt;&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="-360" y="280" width="160" height="40" as="geometry" />
</mxCell>
<mxCell id="fY-Wzga6seKQwhded4A5-41" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;dashed=1;fillColor=#e51400;strokeColor=#B20000;" edge="1" parent="1" target="Fk8QGSgGJ1bhqam2HO4i-7">
<mxGeometry relative="1" as="geometry">
<mxPoint x="-200" y="-220" as="targetPoint" />
<mxPoint x="-680" y="-20" as="sourcePoint" />
<Array as="points">
<mxPoint x="-680" y="140" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="Fk8QGSgGJ1bhqam2HO4i-7" value="LoginPage&lt;div&gt;&lt;b style=&quot;font-family: &amp;quot;Courier New&amp;quot;;&quot;&gt;&lt;font style=&quot;font-size: 10px;&quot;&gt;/account/login&lt;/font&gt;&lt;/b&gt;&lt;br&gt;&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="-360" y="120" width="160" height="40" as="geometry" />
</mxCell>
<mxCell id="Fk8QGSgGJ1bhqam2HO4i-8" value="AccountData&lt;div&gt;&lt;b style=&quot;font-family: &amp;quot;Courier New&amp;quot;;&quot;&gt;&lt;font style=&quot;font-size: 10px;&quot;&gt;/account/data&lt;/font&gt;&lt;/b&gt;&lt;br&gt;&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="-360" y="40" width="160" height="40" as="geometry" />
</mxCell>
<mxCell id="Fk8QGSgGJ1bhqam2HO4i-9" value="OrdersPage&lt;div&gt;&lt;b style=&quot;font-family: &amp;quot;Courier New&amp;quot;;&quot;&gt;&lt;font style=&quot;font-size: 10px;&quot;&gt;/account/orders&lt;/font&gt;&lt;/b&gt;&lt;br&gt;&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="-360" y="-40" width="160" height="40" as="geometry" />
</mxCell>
<mxCell id="Fk8QGSgGJ1bhqam2HO4i-13" value="AdminHome&lt;span style=&quot;background-color: initial;&quot;&gt;Page&lt;/span&gt;&lt;div&gt;&lt;div&gt;&lt;b style=&quot;font-family: &amp;quot;Courier New&amp;quot;;&quot;&gt;&lt;font style=&quot;font-size: 10px;&quot;&gt;/admin&lt;/font&gt;&lt;/b&gt;&lt;br&gt;&lt;/div&gt;&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="-600" y="760" width="160" height="40" as="geometry" />
</mxCell>
<mxCell id="Fk8QGSgGJ1bhqam2HO4i-14" value="HelpPage&lt;div&gt;&lt;b style=&quot;font-family: &amp;quot;Courier New&amp;quot;;&quot;&gt;&lt;font style=&quot;font-size: 10px;&quot;&gt;/help&lt;/font&gt;&lt;/b&gt;&lt;br&gt;&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="-600" y="680" width="160" height="40" as="geometry" />
</mxCell>
<mxCell id="fY-Wzga6seKQwhded4A5-32" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="Fk8QGSgGJ1bhqam2HO4i-15" target="Fk8QGSgGJ1bhqam2HO4i-16">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Fk8QGSgGJ1bhqam2HO4i-15" value="LocationsPage&lt;div&gt;&lt;b style=&quot;font-family: &amp;quot;Courier New&amp;quot;;&quot;&gt;&lt;font style=&quot;font-size: 10px;&quot;&gt;/locations&lt;/font&gt;&lt;/b&gt;&lt;br&gt;&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="-600" y="440" width="160" height="40" as="geometry" />
</mxCell>
<mxCell id="fY-Wzga6seKQwhded4A5-9" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;" edge="1" parent="1" source="Fk8QGSgGJ1bhqam2HO4i-16" target="fY-Wzga6seKQwhded4A5-5">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="-280" y="420" />
<mxPoint x="-280" y="420" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="Fk8QGSgGJ1bhqam2HO4i-16" value="LocationsDetailPage&lt;div&gt;&lt;b style=&quot;font-family: &amp;quot;Courier New&amp;quot;;&quot;&gt;&lt;font style=&quot;font-size: 10px;&quot;&gt;/locations/detail/:name&lt;/font&gt;&lt;/b&gt;&lt;br&gt;&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="-360" y="440" width="160" height="40" as="geometry" />
</mxCell>
<mxCell id="fY-Wzga6seKQwhded4A5-30" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="fY-Wzga6seKQwhded4A5-1" target="fY-Wzga6seKQwhded4A5-5">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="fY-Wzga6seKQwhded4A5-1" value="ConcertsPage&lt;div&gt;&lt;b style=&quot;font-family: &amp;quot;Courier New&amp;quot;;&quot;&gt;&lt;font style=&quot;font-size: 10px;&quot;&gt;/concerts&lt;/font&gt;&lt;/b&gt;&lt;br&gt;&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="-600" y="360" width="160" height="40" as="geometry" />
</mxCell>
<mxCell id="fY-Wzga6seKQwhded4A5-3" value="if logged in" style="text;strokeColor=none;align=center;fillColor=none;html=1;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="-680" y="-60" width="80" height="30" as="geometry" />
</mxCell>
<mxCell id="fY-Wzga6seKQwhded4A5-4" value="else" style="text;strokeColor=none;align=center;fillColor=none;html=1;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="-690" y="110" width="80" height="30" as="geometry" />
</mxCell>
<mxCell id="fY-Wzga6seKQwhded4A5-34" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="fY-Wzga6seKQwhded4A5-5" target="fY-Wzga6seKQwhded4A5-7">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="-160" y="380" />
<mxPoint x="-160" y="540" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="fY-Wzga6seKQwhded4A5-5" value="ConcertsBookingPage&lt;div&gt;&lt;b style=&quot;font-family: &amp;quot;Courier New&amp;quot;;&quot;&gt;&lt;font style=&quot;font-size: 10px;&quot;&gt;/concerts/booking/:id&lt;/font&gt;&lt;/b&gt;&lt;br&gt;&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="-360" y="360" width="160" height="40" as="geometry" />
</mxCell>
<mxCell id="fY-Wzga6seKQwhded4A5-7" value="BasketPage&lt;div&gt;&lt;b style=&quot;font-family: &amp;quot;Courier New&amp;quot;;&quot;&gt;&lt;font style=&quot;font-size: 10px;&quot;&gt;/basket&lt;/font&gt;&lt;/b&gt;&lt;br&gt;&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="-600" y="520" width="160" height="40" as="geometry" />
</mxCell>
<mxCell id="fY-Wzga6seKQwhded4A5-37" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="fY-Wzga6seKQwhded4A5-8" target="Fk8QGSgGJ1bhqam2HO4i-5">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="fY-Wzga6seKQwhded4A5-38" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="fY-Wzga6seKQwhded4A5-8" target="fY-Wzga6seKQwhded4A5-5">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="fY-Wzga6seKQwhded4A5-39" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="fY-Wzga6seKQwhded4A5-8" target="Fk8QGSgGJ1bhqam2HO4i-16">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="fY-Wzga6seKQwhded4A5-8" value="SearchPage&lt;div&gt;&lt;b style=&quot;font-family: &amp;quot;Courier New&amp;quot;;&quot;&gt;&lt;font style=&quot;font-size: 10px;&quot;&gt;/search&lt;/font&gt;&lt;/b&gt;&lt;br&gt;&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="-600" y="200" width="160" height="40" as="geometry" />
</mxCell>
<mxCell id="fY-Wzga6seKQwhded4A5-14" value="if ticket add to basket" style="text;strokeColor=none;align=center;fillColor=none;html=1;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="-290" y="510" width="130" height="30" as="geometry" />
</mxCell>
<mxCell id="fY-Wzga6seKQwhded4A5-29" value="on log out" style="text;strokeColor=none;align=center;fillColor=none;html=1;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="-460" y="90" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="fY-Wzga6seKQwhded4A5-42" value="AccountRatingsPage&lt;div&gt;&lt;b style=&quot;font-family: &amp;quot;Courier New&amp;quot;;&quot;&gt;&lt;font style=&quot;font-size: 10px;&quot;&gt;/account/ratings&lt;/font&gt;&lt;/b&gt;&lt;br&gt;&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="-360" y="-120" width="160" height="40" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 133 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

309
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "eventmaster",
"version": "0.2.0",
"version": "0.1.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "eventmaster",
"version": "0.2.0",
"version": "0.1.0",
"license": "MIT",
"dependencies": {
"@mdi/font": "^7.4.47",
@@ -29,9 +29,6 @@
"sequelize": "^6.37.4",
"sequelize-typescript": "^2.1.6",
"sqlite3": "^5.1.7",
"swagger-autogen": "^2.23.7",
"swagger-jsdoc": "^6.2.8",
"swagger-ui-express": "^5.0.1",
"vue": "^3.4.29",
"vue-i18n": "^10.0.4",
"vue-router": "^4.4.5",
@@ -47,8 +44,6 @@
"@types/jsonwebtoken": "^9.0.7",
"@types/multer": "^1.4.12",
"@types/node": "^22.9.0",
"@types/swagger-jsdoc": "^6.0.4",
"@types/swagger-ui-express": "^4.1.7",
"@vitejs/plugin-vue": "^5.1.4",
"concurrently": "^9.0.1",
"copyfiles": "^2.4.1",
@@ -61,50 +56,6 @@
"vue-tsc": "^2.1.10"
}
},
"node_modules/@apidevtools/json-schema-ref-parser": {
"version": "9.1.2",
"resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.1.2.tgz",
"integrity": "sha512-r1w81DpR+KyRWd3f+rk6TNqMgedmAxZP5v5KWlXQWlgMUUtyEJch0DKEci1SorPMiSeM8XPl7MZ3miJ60JIpQg==",
"license": "MIT",
"dependencies": {
"@jsdevtools/ono": "^7.1.3",
"@types/json-schema": "^7.0.6",
"call-me-maybe": "^1.0.1",
"js-yaml": "^4.1.0"
}
},
"node_modules/@apidevtools/openapi-schemas": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@apidevtools/openapi-schemas/-/openapi-schemas-2.1.0.tgz",
"integrity": "sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ==",
"license": "MIT",
"engines": {
"node": ">=10"
}
},
"node_modules/@apidevtools/swagger-methods": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@apidevtools/swagger-methods/-/swagger-methods-3.0.2.tgz",
"integrity": "sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==",
"license": "MIT"
},
"node_modules/@apidevtools/swagger-parser": {
"version": "10.0.3",
"resolved": "https://registry.npmjs.org/@apidevtools/swagger-parser/-/swagger-parser-10.0.3.tgz",
"integrity": "sha512-sNiLY51vZOmSPFZA5TF35KZ2HbgYklQnTSDnkghamzLb3EkNtcQnrBQEj5AOCxHpTtXpqMCRM1CrmV2rG6nw4g==",
"license": "MIT",
"dependencies": {
"@apidevtools/json-schema-ref-parser": "^9.0.6",
"@apidevtools/openapi-schemas": "^2.0.4",
"@apidevtools/swagger-methods": "^3.0.2",
"@jsdevtools/ono": "^7.1.3",
"call-me-maybe": "^1.0.1",
"z-schema": "^5.0.1"
},
"peerDependencies": {
"openapi-types": ">=7"
}
},
"node_modules/@babel/helper-string-parser": {
"version": "7.25.7",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.7.tgz",
@@ -1595,12 +1546,6 @@
"@jridgewell/sourcemap-codec": "^1.4.10"
}
},
"node_modules/@jsdevtools/ono": {
"version": "7.1.3",
"resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz",
"integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==",
"license": "MIT"
},
"node_modules/@malept/cross-spawn-promise": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-2.0.0.tgz",
@@ -1988,13 +1933,6 @@
"win32"
]
},
"node_modules/@scarf/scarf": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@scarf/scarf/-/scarf-1.4.0.tgz",
"integrity": "sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==",
"hasInstallScript": true,
"license": "Apache-2.0"
},
"node_modules/@sideway/address": {
"version": "4.1.5",
"resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz",
@@ -2200,12 +2138,6 @@
"dev": true,
"license": "MIT"
},
"node_modules/@types/json-schema": {
"version": "7.0.15",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
"integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
"license": "MIT"
},
"node_modules/@types/jsonwebtoken": {
"version": "9.0.7",
"resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.7.tgz",
@@ -2324,24 +2256,6 @@
"@types/send": "*"
}
},
"node_modules/@types/swagger-jsdoc": {
"version": "6.0.4",
"resolved": "https://registry.npmjs.org/@types/swagger-jsdoc/-/swagger-jsdoc-6.0.4.tgz",
"integrity": "sha512-W+Xw5epcOZrF/AooUM/PccNMSAFOKWZA5dasNyMujTwsBkU74njSJBpvCCJhHAJ95XRMzQrrW844Btu0uoetwQ==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/swagger-ui-express": {
"version": "4.1.7",
"resolved": "https://registry.npmjs.org/@types/swagger-ui-express/-/swagger-ui-express-4.1.7.tgz",
"integrity": "sha512-ovLM9dNincXkzH4YwyYpll75vhzPBlWx6La89wwvYH7mHjVpf0X0K/vR/aUM7SRxmr5tt9z7E5XJcjQ46q+S3g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/express": "*",
"@types/serve-static": "*"
}
},
"node_modules/@types/validator": {
"version": "13.12.2",
"resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.12.2.tgz",
@@ -3136,6 +3050,7 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"dev": true,
"license": "Python-2.0"
},
"node_modules/array-flatten": {
@@ -3720,12 +3635,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/call-me-maybe": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.2.tgz",
"integrity": "sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==",
"license": "MIT"
},
"node_modules/canvg": {
"version": "3.0.10",
"resolved": "https://registry.npmjs.org/canvg/-/canvg-3.0.10.tgz",
@@ -4461,15 +4370,6 @@
"node": ">=4.0.0"
}
},
"node_modules/deepmerge": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
"integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/defaults": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz",
@@ -4698,18 +4598,6 @@
"node": ">=8"
}
},
"node_modules/doctrine": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
"integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
"license": "Apache-2.0",
"dependencies": {
"esutils": "^2.0.2"
},
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/dompurify": {
"version": "2.5.7",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.5.7.tgz",
@@ -5172,15 +5060,6 @@
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
"license": "MIT"
},
"node_modules/esutils": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
"integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
"license": "BSD-2-Clause",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/etag": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
@@ -5207,9 +5086,9 @@
}
},
"node_modules/exifreader/node_modules/@xmldom/xmldom": {
"version": "0.9.6",
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.9.6.tgz",
"integrity": "sha512-Su4xcxR0CPGwlDHNmVP09fqET9YxbyDXHaSob6JlBH7L6reTYaeim6zbk9o08UarO0L5GTRo3uzl0D+9lSxmvw==",
"version": "0.9.5",
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.9.5.tgz",
"integrity": "sha512-6g1EwSs8cr8JhP1iBxzyVAWM6BIDvx9Y3FZRIQiMDzgG43Pxi8YkWOZ0nQj2NHgNzgXDZbJewFx/n+YAvMZrfg==",
"license": "MIT",
"optional": true,
"engines": {
@@ -6331,6 +6210,7 @@
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
"dev": true,
"license": "MIT",
"dependencies": {
"argparse": "^2.0.1"
@@ -6372,6 +6252,7 @@
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
"dev": true,
"license": "MIT",
"bin": {
"json5": "lib/cli.js"
@@ -6563,12 +6444,6 @@
"license": "MIT",
"peer": true
},
"node_modules/lodash.get": {
"version": "4.4.2",
"resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
"integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==",
"license": "MIT"
},
"node_modules/lodash.includes": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
@@ -6581,12 +6456,6 @@
"integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==",
"license": "MIT"
},
"node_modules/lodash.isequal": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
"integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==",
"license": "MIT"
},
"node_modules/lodash.isinteger": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
@@ -6611,12 +6480,6 @@
"integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==",
"license": "MIT"
},
"node_modules/lodash.mergewith": {
"version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz",
"integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==",
"license": "MIT"
},
"node_modules/lodash.once": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
@@ -7366,13 +7229,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/openapi-types": {
"version": "12.1.3",
"resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz",
"integrity": "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==",
"license": "MIT",
"peer": true
},
"node_modules/ora": {
"version": "5.4.1",
"resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz",
@@ -8960,116 +8816,6 @@
"node": ">=12.0.0"
}
},
"node_modules/swagger-autogen": {
"version": "2.23.7",
"resolved": "https://registry.npmjs.org/swagger-autogen/-/swagger-autogen-2.23.7.tgz",
"integrity": "sha512-vr7uRmuV0DCxWc0wokLJAwX3GwQFJ0jwN+AWk0hKxre2EZwusnkGSGdVFd82u7fQLgwSTnbWkxUL7HXuz5LTZQ==",
"license": "MIT",
"dependencies": {
"acorn": "^7.4.1",
"deepmerge": "^4.2.2",
"glob": "^7.1.7",
"json5": "^2.2.3"
}
},
"node_modules/swagger-autogen/node_modules/acorn": {
"version": "7.4.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
"integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
"license": "MIT",
"bin": {
"acorn": "bin/acorn"
},
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/swagger-jsdoc": {
"version": "6.2.8",
"resolved": "https://registry.npmjs.org/swagger-jsdoc/-/swagger-jsdoc-6.2.8.tgz",
"integrity": "sha512-VPvil1+JRpmJ55CgAtn8DIcpBs0bL5L3q5bVQvF4tAW/k/9JYSj7dCpaYCAv5rufe0vcCbBRQXGvzpkWjvLklQ==",
"license": "MIT",
"dependencies": {
"commander": "6.2.0",
"doctrine": "3.0.0",
"glob": "7.1.6",
"lodash.mergewith": "^4.6.2",
"swagger-parser": "^10.0.3",
"yaml": "2.0.0-1"
},
"bin": {
"swagger-jsdoc": "bin/swagger-jsdoc.js"
},
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/swagger-jsdoc/node_modules/commander": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-6.2.0.tgz",
"integrity": "sha512-zP4jEKbe8SHzKJYQmq8Y9gYjtO/POJLgIdKgV7B9qNmABVFVc+ctqSX6iXh4mCpJfRBOabiZ2YKPg8ciDw6C+Q==",
"license": "MIT",
"engines": {
"node": ">= 6"
}
},
"node_modules/swagger-jsdoc/node_modules/glob": {
"version": "7.1.6",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
"integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
"deprecated": "Glob versions prior to v9 are no longer supported",
"license": "ISC",
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.0.4",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
},
"engines": {
"node": "*"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/swagger-parser": {
"version": "10.0.3",
"resolved": "https://registry.npmjs.org/swagger-parser/-/swagger-parser-10.0.3.tgz",
"integrity": "sha512-nF7oMeL4KypldrQhac8RyHerJeGPD1p2xDh900GPvc+Nk7nWP6jX2FcC7WmkinMoAmoO774+AFXcWsW8gMWEIg==",
"license": "MIT",
"dependencies": {
"@apidevtools/swagger-parser": "10.0.3"
},
"engines": {
"node": ">=10"
}
},
"node_modules/swagger-ui-dist": {
"version": "5.18.2",
"resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.18.2.tgz",
"integrity": "sha512-J+y4mCw/zXh1FOj5wGJvnAajq6XgHOyywsa9yITmwxIlJbMqITq3gYRZHaeqLVH/eV/HOPphE6NjF+nbSNC5Zw==",
"license": "Apache-2.0",
"dependencies": {
"@scarf/scarf": "=1.4.0"
}
},
"node_modules/swagger-ui-express": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-5.0.1.tgz",
"integrity": "sha512-SrNU3RiBGTLLmFU8GIJdOdanJTl4TOmT27tt3bWWHppqYmAZ6IDuEuBvMU6nZq0zLEe6b/1rACXCgLZqO6ZfrA==",
"license": "MIT",
"dependencies": {
"swagger-ui-dist": ">=5.0.0"
},
"engines": {
"node": ">= v0.10.32"
},
"peerDependencies": {
"express": ">=4.0.0 || >=5.0.0-beta"
}
},
"node_modules/tar": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz",
@@ -9896,15 +9642,6 @@
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
"license": "ISC"
},
"node_modules/yaml": {
"version": "2.0.0-1",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.0.0-1.tgz",
"integrity": "sha512-W7h5dEhywMKenDJh2iX/LABkbFnBxasD27oyXWDS/feDsxiw0dD5ncXdYXgkvAsXIY2MpW/ZKkr9IU30DBdMNQ==",
"license": "ISC",
"engines": {
"node": ">= 6"
}
},
"node_modules/yargs": {
"version": "17.7.2",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
@@ -9968,36 +9705,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/z-schema": {
"version": "5.0.5",
"resolved": "https://registry.npmjs.org/z-schema/-/z-schema-5.0.5.tgz",
"integrity": "sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==",
"license": "MIT",
"dependencies": {
"lodash.get": "^4.4.2",
"lodash.isequal": "^4.5.0",
"validator": "^13.7.0"
},
"bin": {
"z-schema": "bin/z-schema"
},
"engines": {
"node": ">=8.0.0"
},
"optionalDependencies": {
"commander": "^9.4.1"
}
},
"node_modules/z-schema/node_modules/commander": {
"version": "9.5.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz",
"integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==",
"license": "MIT",
"optional": true,
"engines": {
"node": "^12.20.0 || >=14"
}
},
"node_modules/zip-stream": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.1.tgz",

View File

@@ -1,6 +1,6 @@
{
"name": "eventmaster",
"version": "0.3.0",
"version": "0.2.0",
"author": "Tobias Zoghaib",
"description": "Hackable ticket store for educational purposes",
"license": "MIT",
@@ -53,9 +53,6 @@
"sequelize": "^6.37.4",
"sequelize-typescript": "^2.1.6",
"sqlite3": "^5.1.7",
"swagger-autogen": "^2.23.7",
"swagger-jsdoc": "^6.2.8",
"swagger-ui-express": "^5.0.1",
"vue": "^3.4.29",
"vue-i18n": "^10.0.4",
"vue-router": "^4.4.5",
@@ -71,8 +68,6 @@
"@types/jsonwebtoken": "^9.0.7",
"@types/multer": "^1.4.12",
"@types/node": "^22.9.0",
"@types/swagger-jsdoc": "^6.0.4",
"@types/swagger-ui-express": "^4.1.7",
"@vitejs/plugin-vue": "^5.1.4",
"concurrently": "^9.0.1",
"copyfiles": "^2.4.1",

View File

@@ -27,7 +27,7 @@ export async function fetchAllAccounts(token: string) {
* @returns Response from server with token body
*/
export async function getLogin(username: string, password: string) {
return await axios.get(BASE_URL + "/login?username=" + username + "&password=" + password)
return await axios.get(BASE_URL + "/account/login?username=" + username + "&password=" + password)
}
@@ -39,7 +39,7 @@ export async function getLogin(username: string, password: string) {
* @returns Response from server with account body
*/
export async function getAccount(token: string) {
return await axios.get(BASE_URL + "/account", {
return await axios.get(BASE_URL + "/account/data", {
headers: {
"Authorization": token
}
@@ -77,17 +77,9 @@ export async function updateAccount(account: AccountModel, token: string) {
* Delete an account in servers database
*
* @param account Account to delete
* @param token Validation token
*
* @returns Response from server
*/
export async function deleteAccount(account: AccountModel, token: string) {
return await axios.delete(BASE_URL + "/account", {
headers: {
"Authorization": token
},
data: {
account: account
}
})
export async function deleteAccount(account: AccountModel) {
return await axios.delete(BASE_URL + "/account/" + account.id)
}

View File

@@ -4,19 +4,15 @@ import { OrderApiModel } from "../models/apiEndpoints/orderApiModel"
const BASE_URL = "http://localhost:3000/orders"
export async function fetchUserOrders(userId: number, token: string) {
return axios.get(BASE_URL + "?id=" + userId, {
headers: {
"Authorization": token
}
})
export async function fetchUserOrders(userId: number) {
return axios.get(BASE_URL + "/" + userId)
}
export async function createOrder(
accountId: number,
basketItem: Array<BasketItemModel>,
paymentId: number,
addressId: number,
token: string
addressId: number
) {
let tickets = []
@@ -31,13 +27,10 @@ export async function createOrder(
}
return axios.post(BASE_URL, {
accountId: accountId,
tickets: tickets,
paymentId: paymentId,
addressId: addressId,
}, {
headers: {
"Authorization": token
}
addressId: addressId
})
}

View File

@@ -11,7 +11,7 @@ const accountStore = useAccountStore()
const orderStore = useOrderStore()
const router = useRouter()
orderStore.getOrdersOfAccount(accountStore.userAccount, accountStore.userAccountToken)
orderStore.getOrdersOfAccount(accountStore.userAccount)
accountStore.refreshAccount()
</script>

View File

@@ -8,7 +8,7 @@ import { useOrderStore } from '@/stores/order.store';
const accountStore = useAccountStore()
const orderStore = useOrderStore()
orderStore.getOrdersOfAccount(accountStore.userAccount, accountStore.userAccountToken)
orderStore.getOrdersOfAccount(accountStore.userAccount)
</script>
<template>

View File

@@ -238,7 +238,7 @@ export const useAccountStore = defineStore("accountStore", {
async refreshOrders() {
this.fetchInProgress = true
await fetchUserOrders(this.userAccount.id, this.userAccountToken)
await fetchUserOrders(this.userAccount.id)
.then(result => {
this.orders = result.data
this.fetchInProgress = false
@@ -344,7 +344,7 @@ export const useAccountStore = defineStore("accountStore", {
async deleteAccount(account: AccountModel) {
this.fetchInProgress = true
deleteAccount(account, this.userAccountToken)
deleteAccount(account)
.then(response => {
this.fetchInProgress = false
})

View File

@@ -100,10 +100,10 @@ export const useBasketStore = defineStore('basketStore', {
const exerciseStore = useExerciseStore()
await createOrder(
accountStore.userAccount.id,
this.itemsInBasket,
this.usedPayment.id,
this.usedAddress.id,
accountStore.userAccountToken
this.usedAddress.id
)
.then(async result => {
if (result.status == 201) {

View File

@@ -95,5 +95,13 @@ export const useConcertStore = defineStore("concertStore", {
this.concert = new ConcertDetailsApiModel()
this.showEditDialog = true
},
editConcert(concert: ConcertModel) {
// todo
},
async deleteConcert(item: ConcertModel) {
// todo
}
}
})

View File

@@ -40,10 +40,10 @@ export const useOrderStore = defineStore("orderStore", {
*
* @param user User to request orders from
*/
async getOrdersOfAccount(user: AccountModel, token: string) {
async getOrdersOfAccount(user: AccountModel) {
this.fetchInProgress = true
fetchUserOrders(user.id, token)
fetchUserOrders(user.id)
.then(res => {
this.orders = res.data
this.fetchInProgress = false

View File

@@ -49,7 +49,7 @@ export const useSearchStore = defineStore("searchStore", {
// Check for exercise solution
if (this.searchTerm.toUpperCase().includes("SELECT")) {
if (result.data.length != 0) {
// Exercise 2.1
if (result.data[0].type != undefined && result.data[0].type == "table") {
exerciseStore.solveExercise(2, 1)
@@ -68,7 +68,7 @@ export const useSearchStore = defineStore("searchStore", {
}
// Exercise 2.4
else if (this.searchTerm.toUpperCase().includes("UPDATE")) {
else if (this.searchTerm.includes("UPDATE")) {
const accountStore = useAccountStore()
await accountStore.refreshAccount()
@@ -79,7 +79,7 @@ export const useSearchStore = defineStore("searchStore", {
}
// Exercise 2.6
else if (this.searchTerm.toUpperCase().includes("DELETE")) {
else if (this.searchTerm.includes("DELETE")) {
const bandStore = useBandStore()
await bandStore.getBand("muse")
@@ -92,7 +92,6 @@ export const useSearchStore = defineStore("searchStore", {
}
else {
// Normal behaviour
this.bands = result.data
}
})