70 Commits
main ... main

Author SHA1 Message Date
ea9f05b048 Added belongs_to to most of diesel structs for easier querying. 2026-01-06 22:06:55 +01:00
5c928b30f3 Implemented deleting games as admin. 2026-01-04 19:59:49 +01:00
0c256846f6 Added owning games and not willing to travel with them. 2025-12-30 21:18:16 +01:00
ff88029a4b Added Location and location ownership/rights to gamenight. 2025-12-24 14:48:54 +01:00
8a48119c80 Displays available owned games for a GameNight. 2025-07-13 14:29:34 +02:00
0e89bca126 Small rewrite to allow fetching owned game of users 2025-07-13 13:30:25 +02:00
ad0e81fdc3 Cleans up the code a bit 2025-07-12 20:01:57 +02:00
df1e3ff905 Adds own/disown game logic 2025-07-12 20:01:57 +02:00
3f99b68d62 Adds renaming games functionality 2025-07-12 17:07:33 +02:00
28f7306d57 Adds Adding of Games to the system. 2025-06-27 22:08:23 +02:00
3f51d52edf Refactored the connect flow and make canceling behavior more consistent. 2025-06-27 16:31:48 +02:00
f0883a0ff0 Fixes leave on the server and reworked login flow. 2025-06-27 14:45:36 +02:00
fbe456a0f5 Implements leaving on the client side 2025-06-24 16:49:21 +02:00
d11e31149b Implemented leaving a gamenight on the server/database side 2025-06-15 19:12:46 +02:00
db6f55bc47 Rewrite to chatty but functional API. 2025-06-14 22:16:13 +02:00
d1832bc794 Last cleanup 2025-06-03 19:50:43 +02:00
f1d23cb495 Returns participants for a gamenight 2025-05-31 22:37:51 +02:00
156be1821a Cleaned up api client sources 2025-05-31 12:03:58 +02:00
6950ac62e8 Autogenerate only the models of the API for the backend-server 2025-05-31 11:38:55 +02:00
597a960bf1 Splits database into a separate crate 2025-05-30 14:31:00 +02:00
Dennis Brentjes
3f7ed03973 Fixes some Clippy remarks. 2025-05-19 21:01:38 +02:00
c994321576 Started on separating domain and adapters 2025-05-14 07:41:28 +02:00
4e26d3cdcb Created a domain module to decouple flows from the core. 2025-05-03 22:49:01 +02:00
fce0ebd76b Deleted the old Rust backend based on rocket. 2025-05-02 23:06:38 +02:00
6699dcf392 Added some more gamenight-cli Flows. 2025-05-02 23:03:57 +02:00
db25dc0aed Started working on a cli frontend. 2025-04-23 20:27:06 +02:00
02913c7b52 upgrades packages. 2025-03-30 22:54:21 +02:00
Dennis Brentjes
9e84a62c41 Added Avanlonia frontend. 2023-08-15 11:31:58 +02:00
22f05c00c1 Added a client (From generator) and a basic login page to test it. 2023-04-30 20:51:42 +02:00
b2aba31264 Added initial Uno Platform frontend project. 2023-04-30 17:23:00 +02:00
Dennis Brentjes
1296f363af Re-added auto migrations 2023-03-30 17:08:44 +02:00
Dennis Brentjes
70ae15f655 Fixes the ugly Register User post handler. 2023-03-30 09:32:24 +02:00
3509a70a6a Abstracted away getting a PgConnection with expect(). 2023-03-26 11:25:15 +02:00
217e5ee64b Adds get for a single gamenight. 2023-03-25 23:32:41 +01:00
5216f55a14 Adds the post gamenight handler. 2023-03-25 22:44:58 +01:00
534e6867d8 Adds user authorization to the actix backend. 2023-03-25 19:20:38 +01:00
1c8110cdb0 Added Login and Register handlers for actix backend 2023-03-24 22:28:18 +01:00
d961896242 Started reimplementation of the Rest api in actix-web 2023-03-17 22:20:26 +01:00
7741c1dbae Merge pull request 'Adds an AdminPanel with currently active registration tokens.' (#9) from admin-panel into main
Reviewed-on: Roflin/gamenight#9
2022-06-05 16:15:50 +02:00
65d2dece55 Adds an AdminPanel with currently active registration tokens. 2022-06-04 21:57:54 +02:00
34737bfb6b Updates all libraries and some cleanup in the Rust part. 2022-06-04 13:10:09 +02:00
5ace39d820 Merge pull request 'join_gamenight' (#8) from join_gamenight into main
Reviewed-on: Roflin/gamenight#8
2022-06-03 19:47:02 +02:00
b7f981e3a6 Adds the ability to join or leave a gamenight. 2022-05-31 21:27:35 +02:00
f7f9f7456b Adds a Apihelper to cleanup the react components. 2022-05-31 19:56:44 +02:00
cfaa6ebdb1 Merge pull request 'Adds some basic styling' (#7) from some-styling-work into main
Reviewed-on: Roflin/gamenight#7
2022-05-30 21:35:58 +02:00
8a318e877f Merge pull request 'gamenight-participants' (#6) from gamenight-participants into main
Reviewed-on: Roflin/gamenight#6
2022-05-30 21:32:47 +02:00
bcfcf66df5 Merge pull request 'Adds the ability to add games with suggestions from known games.' (#3) from game-adding-to-gamenight into main
Reviewed-on: Roflin/gamenight#3
2022-05-30 21:31:01 +02:00
83a0b5ad9d Adds some basic styling 2022-05-29 18:26:08 +02:00
9de8ffaa2d Some Cleaup. 2022-05-29 10:46:05 +02:00
102a3e6082 Formatting commit 2022-05-29 10:33:55 +02:00
639405bf9f Adds a details page for a single gamenight. 2022-05-29 10:33:19 +02:00
2ba2026e21 Gamenights also return their game list. 2022-05-29 10:33:19 +02:00
86cdbedd41 Adds the participants part of the API. 2022-05-29 10:33:19 +02:00
836a4ab59f Formatting commit. 2022-05-29 10:33:19 +02:00
5c27be0191 Schema rewrite to split up the schema.rs file. 2022-05-29 10:33:19 +02:00
1a6ead4760 Adds the ability to add games with suggestions from known games. 2022-05-29 10:32:20 +02:00
5ffeea6553 Merge pull request 'initial-frontend-work' (#2) from initial-frontend-work into main
Reviewed-on: Roflin/gamenight#2
2022-05-27 20:30:06 +02:00
cc26aed9a5 Reworked the database code to make use of the ? operator. 2022-05-14 23:44:40 +02:00
92e0257e74 Added gamenight owners and some ui and api functions to delete gamenights. 2022-05-14 23:36:35 +02:00
0a214ca388 Fixes the infinite loop and refactores some statechanges into useEffect hooks 2022-05-01 17:51:28 +02:00
2cfaf2b4cc Adds an add gamenight control and fixes the fetch gamenight Effect,
Introduces an infinite fetch gamenights loop
2022-04-29 22:40:10 +02:00
bf796201bf move to function based react components 2022-04-29 20:27:54 +02:00
56d0889963 A start on a frontend application in React. 2022-04-23 23:30:26 +02:00
d80f705b5d Merge pull request 'Added a user system with no proper user validation but working authorisation.' (#1) from user-system into main
Reviewed-on: Roflin/gamenight#1
2022-04-23 13:17:28 +02:00
aab60dcc11 Fixes 3 potential panics when querying the user and pwd table. 2022-04-21 21:51:57 +02:00
b5e9420c1f Makes seperate function for authorized and unauthorized request. 2022-04-21 21:35:14 +02:00
5f73d556c6 Fixes the review comments 2022-04-21 20:02:15 +02:00
81e65b1619 Ran rust fmt. 2022-04-21 19:12:16 +02:00
df8b553345 Adds some validation to new user registration. 2022-04-21 18:51:13 +02:00
af0dcee159 Added a user system with no proper user validation but working authorisation. 2022-04-20 22:28:00 +02:00
165 changed files with 12590 additions and 2315 deletions

7
.gitignore vendored
View File

@@ -1,4 +1,5 @@
/target
/target/
**/*.rs.bk
Cargo.lock
.vscode
Rocket.toml
*.sqlite
**/.idea

2019
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,23 +0,0 @@
[package]
name = "gamenight"
version = "0.1.0"
authors = ["Dennis Brentjes <d.brentjes@gmail.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
rocket = { version = "0.5.0-rc.1", features = ["default", "json"] }
libsqlite3-sys = { version = ">=0.8.0, <0.19.0", features = ["bundled"] }
rocket_sync_db_pools = { version = "0.1.0-rc.1", features = ["diesel_sqlite_pool"] }
diesel = { version = "1.4.8", features = ["sqlite"] }
diesel_migrations = "1.4.0"
rocket_dyn_templates = { version = "0.1.0-rc.1", features = ["handlebars"] }
chrono = "0.4.19"
serde = "1.0.136"
[profile.dev]
overflow-checks = true
[profile.release]
overflow-checks = true

View File

@@ -1,4 +0,0 @@
#Copy this file over to Rocket.toml after changing all relevant values.
[global.databases]
gamenight_database = { url = "gamenight.sqlite" }

3
backend-actix/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
target
src/models/
docs/

2894
backend-actix/Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

19
backend-actix/Cargo.toml Normal file
View File

@@ -0,0 +1,19 @@
[package]
name = "backend-actix"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
gamenight-database = { path = "../gamenight-database"}
actix-web = "4"
actix-cors = "0.7"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
uuid = { version = "1.3.0", features = ["serde", "v4"] }
chrono = { version = "0.4", features = ["serde"] }
jsonwebtoken = { version = "10.2.0", features = ["rust_crypto"] }
validator = { version = "0.20", features = ["derive"] }
env_logger = "0.11"
tracing-actix-web = "0.7"

43
backend-actix/build.rs Normal file
View File

@@ -0,0 +1,43 @@
use std::{
fs::{exists, read_dir, remove_dir_all, File},
io::Write,
process::Command,
};
use std::fs::create_dir;
fn main() {
println!("cargo::rerun-if-changed=gamenight-api.yaml");
if exists("src/models").unwrap() {
remove_dir_all("src/models").unwrap();
}
create_dir("src/models/").unwrap();
let _ = Command::new("openapi-generator")
.args([
"generate",
"-i",
"gamenight-api.yaml",
"-g",
"rust",
"--global-property",
"models",
])
.output()
.expect("Failed to generate models sources for the gamenight API");
let mut file = File::create("./src/models/mod.rs").unwrap();
let paths = read_dir("./src/models").unwrap();
for path in paths {
let path = path.unwrap();
let path = path.path();
let stem = path.file_stem().unwrap();
if stem == "mod" {
continue;
}
let line = format!("pub mod {};\n", stem.to_str().unwrap());
let _ = file.write(line.as_bytes()).unwrap();
}
}

View File

@@ -0,0 +1,750 @@
openapi: 3.0.0
info:
title: Gamenight
version: '1.0'
contact:
name: Dennis Brentjes
email: dennis@brentj.es
url: 'https://brentj.es'
description: Api specification for a Gamenight server
license:
name: MIT
servers:
- url: 'http://localhost:8080'
description: Gamenight
paths:
/token:
get:
summary: ''
operationId: get-token
responses:
'200':
$ref: '#/components/responses/TokenResponse'
'401':
$ref: '#/components/responses/FailureResponse'
requestBody:
$ref: '#/components/requestBodies/LoginRequest'
description: Submit your credentials to get a JWT-token to use with the rest of the api.
parameters: []
post:
summary: ''
operationId: post-token
responses:
'200':
$ref: '#/components/responses/TokenResponse'
'401':
$ref: '#/components/responses/FailureResponse'
description: Refresh your JWT-token without logging in again.
parameters: []
security:
- JWT-Auth: []
/users:
get:
responses:
'200':
$ref: '#/components/responses/UsersResponse'
'400':
$ref: '#/components/responses/FailureResponse'
'401':
$ref: '#/components/responses/FailureResponse'
security:
- JWT-Auth: []
/user:
post:
summary: ''
operationId: post-register
requestBody:
$ref: '#/components/requestBodies/RegisterRequest'
responses:
'200':
description: ''
'422':
$ref: '#/components/responses/FailureResponse'
description: 'Create a new user given a registration token and user information, username and email must be unique, and password and password_repeat must match.'
parameters: []
security:
- JWT-Auth: []
get:
description: 'Get a user from primary id'
requestBody:
$ref: '#/components/requestBodies/GetUserRequest'
parameters: []
responses:
'200':
$ref: '#/components/responses/UserResponse'
'401':
$ref: '#/components/responses/FailureResponse'
'404':
$ref: '#/components/responses/FailureResponse'
'422':
$ref: '#/components/responses/FailureResponse'
security:
- JWT-Auth: []
/gamenights:
get:
summary: Get a all gamenights
responses:
'200':
$ref: '#/components/responses/GamenightsResponse'
'400':
$ref: '#/components/responses/FailureResponse'
'401':
$ref: '#/components/responses/FailureResponse'
operationId: get-gamenights
security:
- JWT-Auth: []
description: Retrieve the list of gamenights on this gamenight server. Requires authorization.
/participants:
get:
summary: Get all participants for a gamenight
responses:
'200':
$ref: '#/components/responses/ParticipantsResponse'
'400':
$ref: '#/components/responses/FailureResponse'
'401':
$ref: '#/components/responses/FailureResponse'
requestBody:
$ref: '#/components/requestBodies/GetParticipants'
security:
- JWT-Auth: []
description: Retrieve the participants of a single gamenight by id.
/gamenight:
post:
summary: ''
operationId: post-gamenight
responses:
'200':
description: OK
'401':
$ref: '#/components/responses/FailureResponse'
'422':
$ref: '#/components/responses/FailureResponse'
security:
- JWT-Auth: []
requestBody:
$ref: '#/components/requestBodies/AddGamenight'
description: 'Add a gamenight by providing a name and a date, only available when providing an JWT token.'
get:
summary: ''
operationId: get-gamenight
responses:
'200':
$ref: '#/components/responses/GamenightResponse'
'401':
$ref: '#/components/responses/FailureResponse'
'422':
$ref: '#/components/responses/FailureResponse'
requestBody:
$ref: '#/components/requestBodies/GetGamenight'
security:
- JWT-Auth: []
/join:
post:
responses:
'200':
description: OK
'401':
$ref: '#/components/responses/FailureResponse'
'422':
$ref: '#/components/responses/FailureResponse'
requestBody:
$ref: '#/components/requestBodies/JoinGamenight'
security:
- JWT-Auth: []
/leave:
post:
responses:
'200':
description: "OK"
'401':
$ref: '#/components/responses/FailureResponse'
'422':
$ref: '#/components/responses/FailureResponse'
requestBody:
$ref: '#/components/requestBodies/LeaveGamenight'
security:
- JWT-Auth: []
/games:
get:
responses:
'200':
$ref: '#/components/responses/GamesResponse'
'401':
$ref: '#/components/responses/FailureResponse'
'422':
$ref: '#/components/responses/FailureResponse'
security:
- JWT-Auth: []
/game:
get:
responses:
'200':
$ref: '#/components/responses/GameResponse'
'401':
$ref: '#/components/responses/FailureResponse'
'422':
$ref: '#/components/responses/FailureResponse'
requestBody:
$ref: '#/components/requestBodies/GetGameRequest'
security:
- JWT-Auth: []
post:
responses:
'200':
$ref: '#/components/responses/GameIdResponse'
'401':
$ref: '#/components/responses/FailureResponse'
'422':
$ref: '#/components/responses/FailureResponse'
requestBody:
$ref: '#/components/requestBodies/AddGameRequest'
security:
- JWT-Auth: []
delete:
responses:
'200':
description: "Ok"
'401':
$ref: '#/components/responses/FailureResponse'
'422':
$ref: '#/components/responses/FailureResponse'
requestBody:
$ref: '#/components/requestBodies/RemoveGameRequest'
security:
- JWT-Auth: [ ]
/rename_game:
post:
responses:
'200':
description: "OK"
'401':
$ref: '#/components/responses/FailureResponse'
'422':
$ref: '#/components/responses/FailureResponse'
requestBody:
$ref: '#/components/requestBodies/RenameGameRequest'
security:
- JWT-Auth: []
/own:
post:
responses:
'200':
description: "OK"
'401':
$ref: '#/components/responses/FailureResponse'
'422':
$ref: '#/components/responses/FailureResponse'
requestBody:
$ref: '#/components/requestBodies/OwnGameRequest'
security:
- JWT-Auth: []
/disown:
post:
responses:
'200':
description: "OK"
'401':
$ref: '#/components/responses/FailureResponse'
'422':
$ref: '#/components/responses/FailureResponse'
requestBody:
$ref: '#/components/requestBodies/DisownGameRequest'
security:
- JWT-Auth: []
/owned_games:
get:
responses:
'200':
$ref: "#/components/responses/GameIdsResponse"
'401':
$ref: '#/components/responses/FailureResponse'
'422':
$ref: '#/components/responses/FailureResponse'
requestBody:
$ref: '#/components/requestBodies/OwnedGamesRequest'
security:
- JWT-Auth: []
/location:
get:
responses:
'200':
$ref: '#/components/responses/LocationResponse'
'401':
$ref: '#/components/responses/FailureResponse'
'422':
$ref: '#/components/responses/FailureResponse'
requestBody:
$ref: '#/components/requestBodies/GetLocationRequest'
security:
- JWT-Auth: []
post:
responses:
'200':
$ref: '#/components/responses/LocationIdResponse'
'401':
$ref: '#/components/responses/FailureResponse'
'422':
$ref: '#/components/responses/FailureResponse'
requestBody:
$ref: '#/components/requestBodies/AddLocationRequest'
security:
- JWT-Auth: []
/locations:
get:
responses:
'200':
$ref: '#/components/responses/LocationsResponse'
'401':
$ref: '#/components/responses/FailureResponse'
'422':
$ref: '#/components/responses/FailureResponse'
security:
- JWT-Auth: []
/location_authorize:
post:
responses:
'200':
description: 'Ok'
'401':
$ref: '#/components/responses/FailureResponse'
'422':
$ref: '#/components/responses/FailureResponse'
requestBody:
$ref: '#/components/requestBodies/AuthorizeLocationRequest'
security:
- JWT-Auth: []
/authorized_location_user_ids:
get:
responses:
'200':
$ref: "#/components/responses/UserIdsResponse"
'401':
$ref: '#/components/responses/FailureResponse'
'422':
$ref: '#/components/responses/FailureResponse'
requestBody:
$ref: '#/components/requestBodies/AuthorizedLocationUserIdsRequest'
security:
- JWT-Auth: []
components:
schemas:
Gamenight:
title: Gamenight
type: object
properties:
id:
type: string
name:
type: string
datetime:
type: string
owner_id:
type: string
required:
- id
- name
- datetime
- owner_id
Participants:
title: participants
type: object
properties:
participants:
type: array
items:
type: string
required:
- participants
Failure:
title: Failure
type: object
properties:
message:
type: string
description: 'Failure Reason'
Token:
title: Token
type: object
properties:
jwt_token:
type: string
Login:
title: Login
type: object
properties:
username:
type: string
password:
type: string
required:
- username
- password
Registration:
title: Registration
type: object
properties:
username:
type: string
email:
type: string
password:
type: string
password_repeat:
type: string
registration_token:
type: string
required:
- username
- email
- password
- password_repeat
- registration_token
UserId:
title: UserId
type: object
properties:
user_id:
type: string
required:
- user_id
LocationId:
title: LocationId
type: object
properties:
location_id:
type: string
required:
- location_id
AddGamenightRequestBody:
title: AddGamenightRequestBody
type: object
properties:
name:
type: string
datetime:
type: string
required:
- name
- datetime
GamenightId:
title: GamenightId
type: object
properties:
gamenight_id:
type: string
required:
- gamenight_id
GameId:
title: GameId
type: object
properties:
game_id:
type: string
required:
- game_id
GetGamenightRequestBody:
type: object
properties:
id:
type: string
User:
type: object
properties:
id:
type: string
username:
type: string
email:
type: string
required:
- id
- username
Game:
type: object
properties:
id:
type: string
name:
type: string
required:
- id
- name
AddGameRequestBody:
type: object
properties:
name:
type: string
required:
- name
RenameGameRequestBody:
type: object
properties:
id:
type: string
name:
type: string
required:
- id
- name
OwnGameRequestBody:
type: object
properties:
game_id:
type: string
location_id:
type: string
required:
- game_id
GameIdsResponse:
type: array
items:
type: string
UserIdsResponse:
type: array
items:
$ref: "#/components/schemas/UserId"
AddLocationRequestBody:
type: object
properties:
name:
type: string
address:
type: string
note:
type: string
required:
- name
Location:
type: object
properties:
id:
type: string
name:
type: string
address:
type: string
note:
type: string
required:
- id
- name
AuthorizeLocationRequestBody:
type: object
properties:
location_id:
type: string
user_id:
type: string
op:
type: string
enum: [grant, revoke]
required:
- location_id
- user_id
- op
requestBodies:
LoginRequest:
content:
application/json:
schema:
$ref: '#/components/schemas/Login'
RegisterRequest:
content:
application/json:
schema:
$ref: '#/components/schemas/Registration'
GetUserRequest:
content:
application/json:
schema:
$ref: '#/components/schemas/UserId'
AddGamenight:
content:
application/json:
schema:
$ref: '#/components/schemas/AddGamenightRequestBody'
GetGamenight:
content:
application/json:
schema:
$ref: '#/components/schemas/GetGamenightRequestBody'
GetParticipants:
content:
application/json:
schema:
$ref: '#/components/schemas/GamenightId'
JoinGamenight:
content:
application/json:
schema:
$ref: '#/components/schemas/GamenightId'
LeaveGamenight:
content:
application/json:
schema:
$ref: '#/components/schemas/GamenightId'
GetGameRequest:
content:
application/json:
schema:
$ref: '#/components/schemas/GameId'
AddGameRequest:
content:
application/json:
schema:
$ref: '#/components/schemas/AddGameRequestBody'
RenameGameRequest:
content:
application/json:
schema:
$ref: '#/components/schemas/RenameGameRequestBody'
RemoveGameRequest:
content:
application/json:
schema:
$ref: '#/components/schemas/GameId'
OwnGameRequest:
content:
application/json:
schema:
$ref: '#/components/schemas/OwnGameRequestBody'
DisownGameRequest:
content:
application/json:
schema:
$ref: '#/components/schemas/GameId'
OwnedGamesRequest:
content:
application/json:
schema:
$ref: '#/components/schemas/UserId'
GetLocationRequest:
content:
application/json:
schema:
$ref: '#/components/schemas/LocationId'
AddLocationRequest:
content:
application/json:
schema:
$ref: '#/components/schemas/AddLocationRequestBody'
AuthorizeLocationRequest:
content:
application/json:
schema:
$ref: '#/components/schemas/AuthorizeLocationRequestBody'
AuthorizedLocationUserIdsRequest:
content:
application/json:
schema:
$ref: '#/components/schemas/LocationId'
responses:
TokenResponse:
description: Example response
content:
application/json:
schema:
$ref: '#/components/schemas/Token'
FailureResponse:
description: Example response
content:
application/json:
schema:
$ref: '#/components/schemas/Failure'
ParticipantsResponse:
description: Response with a list of participant uuids
content:
application/json:
schema:
$ref: '#/components/schemas/Participants'
GamenightsResponse:
description: Example response
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Gamenight'
UsersResponse:
description: List of all Users
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/User'
GamenightResponse:
description: A gamenight being hosted
content:
application/json:
schema:
$ref: '#/components/schemas/Gamenight'
UserResponse:
description: A user in the gamenight system
content:
application/json:
schema:
$ref: '#/components/schemas/User'
GamesResponse:
description: A list of Games in this gamenight instance.
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Game'
GameResponse:
description: A game.
content:
application/json:
schema:
$ref: '#/components/schemas/Game'
GameIdResponse:
description: a game id
content:
application/json:
schema:
$ref: '#/components/schemas/GameId'
GameIdsResponse:
description: A list of game ids.
content:
application/json:
schema:
$ref: '#/components/schemas/GameIdsResponse'
UserIdsResponse:
description: A list of user ids.
content:
application/json:
schema:
$ref: '#/components/schemas/UserIdsResponse'
LocationResponse:
description: A location
content:
application/json:
schema:
$ref: '#/components/schemas/Location'
LocationIdResponse:
description: A location Id
content:
application/json:
schema:
$ref: '#/components/schemas/LocationId'
LocationsResponse:
description: A list of all LocationsResponse
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Location'
securitySchemes:
JWT-Auth:
type: http
scheme: bearer
bearerFormat: JWT
description: ''

68
backend-actix/src/main.rs Normal file
View File

@@ -0,0 +1,68 @@
#[allow(unused_imports)]
pub mod models;
pub mod request;
use actix_cors::Cors;
use actix_web::http;
use actix_web::middleware::Logger;
use actix_web::web;
use actix_web::App;
use actix_web::HttpServer;
use gamenight_database::*;
use request::{gamenights, login, register, *};
use tracing_actix_web::TracingLogger;
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let url = "postgres://root:root@127.0.0.1/gamenight";
let pool = get_connection_pool(url);
let mut conn = pool.get_conn();
run_migration(&mut conn);
env_logger::init_from_env(env_logger::Env::new().default_filter_or("debug"));
HttpServer::new(move || {
let cors = Cors::default()
.allowed_origin("0.0.0.0")
.allowed_origin_fn(|_origin, _req_head| true)
.allowed_methods(vec!["GET", "POST"])
.allowed_headers(vec![http::header::AUTHORIZATION, http::header::ACCEPT])
.allowed_header(http::header::CONTENT_TYPE)
.max_age(3600);
App::new()
.wrap(cors)
.wrap(Logger::default())
.wrap(TracingLogger::default())
.app_data(web::Data::new(pool.clone()))
.service(login)
.service(refresh)
.service(register)
.service(gamenights)
.service(gamenight_post)
.service(gamenight_get)
.service(get_user)
.service(get_users)
.service(get_user_unauthenticated)
.service(post_join_gamenight)
.service(post_leave_gamenight)
.service(get_get_participants)
.service(get_games)
.service(get_game)
.service(post_game)
.service(post_rename_game)
.service(post_own_game)
.service(post_disown_game)
.service(get_owned_games)
.service(delete_game)
.service(get_locations)
.service(post_location)
.service(post_location_authorize)
.service(get_authorized_location_user_ids)
})
.bind(("::1", 8080))?
.run()
.await
}

View File

@@ -0,0 +1,81 @@
use std::future::{ready, Ready};
use actix_web::{dev::Payload, http, web::Data, FromRequest, HttpRequest};
use chrono::Utc;
use jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, Validation};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use gamenight_database::{
user::{get_user, User},
DbPool,
};
use super::error::ApiError;
#[derive(Debug, Serialize, Deserialize)]
pub struct Claims {
exp: i64,
uid: Uuid,
}
pub struct AuthUser(pub User);
impl From<User> for AuthUser {
fn from(value: User) -> Self {
Self(value)
}
}
fn get_claims(req: &HttpRequest) -> Result<Claims, ApiError> {
let token = req
.headers()
.get(http::header::AUTHORIZATION)
.map(|h| h.to_str().unwrap().split_at(7).1.to_string());
let token = token.ok_or(ApiError {
status: 400,
message: "JWT-token was not specified in the Authorization header as Bearer: token"
.to_string(),
})?;
let secret = "secret";
Ok(decode::<Claims>(
token.as_str(),
&DecodingKey::from_secret(secret.as_bytes()),
&Validation::default(),
)?
.claims)
}
pub fn get_token(user: &User) -> Result<String, ApiError> {
let claims = Claims {
exp: Utc::now().timestamp() + chrono::Duration::days(7).num_seconds(),
uid: user.id,
};
let secret = "secret";
Ok(encode(
&Header::default(),
&claims,
&EncodingKey::from_secret(secret.as_bytes()),
)?)
}
impl FromRequest for AuthUser {
type Error = ApiError;
type Future = Ready<Result<Self, Self::Error>>;
fn from_request(req: &HttpRequest, _payload: &mut Payload) -> Self::Future {
ready((|| -> Result<AuthUser, ApiError> {
let pool = req
.app_data::<Data<DbPool>>()
.expect("No database configured");
let mut conn = pool.get().expect("couldn't get db connection from pool");
let uid = get_claims(req)?.uid;
let user = get_user(&mut conn, uid)?;
Ok(user.into())
})())
}
}

View File

@@ -0,0 +1,95 @@
use actix_web::{
error::BlockingError,
http::{header::ContentType, StatusCode},
HttpResponse, ResponseError,
};
use serde::{Deserialize, Serialize};
use std::fmt::{Display, Formatter, Result};
use validator::ValidationErrors;
use gamenight_database::error::DatabaseError;
#[derive(Serialize, Deserialize, Debug)]
pub struct ApiError {
#[serde(skip_serializing)]
pub status: u16,
pub message: String,
}
impl Display for ApiError {
fn fmt(&self, f: &mut Formatter) -> Result {
write!(f, "{}", self.message)
}
}
impl ResponseError for ApiError {
fn error_response(&self) -> HttpResponse {
HttpResponse::build(StatusCode::from_u16(self.status).unwrap())
.content_type(ContentType::json())
.body(serde_json::to_string(&self).unwrap())
}
}
impl From<DatabaseError> for ApiError {
fn from(value: DatabaseError) -> Self {
ApiError {
//Todo, split this in unrecoverable and schema error
status: 500,
message: value.0,
}
}
}
impl From<BlockingError> for ApiError {
fn from(value: BlockingError) -> Self {
ApiError {
status: 500,
message: value.to_string(),
}
}
}
impl From<serde_json::Error> for ApiError {
fn from(value: serde_json::Error) -> Self {
ApiError {
status: 500,
message: value.to_string(),
}
}
}
impl From<jsonwebtoken::errors::Error> for ApiError {
fn from(value: jsonwebtoken::errors::Error) -> Self {
ApiError {
status: 500,
message: value.to_string(),
}
}
}
impl From<ValidationErrors> for ApiError {
fn from(value: ValidationErrors) -> Self {
ApiError {
status: 422,
message: value.to_string(),
}
}
}
impl From<chrono::ParseError> for ApiError {
fn from(value: chrono::ParseError) -> Self {
ApiError {
status: 422,
message: value.to_string(),
}
}
}
impl From<uuid::Error> for ApiError {
fn from(value: uuid::Error) -> Self {
ApiError {
status: 422,
message: value.to_string(),
}
}
}

View File

@@ -0,0 +1,170 @@
use crate::game::rename_game;
use crate::owned_game::own_game;
use crate::owned_game::owned_games;
use crate::owned_game::disown_game;
use crate::owned_game::OwnedGame;
use gamenight_database::game::load_game;
use crate::game::insert_game;
use uuid::Uuid;
use crate::game::remove_game;
use actix_web::{delete, get, http::header::ContentType, post, web, HttpResponse, Responder};
use gamenight_database::{
user::Role,
DbPool, GetConnection,
};
use crate::{
models::{
add_game_request_body::AddGameRequestBody, game::Game, game_id::GameId,
rename_game_request_body::RenameGameRequestBody, own_game_request_body::OwnGameRequestBody
},
request::{authorization::AuthUser, error::ApiError},
};
#[get("/games")]
pub async fn get_games(
pool: web::Data<DbPool>,
_user: AuthUser,
) -> Result<impl Responder, ApiError> {
let mut conn = pool.get_conn();
let games: Vec<gamenight_database::game::Game> = gamenight_database::games(&mut conn)?;
let model: Vec<Game> = games
.iter()
.map(|x| Game {
id: x.id.to_string(),
name: x.name.clone(),
})
.collect();
Ok(HttpResponse::Ok()
.content_type(ContentType::json())
.body(serde_json::to_string(&model)?))
}
impl From<AddGameRequestBody> for gamenight_database::game::Game {
fn from(value: AddGameRequestBody) -> Self {
Self {
id: Uuid::new_v4(),
name: value.name,
}
}
}
#[get("/game")]
pub async fn get_game(
pool: web::Data<DbPool>,
_user: AuthUser,
game_id: web::Json<GameId>,
) -> Result<impl Responder, ApiError> {
let mut conn = pool.get_conn();
let db_game = load_game(&mut conn, Uuid::parse_str(&game_id.0.game_id)?)?;
let model = Game {
id: db_game.id.to_string(),
name: db_game.name,
};
Ok(HttpResponse::Ok()
.content_type(ContentType::json())
.body(serde_json::to_string(&model)?))
}
#[post("/game")]
pub async fn post_game(
pool: web::Data<DbPool>,
_user: AuthUser,
game_data: web::Json<AddGameRequestBody>,
) -> Result<impl Responder, ApiError> {
let mut conn = pool.get_conn();
let game = game_data.0.into();
insert_game(&mut conn, &game)?;
Ok(HttpResponse::Ok()
.content_type(ContentType::json())
.body(serde_json::to_string(&GameId{game_id: game.id.to_string()})?))
}
#[delete("/game")]
pub async fn delete_game(
pool: web::Data<DbPool>,
user: AuthUser,
game_id: web::Json<GameId>,
) -> Result<impl Responder, ApiError> {
if user.0.role != Role::Admin {
Ok(HttpResponse::Unauthorized())
} else {
let mut conn = pool.get_conn();
remove_game(&mut conn, Uuid::parse_str(&game_id.0.game_id)?)?;
Ok(HttpResponse::Ok())
}
}
#[post("/rename_game")]
pub async fn post_rename_game(
pool: web::Data<DbPool>,
_user: AuthUser,
game_data: web::Json<RenameGameRequestBody>,
) -> Result<impl Responder, ApiError> {
let mut conn = pool.get_conn();
rename_game(
&mut conn,
Uuid::parse_str(&game_data.0.id)?,
game_data.0.name,
)?;
Ok(HttpResponse::Ok())
}
#[post("/own")]
pub async fn post_own_game(
pool: web::Data<DbPool>,
user: AuthUser,
own_data: web::Json<OwnGameRequestBody>,
) -> Result<impl Responder, ApiError> {
let mut conn = pool.get_conn();
own_game(
&mut conn,
OwnedGame {
user_id: user.0.id,
game_id: Uuid::parse_str(&own_data.game_id)?,
location_id: own_data.location_id.clone().map(|x| Uuid::parse_str(&x).unwrap()),
},
)?;
Ok(HttpResponse::Ok())
}
#[post("/disown")]
pub async fn post_disown_game(
pool: web::Data<DbPool>,
user: AuthUser,
game_id: web::Json<GameId>,
) -> Result<impl Responder, ApiError> {
let mut conn = pool.get_conn();
disown_game(
&mut conn,
OwnedGame {
user_id: user.0.id,
game_id: Uuid::parse_str(&game_id.0.game_id)?,
location_id: None
},
)?;
Ok(HttpResponse::Ok())
}
#[get("/owned_games")]
pub async fn get_owned_games(
pool: web::Data<DbPool>,
user: AuthUser,
) -> Result<impl Responder, ApiError> {
let mut conn = pool.get_conn();
let game_ids = owned_games(&mut conn, user.0.id)?;
let model: Vec<String> = game_ids.iter().map(|x| x.to_string()).collect();
Ok(HttpResponse::Ok()
.content_type(ContentType::json())
.body(serde_json::to_string(&model)?))
}

View File

@@ -0,0 +1,87 @@
use actix_web::{get, http::header::ContentType, post, web, HttpResponse, Responder};
use chrono::{DateTime, ParseError};
use uuid::Uuid;
use gamenight_database::{gamenight, DbPool, GetConnection};
use crate::request::error::ApiError;
use crate::{
models::{
add_gamenight_request_body::AddGamenightRequestBody, gamenight::Gamenight,
get_gamenight_request_body::GetGamenightRequestBody,
},
request::authorization::AuthUser,
};
impl AddGamenightRequestBody {
pub fn into_with_user(&self, user: AuthUser) -> Result<gamenight::Gamenight, ParseError> {
Ok(gamenight::Gamenight {
datetime: DateTime::parse_from_rfc3339(&self.datetime)?.with_timezone(&chrono::Utc),
id: Uuid::new_v4(),
name: self.name.clone(),
owner_id: user.0.id,
location_id: None,
})
}
}
impl From<GetGamenightRequestBody> for Uuid {
fn from(value: GetGamenightRequestBody) -> Self {
Uuid::parse_str(value.id.unwrap().as_str()).unwrap()
}
}
#[get("/gamenights")]
pub async fn gamenights(
pool: web::Data<DbPool>,
_user: AuthUser,
) -> Result<impl Responder, ApiError> {
let mut conn = pool.get_conn();
let gamenights: Vec<gamenight::Gamenight> = gamenight_database::gamenights(&mut conn)?;
let model: Vec<Gamenight> = gamenights
.iter()
.map(|x| Gamenight {
id: x.id.to_string(),
name: x.name.clone(),
datetime: x.datetime.to_rfc3339(),
owner_id: x.owner_id.to_string(),
})
.collect();
Ok(HttpResponse::Ok()
.content_type(ContentType::json())
.body(serde_json::to_string(&model)?))
}
#[post("/gamenight")]
pub async fn gamenight_post(
pool: web::Data<DbPool>,
user: AuthUser,
gamenight_data: web::Json<AddGamenightRequestBody>,
) -> Result<impl Responder, ApiError> {
let mut conn = pool.get_conn();
gamenight::add_gamenight(&mut conn, gamenight_data.into_with_user(user)?)?;
Ok(HttpResponse::Ok())
}
#[get("/gamenight")]
pub async fn gamenight_get(
pool: web::Data<DbPool>,
_user: AuthUser,
gamenight_data: web::Json<GetGamenightRequestBody>,
) -> Result<impl Responder, ApiError> {
let mut conn = pool.get_conn();
let gamenight = gamenight::get_gamenight(&mut conn, gamenight_data.into_inner().into())?;
let model = Gamenight {
id: gamenight.id.to_string(),
datetime: gamenight.datetime.to_rfc3339(),
name: gamenight.name,
owner_id: gamenight.owner_id.to_string(),
};
Ok(HttpResponse::Ok()
.content_type(ContentType::json())
.body(serde_json::to_string(&model)?))
}

View File

@@ -0,0 +1,55 @@
use actix_web::{post, web, HttpResponse, Responder};
use gamenight_database::{
gamenight_participants::{
delete_gamenight_participant, insert_gamenight_participant, GamenightParticipant,
},
DbPool, GetConnection,
};
use uuid::Uuid;
use crate::{
models::gamenight_id::GamenightId,
request::{authorization::AuthUser, error::ApiError},
};
#[post("/join")]
pub async fn post_join_gamenight(
pool: web::Data<DbPool>,
user: AuthUser,
gamenight_id: web::Json<GamenightId>,
) -> Result<impl Responder, ApiError> {
web::block(move || -> Result<usize, ApiError> {
let mut conn = pool.get_conn();
Ok(insert_gamenight_participant(
&mut conn,
GamenightParticipant {
gamenight_id: Uuid::parse_str(&gamenight_id.gamenight_id)?,
user_id: user.0.id,
},
)?)
})
.await??;
Ok(HttpResponse::Ok())
}
#[post("/leave")]
pub async fn post_leave_gamenight(
pool: web::Data<DbPool>,
user: AuthUser,
gamenight_id: web::Json<GamenightId>,
) -> Result<impl Responder, ApiError> {
web::block(move || -> Result<usize, ApiError> {
let mut conn = pool.get_conn();
let participant = GamenightParticipant {
gamenight_id: Uuid::parse_str(&gamenight_id.gamenight_id)?,
user_id: user.0.id,
};
let x = delete_gamenight_participant(&mut conn, participant)?;
Ok(x)
})
.await??;
Ok(HttpResponse::Ok())
}

View File

@@ -0,0 +1,65 @@
use actix_web::{get, http::header::ContentType, post, web, HttpResponse, Responder};
use gamenight_database::{
location::{insert_location, locations},
DbPool, GetConnection,
};
use uuid::Uuid;
use crate::{
models::{
add_location_request_body::AddLocationRequestBody, location::Location,
location_id::LocationId,
},
request::{authorization::AuthUser, error::ApiError},
};
impl From<AddLocationRequestBody> for gamenight_database::location::Location {
fn from(value: AddLocationRequestBody) -> Self {
Self {
id: Uuid::new_v4(),
name: value.name,
address: value.address,
note: value.note,
}
}
}
#[get("/locations")]
pub async fn get_locations(
pool: web::Data<DbPool>,
_user: AuthUser,
) -> Result<impl Responder, ApiError> {
let mut conn = pool.get_conn();
let games: Vec<gamenight_database::location::Location> = locations(&mut conn)?;
let model: Vec<Location> = games
.iter()
.map(|x| Location {
id: x.id.to_string(),
name: x.name.clone(),
address: x.address.clone(),
note: x.note.clone(),
})
.collect();
Ok(HttpResponse::Ok()
.content_type(ContentType::json())
.body(serde_json::to_string(&model)?))
}
#[post("/location")]
pub async fn post_location(
pool: web::Data<DbPool>,
_user: AuthUser,
game_data: web::Json<AddLocationRequestBody>,
) -> Result<impl Responder, ApiError> {
let mut conn = pool.get_conn();
let uuid = insert_location(&mut conn, game_data.0.into())?;
let model = LocationId {
location_id: uuid.to_string(),
};
Ok(HttpResponse::Ok()
.content_type(ContentType::json())
.body(serde_json::to_string(&model)?))
}

View File

@@ -0,0 +1,79 @@
use actix_web::{get, http::header::ContentType, post, web, HttpResponse, Responder};
use gamenight_database::{
location_owner::{grant_permission, location_permissions, revoke_permission, LocationOwner},
user::Role,
DbPool, GetConnection,
};
use uuid::Uuid;
use crate::{
models::{
authorize_location_request_body::{
AuthorizeLocationRequestBody,
Op::{Grant, Revoke},
},
location_id::LocationId,
user_id::UserId,
},
request::{authorization::AuthUser, error::ApiError},
};
impl<'a> TryFrom<&'a AuthorizeLocationRequestBody> for LocationOwner {
type Error = ApiError;
fn try_from(value: &'a AuthorizeLocationRequestBody) -> Result<Self, Self::Error> {
Ok(LocationOwner {
location_id: Uuid::parse_str(&value.location_id)?,
user_id: Uuid::parse_str(&value.user_id)?,
})
}
}
#[post("/location_authorize")]
pub async fn post_location_authorize(
pool: web::Data<DbPool>,
user: AuthUser,
auth_data: web::Json<AuthorizeLocationRequestBody>,
) -> Result<impl Responder, ApiError> {
let mut conn = pool.get_conn();
let inner_auth_data = &auth_data.into_inner();
let location_owner: LocationOwner = inner_auth_data.try_into()?;
let authorized = location_permissions(&mut conn, location_owner.location_id)?;
if user.0.role != Role::Admin && !authorized.contains(&user.0.id) {
Ok(HttpResponse::Unauthorized())
} else {
match inner_auth_data.op {
Grant => grant_permission(&mut conn, location_owner)?,
Revoke => revoke_permission(&mut conn, location_owner)?,
};
Ok(HttpResponse::Ok())
}
}
impl From<Uuid> for UserId {
fn from(value: Uuid) -> Self {
Self {
user_id: value.to_string(),
}
}
}
#[get("/authorized_location_user_ids")]
pub async fn get_authorized_location_user_ids(
pool: web::Data<DbPool>,
_user: AuthUser,
location_id: web::Json<LocationId>,
) -> Result<impl Responder, ApiError> {
let mut conn = pool.get_conn();
let permissions =
location_permissions(&mut conn, Uuid::parse_str(&location_id.0.location_id)?)?;
let model: Vec<UserId> = permissions.into_iter().map(Into::into).collect();
Ok(HttpResponse::Ok()
.content_type(ContentType::json())
.body(serde_json::to_string(&model)?))
}

View File

@@ -0,0 +1,34 @@
mod authorization;
mod error;
mod game;
mod gamenight_handlers;
mod join_gamenight;
mod location;
mod location_owner;
mod participant_handlers;
mod user_handlers;
pub use game::get_game;
pub use game::get_games;
pub use game::get_owned_games;
pub use game::post_disown_game;
pub use game::post_game;
pub use game::post_own_game;
pub use game::post_rename_game;
pub use game::delete_game;
pub use gamenight_handlers::gamenight_get;
pub use gamenight_handlers::gamenight_post;
pub use gamenight_handlers::gamenights;
pub use join_gamenight::post_join_gamenight;
pub use join_gamenight::post_leave_gamenight;
pub use location::get_locations;
pub use location::post_location;
pub use location_owner::get_authorized_location_user_ids;
pub use location_owner::post_location_authorize;
pub use participant_handlers::get_get_participants;
pub use user_handlers::get_user;
pub use user_handlers::get_user_unauthenticated;
pub use user_handlers::get_users;
pub use user_handlers::login;
pub use user_handlers::refresh;
pub use user_handlers::register;

View File

@@ -0,0 +1,31 @@
use actix_web::{get, http::header::ContentType, web, HttpResponse, Responder};
use gamenight_database::{DbPool, GetConnection};
use uuid::Uuid;
use crate::{
models::{gamenight_id::GamenightId, participants::Participants},
request::{authorization::AuthUser, error::ApiError},
};
#[get("/participants")]
pub async fn get_get_participants(
pool: web::Data<DbPool>,
_user: AuthUser,
gamenight_info: web::Json<GamenightId>,
) -> Result<impl Responder, ApiError> {
let mut conn = pool.get_conn();
let users = gamenight_database::get_participants(
&mut conn,
&Uuid::parse_str(&gamenight_info.into_inner().gamenight_id)?,
)?
.iter()
.map(|x| x.to_string())
.collect();
Ok(HttpResponse::Ok()
.content_type(ContentType::json())
.body(serde_json::to_string(&Participants {
participants: users,
})?))
}

View File

@@ -0,0 +1,192 @@
use crate::models::{
login::Login, registration::Registration, token::Token, user::User, user_id::UserId,
};
use crate::request::{authorization::get_token, error::ApiError};
use actix_web::{get, http::header::ContentType, post, web, HttpResponse, Responder};
use gamenight_database::user::{count_users_with_email, count_users_with_username};
use gamenight_database::{DbPool, GetConnection};
use serde::{Deserialize, Serialize};
use serde_json;
use uuid::Uuid;
use validator::{Validate, ValidateArgs, ValidationError};
use super::authorization::AuthUser;
impl From<Login> for gamenight_database::user::LoginUser {
fn from(val: Login) -> Self {
gamenight_database::user::LoginUser {
username: val.username,
password: val.password,
}
}
}
impl From<Registration> for gamenight_database::user::Register {
fn from(val: Registration) -> Self {
gamenight_database::user::Register {
email: val.email,
username: val.username,
password: val.password,
}
}
}
pub struct RegisterContext<'v_a> {
pub pool: &'v_a DbPool,
}
pub fn unique_username(
username: &String,
context: &RegisterContext,
) -> Result<(), ValidationError> {
let mut conn = context.pool.get_conn();
match count_users_with_username(&mut conn, username) {
Ok(0) => Ok(()),
Ok(_) => Err(ValidationError::new("User already exists")),
Err(_) => Err(ValidationError::new("Database error while validating user")),
}
}
pub fn unique_email(email: &String, context: &RegisterContext) -> Result<(), ValidationError> {
let mut conn = context.pool.get_conn();
match count_users_with_email(&mut conn, email) {
Ok(0) => Ok(()),
Ok(_) => Err(ValidationError::new("email already exists")),
Err(_) => Err(ValidationError::new(
"Database error while validating email",
)),
}
}
#[derive(Serialize, Deserialize, Clone, Validate)]
#[validate(context = RegisterContext::<'v_a>)]
pub struct ValidatableRegistration {
#[validate(length(min = 1), custom(function = "unique_username", use_context))]
pub username: String,
#[validate(email, custom(function = "unique_email", use_context))]
pub email: String,
#[validate(length(min = 10), must_match(other = "password_repeat",))]
pub password: String,
pub password_repeat: String,
}
impl From<Registration> for ValidatableRegistration {
fn from(value: Registration) -> Self {
Self {
username: value.username,
email: value.email,
password: value.password,
password_repeat: value.password_repeat,
}
}
}
#[get("/token")]
pub async fn login(
pool: web::Data<DbPool>,
login_data: web::Json<Login>,
) -> Result<impl Responder, ApiError> {
let data = login_data.into_inner();
if let Ok(Some(user)) = web::block(move || {
let mut conn = pool.get_conn();
gamenight_database::login(&mut conn, data.into())
})
.await?
{
let token = get_token(&user)?;
let response = Token {
jwt_token: Some(token),
};
Ok(HttpResponse::Ok()
.content_type(ContentType::json())
.body(serde_json::to_string(&response)?))
} else {
Err(ApiError {
status: 401,
message: "User doesn't exist or password doesn't match".to_string(),
})
}
}
#[post("/token")]
pub async fn refresh(user: AuthUser) -> Result<impl Responder, ApiError> {
let new_token = get_token(&user.0)?;
let response = Token {
jwt_token: Some(new_token),
};
Ok(HttpResponse::Ok()
.content_type(ContentType::json())
.body(serde_json::to_string(&response)?))
}
#[post("/user")]
pub async fn register(
pool: web::Data<DbPool>,
register_data: web::Json<Registration>,
) -> Result<impl Responder, ApiError> {
web::block(move || -> Result<(), ApiError> {
let validatable_registration: ValidatableRegistration = register_data.clone().into();
validatable_registration.validate_with_args(&RegisterContext { pool: &pool })?;
let register_request = register_data.into_inner().into();
let mut conn = pool.get_conn();
gamenight_database::register(&mut conn, register_request)?;
Ok(())
})
.await??;
Ok(HttpResponse::Ok())
}
impl From<gamenight_database::user::User> for User {
fn from(value: gamenight_database::user::User) -> Self {
Self {
id: value.id.to_string(),
username: value.username,
email: None,
}
}
}
#[get("/user")]
pub async fn get_user(
pool: web::Data<DbPool>,
_user: AuthUser,
user_info: web::Json<UserId>,
) -> Result<impl Responder, ApiError> {
let mut conn = pool.get_conn();
let user = gamenight_database::user::get_user(
&mut conn,
Uuid::parse_str(&user_info.into_inner().user_id)?,
)?;
Ok(HttpResponse::Ok()
.content_type(ContentType::json())
.body(serde_json::to_string(&user)?))
}
#[get("/user")]
pub async fn get_user_unauthenticated(
_path: web::Path<UserId>,
) -> Result<impl Responder, ApiError> {
Ok(HttpResponse::Forbidden())
}
#[get("/users")]
pub async fn get_users(
pool: web::Data<DbPool>,
_user: AuthUser,
) -> Result<impl Responder, ApiError> {
let mut conn = pool.get_conn();
let users = gamenight_database::user::get_users(&mut conn)?;
let model: Vec<User> = users.into_iter().map(Into::into).collect();
Ok(HttpResponse::Ok()
.content_type(ContentType::json())
.body(serde_json::to_string(&model)?))
}

18
docker-compose.yml Normal file
View File

@@ -0,0 +1,18 @@
services:
db:
container_name: pg_container
image: postgres
environment:
POSTGRES_USER: root
POSTGRES_PASSWORD: root
POSTGRES_DB: gamenight
ports:
- "5432:5432"
pgadmin:
container_name: pgadmin4_container
image: dpage/pgadmin4
environment:
PGADMIN_DEFAULT_EMAIL: admin@admin.com
PGADMIN_DEFAULT_PASSWORD: root
ports:
- "5050:80"

3
gamenight-api-client-rs/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
/target/
**/*.rs.bk
Cargo.lock

View File

@@ -0,0 +1,23 @@
# OpenAPI Generator Ignore
# Generated by openapi-generator https://github.com/openapitools/openapi-generator
# Use this file to prevent files from being overwritten by the generator.
# The patterns follow closely to .gitignore or .dockerignore.
# As an example, the C# client generator defines ApiClient.cs.
# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line:
#ApiClient.cs
# You can match any string of characters against a directory, file or extension with a single asterisk (*):
#foo/*/qux
# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux
# You can recursively match patterns against a directory, file or extension with a double asterisk (**):
#foo/**/qux
# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux
# You can also negate patterns with an exclamation (!).
# For example, you can ignore all files in a docs folder with the file extension .md:
#docs/*.md
# Then explicitly reverse the ignore rule for a single file:
#!docs/README.md

View File

@@ -0,0 +1,51 @@
.gitignore
.travis.yml
Cargo.toml
README.md
docs/AddGameRequestBody.md
docs/AddGamenightRequestBody.md
docs/AddLocationRequestBody.md
docs/AuthorizeLocationRequestBody.md
docs/DefaultApi.md
docs/Failure.md
docs/Game.md
docs/GameId.md
docs/Gamenight.md
docs/GamenightId.md
docs/GetGamenightRequestBody.md
docs/Location.md
docs/LocationId.md
docs/Login.md
docs/OwnGameRequestBody.md
docs/Participants.md
docs/Registration.md
docs/RenameGameRequestBody.md
docs/Token.md
docs/User.md
docs/UserId.md
git_push.sh
src/apis/configuration.rs
src/apis/default_api.rs
src/apis/mod.rs
src/lib.rs
src/models/add_game_request_body.rs
src/models/add_gamenight_request_body.rs
src/models/add_location_request_body.rs
src/models/authorize_location_request_body.rs
src/models/failure.rs
src/models/game.rs
src/models/game_id.rs
src/models/gamenight.rs
src/models/gamenight_id.rs
src/models/get_gamenight_request_body.rs
src/models/location.rs
src/models/location_id.rs
src/models/login.rs
src/models/mod.rs
src/models/own_game_request_body.rs
src/models/participants.rs
src/models/registration.rs
src/models/rename_game_request_body.rs
src/models/token.rs
src/models/user.rs
src/models/user_id.rs

View File

@@ -0,0 +1 @@
7.18.0

View File

@@ -0,0 +1 @@
language: rust

View File

@@ -0,0 +1,19 @@
[package]
name = "gamenight-api-client-rs"
version = "0.1.0"
authors = ["dennis@brentj.es"]
description = "Api specification for a Gamenight server"
license = "MIT"
edition = "2021"
[dependencies]
serde = { version = "^1.0", features = ["derive"] }
serde_json = "^1.0"
serde_repr = "^0.1"
url = "^2.5"
reqwest = { version = "^0.12", default-features = false, features = ["json", "multipart"] }
[features]
default = ["native-tls"]
native-tls = ["reqwest/native-tls"]
rustls-tls = ["reqwest/rustls-tls"]

View File

@@ -0,0 +1,89 @@
# Rust API client for gamenight-api-client-rs
Api specification for a Gamenight server
For more information, please visit [https://brentj.es](https://brentj.es)
## Overview
This API client was generated by the [OpenAPI Generator](https://openapi-generator.tech) project. By using the [openapi-spec](https://openapis.org) from a remote server, you can easily generate an API client.
- API version: 1.0
- Package version: 0.1.0
- Generator version: 7.18.0
- Build package: `org.openapitools.codegen.languages.RustClientCodegen`
## Installation
Put the package under your project folder in a directory named `gamenight-api-client-rs` and add the following to `Cargo.toml` under `[dependencies]`:
```
gamenight-api-client-rs = { path = "./gamenight-api-client-rs" }
```
## Documentation for API Endpoints
All URIs are relative to *http://localhost:8080*
Class | Method | HTTP request | Description
------------ | ------------- | ------------- | -------------
*DefaultApi* | [**authorized_location_user_ids_get**](docs/DefaultApi.md#authorized_location_user_ids_get) | **GET** /authorized_location_user_ids |
*DefaultApi* | [**disown_post**](docs/DefaultApi.md#disown_post) | **POST** /disown |
*DefaultApi* | [**game_delete**](docs/DefaultApi.md#game_delete) | **DELETE** /game |
*DefaultApi* | [**game_get**](docs/DefaultApi.md#game_get) | **GET** /game |
*DefaultApi* | [**game_post**](docs/DefaultApi.md#game_post) | **POST** /game |
*DefaultApi* | [**games_get**](docs/DefaultApi.md#games_get) | **GET** /games |
*DefaultApi* | [**get_gamenight**](docs/DefaultApi.md#get_gamenight) | **GET** /gamenight |
*DefaultApi* | [**get_gamenights**](docs/DefaultApi.md#get_gamenights) | **GET** /gamenights | Get a all gamenights
*DefaultApi* | [**get_token**](docs/DefaultApi.md#get_token) | **GET** /token |
*DefaultApi* | [**join_post**](docs/DefaultApi.md#join_post) | **POST** /join |
*DefaultApi* | [**leave_post**](docs/DefaultApi.md#leave_post) | **POST** /leave |
*DefaultApi* | [**location_authorize_post**](docs/DefaultApi.md#location_authorize_post) | **POST** /location_authorize |
*DefaultApi* | [**location_get**](docs/DefaultApi.md#location_get) | **GET** /location |
*DefaultApi* | [**location_post**](docs/DefaultApi.md#location_post) | **POST** /location |
*DefaultApi* | [**locations_get**](docs/DefaultApi.md#locations_get) | **GET** /locations |
*DefaultApi* | [**own_post**](docs/DefaultApi.md#own_post) | **POST** /own |
*DefaultApi* | [**owned_games_get**](docs/DefaultApi.md#owned_games_get) | **GET** /owned_games |
*DefaultApi* | [**participants_get**](docs/DefaultApi.md#participants_get) | **GET** /participants | Get all participants for a gamenight
*DefaultApi* | [**post_gamenight**](docs/DefaultApi.md#post_gamenight) | **POST** /gamenight |
*DefaultApi* | [**post_register**](docs/DefaultApi.md#post_register) | **POST** /user |
*DefaultApi* | [**post_token**](docs/DefaultApi.md#post_token) | **POST** /token |
*DefaultApi* | [**rename_game_post**](docs/DefaultApi.md#rename_game_post) | **POST** /rename_game |
*DefaultApi* | [**user_get**](docs/DefaultApi.md#user_get) | **GET** /user |
*DefaultApi* | [**users_get**](docs/DefaultApi.md#users_get) | **GET** /users |
## Documentation For Models
- [AddGameRequestBody](docs/AddGameRequestBody.md)
- [AddGamenightRequestBody](docs/AddGamenightRequestBody.md)
- [AddLocationRequestBody](docs/AddLocationRequestBody.md)
- [AuthorizeLocationRequestBody](docs/AuthorizeLocationRequestBody.md)
- [Failure](docs/Failure.md)
- [Game](docs/Game.md)
- [GameId](docs/GameId.md)
- [Gamenight](docs/Gamenight.md)
- [GamenightId](docs/GamenightId.md)
- [GetGamenightRequestBody](docs/GetGamenightRequestBody.md)
- [Location](docs/Location.md)
- [LocationId](docs/LocationId.md)
- [Login](docs/Login.md)
- [OwnGameRequestBody](docs/OwnGameRequestBody.md)
- [Participants](docs/Participants.md)
- [Registration](docs/Registration.md)
- [RenameGameRequestBody](docs/RenameGameRequestBody.md)
- [Token](docs/Token.md)
- [User](docs/User.md)
- [UserId](docs/UserId.md)
To get access to the crate's generated documentation, use:
```
cargo doc --open
```
## Author
dennis@brentj.es

View File

@@ -0,0 +1,10 @@
use std::process::Command;
fn main() {
println!("cargo::rerun-if-changed=../backend-actix/gamenight-api.yaml");
let _ =
Command::new("openapi-generator")
.args(["generate", "-i", "../backend-actix/gamenight-api.yaml", "-g", "rust", "--additional-properties=withSeparateModelsAndApi=true,modelPackage=gamenight_model,apiPackage=gamenight_api,packageName=gamenight-api-client-rs,packageVersion=0.1.0"])
.output()
.expect("Failed to generate models sources for the gamenight API");
}

View File

@@ -0,0 +1,11 @@
# AddGame
## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**name** | **String** | |
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

View File

@@ -0,0 +1,11 @@
# AddGameRequestBody
## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**name** | **String** | |
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

View File

@@ -0,0 +1,12 @@
# AddGamenightRequestBody
## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**name** | **String** | |
**datetime** | **String** | |
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

View File

@@ -0,0 +1,13 @@
# AddLocation
## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**name** | **String** | |
**address** | Option<**String**> | | [optional]
**note** | Option<**String**> | | [optional]
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

View File

@@ -0,0 +1,13 @@
# AddLocationRequestBody
## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**name** | **String** | |
**address** | Option<**String**> | | [optional]
**note** | Option<**String**> | | [optional]
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

View File

@@ -0,0 +1,13 @@
# AuthorizeLocationRequestBody
## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**location_id** | **String** | |
**user_id** | **String** | |
**op** | **String** | |
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

View File

@@ -0,0 +1,703 @@
# \DefaultApi
All URIs are relative to *http://localhost:8080*
Method | HTTP request | Description
------------- | ------------- | -------------
[**authorized_location_user_ids_get**](DefaultApi.md#authorized_location_user_ids_get) | **GET** /authorized_location_user_ids |
[**disown_post**](DefaultApi.md#disown_post) | **POST** /disown |
[**game_delete**](DefaultApi.md#game_delete) | **DELETE** /game |
[**game_get**](DefaultApi.md#game_get) | **GET** /game |
[**game_post**](DefaultApi.md#game_post) | **POST** /game |
[**games_get**](DefaultApi.md#games_get) | **GET** /games |
[**get_gamenight**](DefaultApi.md#get_gamenight) | **GET** /gamenight |
[**get_gamenights**](DefaultApi.md#get_gamenights) | **GET** /gamenights | Get a all gamenights
[**get_token**](DefaultApi.md#get_token) | **GET** /token |
[**join_post**](DefaultApi.md#join_post) | **POST** /join |
[**leave_post**](DefaultApi.md#leave_post) | **POST** /leave |
[**location_authorize_post**](DefaultApi.md#location_authorize_post) | **POST** /location_authorize |
[**location_get**](DefaultApi.md#location_get) | **GET** /location |
[**location_post**](DefaultApi.md#location_post) | **POST** /location |
[**locations_get**](DefaultApi.md#locations_get) | **GET** /locations |
[**own_post**](DefaultApi.md#own_post) | **POST** /own |
[**owned_games_get**](DefaultApi.md#owned_games_get) | **GET** /owned_games |
[**participants_get**](DefaultApi.md#participants_get) | **GET** /participants | Get all participants for a gamenight
[**post_gamenight**](DefaultApi.md#post_gamenight) | **POST** /gamenight |
[**post_register**](DefaultApi.md#post_register) | **POST** /user |
[**post_token**](DefaultApi.md#post_token) | **POST** /token |
[**rename_game_post**](DefaultApi.md#rename_game_post) | **POST** /rename_game |
[**user_get**](DefaultApi.md#user_get) | **GET** /user |
[**users_get**](DefaultApi.md#users_get) | **GET** /users |
## authorized_location_user_ids_get
> Vec<models::UserId> authorized_location_user_ids_get(location_id)
### Parameters
Name | Type | Description | Required | Notes
------------- | ------------- | ------------- | ------------- | -------------
**location_id** | Option<[**LocationId**](LocationId.md)> | | |
### Return type
[**Vec<models::UserId>**](UserId.md)
### Authorization
[JWT-Auth](../README.md#JWT-Auth)
### HTTP request headers
- **Content-Type**: application/json
- **Accept**: application/json
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
## disown_post
> disown_post(game_id)
### Parameters
Name | Type | Description | Required | Notes
------------- | ------------- | ------------- | ------------- | -------------
**game_id** | Option<[**GameId**](GameId.md)> | | |
### Return type
(empty response body)
### Authorization
[JWT-Auth](../README.md#JWT-Auth)
### HTTP request headers
- **Content-Type**: application/json
- **Accept**: application/json
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
## game_delete
> game_delete(game_id)
### Parameters
Name | Type | Description | Required | Notes
------------- | ------------- | ------------- | ------------- | -------------
**game_id** | Option<[**GameId**](GameId.md)> | | |
### Return type
(empty response body)
### Authorization
[JWT-Auth](../README.md#JWT-Auth)
### HTTP request headers
- **Content-Type**: application/json
- **Accept**: application/json
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
## game_get
> models::Game game_get(game_id)
### Parameters
Name | Type | Description | Required | Notes
------------- | ------------- | ------------- | ------------- | -------------
**game_id** | Option<[**GameId**](GameId.md)> | | |
### Return type
[**models::Game**](Game.md)
### Authorization
[JWT-Auth](../README.md#JWT-Auth)
### HTTP request headers
- **Content-Type**: application/json
- **Accept**: application/json
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
## game_post
> models::GameId game_post(add_game_request_body)
### Parameters
Name | Type | Description | Required | Notes
------------- | ------------- | ------------- | ------------- | -------------
**add_game_request_body** | Option<[**AddGameRequestBody**](AddGameRequestBody.md)> | | |
### Return type
[**models::GameId**](GameId.md)
### Authorization
[JWT-Auth](../README.md#JWT-Auth)
### HTTP request headers
- **Content-Type**: application/json
- **Accept**: application/json
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
## games_get
> Vec<models::Game> games_get()
### Parameters
This endpoint does not need any parameter.
### Return type
[**Vec<models::Game>**](Game.md)
### Authorization
[JWT-Auth](../README.md#JWT-Auth)
### HTTP request headers
- **Content-Type**: Not defined
- **Accept**: application/json
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
## get_gamenight
> models::Gamenight get_gamenight(get_gamenight_request_body)
### Parameters
Name | Type | Description | Required | Notes
------------- | ------------- | ------------- | ------------- | -------------
**get_gamenight_request_body** | Option<[**GetGamenightRequestBody**](GetGamenightRequestBody.md)> | | |
### Return type
[**models::Gamenight**](Gamenight.md)
### Authorization
[JWT-Auth](../README.md#JWT-Auth)
### HTTP request headers
- **Content-Type**: application/json
- **Accept**: application/json
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
## get_gamenights
> Vec<models::Gamenight> get_gamenights()
Get a all gamenights
Retrieve the list of gamenights on this gamenight server. Requires authorization.
### Parameters
This endpoint does not need any parameter.
### Return type
[**Vec<models::Gamenight>**](Gamenight.md)
### Authorization
[JWT-Auth](../README.md#JWT-Auth)
### HTTP request headers
- **Content-Type**: Not defined
- **Accept**: application/json
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
## get_token
> models::Token get_token(login)
Submit your credentials to get a JWT-token to use with the rest of the api.
### Parameters
Name | Type | Description | Required | Notes
------------- | ------------- | ------------- | ------------- | -------------
**login** | Option<[**Login**](Login.md)> | | |
### Return type
[**models::Token**](Token.md)
### Authorization
No authorization required
### HTTP request headers
- **Content-Type**: application/json
- **Accept**: application/json
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
## join_post
> join_post(gamenight_id)
### Parameters
Name | Type | Description | Required | Notes
------------- | ------------- | ------------- | ------------- | -------------
**gamenight_id** | Option<[**GamenightId**](GamenightId.md)> | | |
### Return type
(empty response body)
### Authorization
[JWT-Auth](../README.md#JWT-Auth)
### HTTP request headers
- **Content-Type**: application/json
- **Accept**: application/json
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
## leave_post
> leave_post(gamenight_id)
### Parameters
Name | Type | Description | Required | Notes
------------- | ------------- | ------------- | ------------- | -------------
**gamenight_id** | Option<[**GamenightId**](GamenightId.md)> | | |
### Return type
(empty response body)
### Authorization
[JWT-Auth](../README.md#JWT-Auth)
### HTTP request headers
- **Content-Type**: application/json
- **Accept**: application/json
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
## location_authorize_post
> location_authorize_post(authorize_location_request_body)
### Parameters
Name | Type | Description | Required | Notes
------------- | ------------- | ------------- | ------------- | -------------
**authorize_location_request_body** | Option<[**AuthorizeLocationRequestBody**](AuthorizeLocationRequestBody.md)> | | |
### Return type
(empty response body)
### Authorization
[JWT-Auth](../README.md#JWT-Auth)
### HTTP request headers
- **Content-Type**: application/json
- **Accept**: application/json
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
## location_get
> models::Location location_get(location_id)
### Parameters
Name | Type | Description | Required | Notes
------------- | ------------- | ------------- | ------------- | -------------
**location_id** | Option<[**LocationId**](LocationId.md)> | | |
### Return type
[**models::Location**](Location.md)
### Authorization
[JWT-Auth](../README.md#JWT-Auth)
### HTTP request headers
- **Content-Type**: application/json
- **Accept**: application/json
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
## location_post
> models::LocationId location_post(add_location_request_body)
### Parameters
Name | Type | Description | Required | Notes
------------- | ------------- | ------------- | ------------- | -------------
**add_location_request_body** | Option<[**AddLocationRequestBody**](AddLocationRequestBody.md)> | | |
### Return type
[**models::LocationId**](LocationId.md)
### Authorization
[JWT-Auth](../README.md#JWT-Auth)
### HTTP request headers
- **Content-Type**: application/json
- **Accept**: application/json
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
## locations_get
> Vec<models::Location> locations_get()
### Parameters
This endpoint does not need any parameter.
### Return type
[**Vec<models::Location>**](Location.md)
### Authorization
[JWT-Auth](../README.md#JWT-Auth)
### HTTP request headers
- **Content-Type**: Not defined
- **Accept**: application/json
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
## own_post
> own_post(own_game_request_body)
### Parameters
Name | Type | Description | Required | Notes
------------- | ------------- | ------------- | ------------- | -------------
**own_game_request_body** | Option<[**OwnGameRequestBody**](OwnGameRequestBody.md)> | | |
### Return type
(empty response body)
### Authorization
[JWT-Auth](../README.md#JWT-Auth)
### HTTP request headers
- **Content-Type**: application/json
- **Accept**: application/json
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
## owned_games_get
> Vec<String> owned_games_get(user_id)
### Parameters
Name | Type | Description | Required | Notes
------------- | ------------- | ------------- | ------------- | -------------
**user_id** | Option<[**UserId**](UserId.md)> | | |
### Return type
**Vec<String>**
### Authorization
[JWT-Auth](../README.md#JWT-Auth)
### HTTP request headers
- **Content-Type**: application/json
- **Accept**: application/json
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
## participants_get
> models::Participants participants_get(gamenight_id)
Get all participants for a gamenight
Retrieve the participants of a single gamenight by id.
### Parameters
Name | Type | Description | Required | Notes
------------- | ------------- | ------------- | ------------- | -------------
**gamenight_id** | Option<[**GamenightId**](GamenightId.md)> | | |
### Return type
[**models::Participants**](Participants.md)
### Authorization
[JWT-Auth](../README.md#JWT-Auth)
### HTTP request headers
- **Content-Type**: application/json
- **Accept**: application/json
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
## post_gamenight
> post_gamenight(add_gamenight_request_body)
Add a gamenight by providing a name and a date, only available when providing an JWT token.
### Parameters
Name | Type | Description | Required | Notes
------------- | ------------- | ------------- | ------------- | -------------
**add_gamenight_request_body** | Option<[**AddGamenightRequestBody**](AddGamenightRequestBody.md)> | | |
### Return type
(empty response body)
### Authorization
[JWT-Auth](../README.md#JWT-Auth)
### HTTP request headers
- **Content-Type**: application/json
- **Accept**: application/json
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
## post_register
> post_register(registration)
Create a new user given a registration token and user information, username and email must be unique, and password and password_repeat must match.
### Parameters
Name | Type | Description | Required | Notes
------------- | ------------- | ------------- | ------------- | -------------
**registration** | Option<[**Registration**](Registration.md)> | | |
### Return type
(empty response body)
### Authorization
[JWT-Auth](../README.md#JWT-Auth)
### HTTP request headers
- **Content-Type**: application/json
- **Accept**: application/json
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
## post_token
> models::Token post_token()
Refresh your JWT-token without logging in again.
### Parameters
This endpoint does not need any parameter.
### Return type
[**models::Token**](Token.md)
### Authorization
[JWT-Auth](../README.md#JWT-Auth)
### HTTP request headers
- **Content-Type**: Not defined
- **Accept**: application/json
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
## rename_game_post
> rename_game_post(rename_game_request_body)
### Parameters
Name | Type | Description | Required | Notes
------------- | ------------- | ------------- | ------------- | -------------
**rename_game_request_body** | Option<[**RenameGameRequestBody**](RenameGameRequestBody.md)> | | |
### Return type
(empty response body)
### Authorization
[JWT-Auth](../README.md#JWT-Auth)
### HTTP request headers
- **Content-Type**: application/json
- **Accept**: application/json
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
## user_get
> models::User user_get(user_id)
Get a user from primary id
### Parameters
Name | Type | Description | Required | Notes
------------- | ------------- | ------------- | ------------- | -------------
**user_id** | Option<[**UserId**](UserId.md)> | | |
### Return type
[**models::User**](User.md)
### Authorization
[JWT-Auth](../README.md#JWT-Auth)
### HTTP request headers
- **Content-Type**: application/json
- **Accept**: application/json
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
## users_get
> Vec<models::User> users_get()
### Parameters
This endpoint does not need any parameter.
### Return type
[**Vec<models::User>**](User.md)
### Authorization
[JWT-Auth](../README.md#JWT-Auth)
### HTTP request headers
- **Content-Type**: Not defined
- **Accept**: application/json
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)

View File

@@ -0,0 +1,11 @@
# Failure
## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**message** | Option<**String**> | | [optional]
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

View File

@@ -0,0 +1,12 @@
# Game
## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**id** | **String** | |
**name** | **String** | |
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

View File

@@ -0,0 +1,11 @@
# GameId
## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**game_id** | **String** | |
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

View File

@@ -0,0 +1,14 @@
# Gamenight
## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**id** | **String** | |
**name** | **String** | |
**datetime** | **String** | |
**owner_id** | **String** | |
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

View File

@@ -0,0 +1,11 @@
# GamenightId
## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**gamenight_id** | **String** | |
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

View File

@@ -0,0 +1,11 @@
# GetGamenightRequest
## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**id** | Option<**String**> | | [optional]
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

View File

@@ -0,0 +1,11 @@
# GetGamenightRequestBody
## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**id** | Option<**String**> | | [optional]
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

View File

@@ -0,0 +1,11 @@
# GetToken401Response
## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**message** | **String** | |
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

View File

@@ -0,0 +1,14 @@
# Location
## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**id** | **String** | |
**name** | **String** | |
**address** | Option<**String**> | | [optional]
**note** | Option<**String**> | | [optional]
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

View File

@@ -0,0 +1,11 @@
# LocationId
## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**location_id** | **String** | |
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

View File

@@ -0,0 +1,12 @@
# Login
## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**username** | **String** | |
**password** | **String** | |
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

View File

@@ -0,0 +1,12 @@
# OwnGameRequestBody
## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**game_id** | **String** | |
**location_id** | Option<**String**> | | [optional]
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

View File

@@ -0,0 +1,11 @@
# Participants
## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**participants** | **Vec<String>** | |
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

View File

@@ -0,0 +1,15 @@
# Registration
## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**username** | **String** | |
**email** | **String** | |
**password** | **String** | |
**password_repeat** | **String** | |
**registration_token** | **String** | |
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

View File

@@ -0,0 +1,12 @@
# RenameGameRequestBody
## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**id** | **String** | |
**name** | **String** | |
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

View File

@@ -0,0 +1,11 @@
# Token
## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**jwt_token** | Option<**String**> | | [optional]
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

View File

@@ -0,0 +1,13 @@
# User
## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**id** | **String** | |
**username** | **String** | |
**email** | Option<**String**> | | [optional]
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

View File

@@ -0,0 +1,11 @@
# UserId
## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**user_id** | **String** | |
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

View File

@@ -0,0 +1,57 @@
#!/bin/sh
# ref: https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/
#
# Usage example: /bin/sh ./git_push.sh wing328 openapi-petstore-perl "minor update" "gitlab.com"
git_user_id=$1
git_repo_id=$2
release_note=$3
git_host=$4
if [ "$git_host" = "" ]; then
git_host="github.com"
echo "[INFO] No command line input provided. Set \$git_host to $git_host"
fi
if [ "$git_user_id" = "" ]; then
git_user_id="GIT_USER_ID"
echo "[INFO] No command line input provided. Set \$git_user_id to $git_user_id"
fi
if [ "$git_repo_id" = "" ]; then
git_repo_id="GIT_REPO_ID"
echo "[INFO] No command line input provided. Set \$git_repo_id to $git_repo_id"
fi
if [ "$release_note" = "" ]; then
release_note="Minor update"
echo "[INFO] No command line input provided. Set \$release_note to $release_note"
fi
# Initialize the local directory as a Git repository
git init
# Adds the files in the local repository and stages them for commit.
git add .
# Commits the tracked changes and prepares them to be pushed to a remote repository.
git commit -m "$release_note"
# Sets the new remote
git_remote=$(git remote)
if [ "$git_remote" = "" ]; then # git remote not defined
if [ "$GIT_TOKEN" = "" ]; then
echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment."
git remote add origin https://${git_host}/${git_user_id}/${git_repo_id}.git
else
git remote add origin https://${git_user_id}:"${GIT_TOKEN}"@${git_host}/${git_user_id}/${git_repo_id}.git
fi
fi
git pull origin master
# Pushes (Forces) the changes in the local repository up to the remote repository
echo "Git pushing to https://${git_host}/${git_user_id}/${git_repo_id}.git"
git push origin master 2>&1 | grep -v 'To https'

View File

@@ -0,0 +1,51 @@
/*
* Gamenight
*
* Api specification for a Gamenight server
*
* The version of the OpenAPI document: 1.0
* Contact: dennis@brentj.es
* Generated by: https://openapi-generator.tech
*/
#[derive(Debug, Clone)]
pub struct Configuration {
pub base_path: String,
pub user_agent: Option<String>,
pub client: reqwest::Client,
pub basic_auth: Option<BasicAuth>,
pub oauth_access_token: Option<String>,
pub bearer_access_token: Option<String>,
pub api_key: Option<ApiKey>,
}
pub type BasicAuth = (String, Option<String>);
#[derive(Debug, Clone)]
pub struct ApiKey {
pub prefix: Option<String>,
pub key: String,
}
impl Configuration {
pub fn new() -> Configuration {
Configuration::default()
}
}
impl Default for Configuration {
fn default() -> Self {
Configuration {
base_path: "http://localhost:8080".to_owned(),
user_agent: Some("OpenAPI-Generator/1.0/rust".to_owned()),
client: reqwest::Client::new(),
basic_auth: None,
oauth_access_token: None,
bearer_access_token: None,
api_key: None,
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,116 @@
use std::error;
use std::fmt;
#[derive(Debug, Clone)]
pub struct ResponseContent<T> {
pub status: reqwest::StatusCode,
pub content: String,
pub entity: Option<T>,
}
#[derive(Debug)]
pub enum Error<T> {
Reqwest(reqwest::Error),
Serde(serde_json::Error),
Io(std::io::Error),
ResponseError(ResponseContent<T>),
}
impl <T> fmt::Display for Error<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let (module, e) = match self {
Error::Reqwest(e) => ("reqwest", e.to_string()),
Error::Serde(e) => ("serde", e.to_string()),
Error::Io(e) => ("IO", e.to_string()),
Error::ResponseError(e) => ("response", format!("status code {}", e.status)),
};
write!(f, "error in {}: {}", module, e)
}
}
impl <T: fmt::Debug> error::Error for Error<T> {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
Some(match self {
Error::Reqwest(e) => e,
Error::Serde(e) => e,
Error::Io(e) => e,
Error::ResponseError(_) => return None,
})
}
}
impl <T> From<reqwest::Error> for Error<T> {
fn from(e: reqwest::Error) -> Self {
Error::Reqwest(e)
}
}
impl <T> From<serde_json::Error> for Error<T> {
fn from(e: serde_json::Error) -> Self {
Error::Serde(e)
}
}
impl <T> From<std::io::Error> for Error<T> {
fn from(e: std::io::Error) -> Self {
Error::Io(e)
}
}
pub fn urlencode<T: AsRef<str>>(s: T) -> String {
::url::form_urlencoded::byte_serialize(s.as_ref().as_bytes()).collect()
}
pub fn parse_deep_object(prefix: &str, value: &serde_json::Value) -> Vec<(String, String)> {
if let serde_json::Value::Object(object) = value {
let mut params = vec![];
for (key, value) in object {
match value {
serde_json::Value::Object(_) => params.append(&mut parse_deep_object(
&format!("{}[{}]", prefix, key),
value,
)),
serde_json::Value::Array(array) => {
for (i, value) in array.iter().enumerate() {
params.append(&mut parse_deep_object(
&format!("{}[{}][{}]", prefix, key, i),
value,
));
}
},
serde_json::Value::String(s) => params.push((format!("{}[{}]", prefix, key), s.clone())),
_ => params.push((format!("{}[{}]", prefix, key), value.to_string())),
}
}
return params;
}
unimplemented!("Only objects are supported with style=deepObject")
}
/// Internal use only
/// A content type supported by this client.
#[allow(dead_code)]
enum ContentType {
Json,
Text,
Unsupported(String)
}
impl From<&str> for ContentType {
fn from(content_type: &str) -> Self {
if content_type.starts_with("application") && content_type.contains("json") {
return Self::Json;
} else if content_type.starts_with("text/plain") {
return Self::Text;
} else {
return Self::Unsupported(content_type.to_string());
}
}
}
pub mod default_api;
pub mod configuration;

View File

@@ -0,0 +1,11 @@
#![allow(unused_imports)]
#![allow(clippy::too_many_arguments)]
extern crate serde_repr;
extern crate serde;
extern crate serde_json;
extern crate url;
extern crate reqwest;
pub mod apis;
pub mod models;

View File

@@ -0,0 +1,27 @@
/*
* Gamenight
*
* Api specifaction for a Gamenight server
*
* The version of the OpenAPI document: 1.0
* Contact: dennis@brentj.es
* Generated by: https://openapi-generator.tech
*/
use crate::models;
use serde::{Deserialize, Serialize};
#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
pub struct AddGame {
#[serde(rename = "name")]
pub name: String,
}
impl AddGame {
pub fn new(name: String) -> AddGame {
AddGame {
name,
}
}
}

View File

@@ -0,0 +1,27 @@
/*
* Gamenight
*
* Api specification for a Gamenight server
*
* The version of the OpenAPI document: 1.0
* Contact: dennis@brentj.es
* Generated by: https://openapi-generator.tech
*/
use crate::models;
use serde::{Deserialize, Serialize};
#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
pub struct AddGameRequestBody {
#[serde(rename = "name")]
pub name: String,
}
impl AddGameRequestBody {
pub fn new(name: String) -> AddGameRequestBody {
AddGameRequestBody {
name,
}
}
}

View File

@@ -0,0 +1,30 @@
/*
* Gamenight
*
* Api specification for a Gamenight server
*
* The version of the OpenAPI document: 1.0
* Contact: dennis@brentj.es
* Generated by: https://openapi-generator.tech
*/
use crate::models;
use serde::{Deserialize, Serialize};
#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
pub struct AddGamenightRequestBody {
#[serde(rename = "name")]
pub name: String,
#[serde(rename = "datetime")]
pub datetime: String,
}
impl AddGamenightRequestBody {
pub fn new(name: String, datetime: String) -> AddGamenightRequestBody {
AddGamenightRequestBody {
name,
datetime,
}
}
}

View File

@@ -0,0 +1,33 @@
/*
* Gamenight
*
* Api specifaction for a Gamenight server
*
* The version of the OpenAPI document: 1.0
* Contact: dennis@brentj.es
* Generated by: https://openapi-generator.tech
*/
use crate::models;
use serde::{Deserialize, Serialize};
#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
pub struct AddLocation {
#[serde(rename = "name")]
pub name: String,
#[serde(rename = "address", skip_serializing_if = "Option::is_none")]
pub address: Option<String>,
#[serde(rename = "note", skip_serializing_if = "Option::is_none")]
pub note: Option<String>,
}
impl AddLocation {
pub fn new(name: String) -> AddLocation {
AddLocation {
name,
address: None,
note: None,
}
}
}

View File

@@ -0,0 +1,33 @@
/*
* Gamenight
*
* Api specification for a Gamenight server
*
* The version of the OpenAPI document: 1.0
* Contact: dennis@brentj.es
* Generated by: https://openapi-generator.tech
*/
use crate::models;
use serde::{Deserialize, Serialize};
#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
pub struct AddLocationRequestBody {
#[serde(rename = "name")]
pub name: String,
#[serde(rename = "address", skip_serializing_if = "Option::is_none")]
pub address: Option<String>,
#[serde(rename = "note", skip_serializing_if = "Option::is_none")]
pub note: Option<String>,
}
impl AddLocationRequestBody {
pub fn new(name: String) -> AddLocationRequestBody {
AddLocationRequestBody {
name,
address: None,
note: None,
}
}
}

View File

@@ -0,0 +1,47 @@
/*
* Gamenight
*
* Api specification for a Gamenight server
*
* The version of the OpenAPI document: 1.0
* Contact: dennis@brentj.es
* Generated by: https://openapi-generator.tech
*/
use crate::models;
use serde::{Deserialize, Serialize};
#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
pub struct AuthorizeLocationRequestBody {
#[serde(rename = "location_id")]
pub location_id: String,
#[serde(rename = "user_id")]
pub user_id: String,
#[serde(rename = "op")]
pub op: Op,
}
impl AuthorizeLocationRequestBody {
pub fn new(location_id: String, user_id: String, op: Op) -> AuthorizeLocationRequestBody {
AuthorizeLocationRequestBody {
location_id,
user_id,
op,
}
}
}
///
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
pub enum Op {
#[serde(rename = "grant")]
Grant,
#[serde(rename = "revoke")]
Revoke,
}
impl Default for Op {
fn default() -> Op {
Self::Grant
}
}

View File

@@ -0,0 +1,29 @@
/*
* Gamenight
*
* Api specification for a Gamenight server
*
* The version of the OpenAPI document: 1.0
* Contact: dennis@brentj.es
* Generated by: https://openapi-generator.tech
*/
use crate::models;
use serde::{Deserialize, Serialize};
/// Failure : Failure Reason
#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
pub struct Failure {
#[serde(rename = "message", skip_serializing_if = "Option::is_none")]
pub message: Option<String>,
}
impl Failure {
/// Failure Reason
pub fn new() -> Failure {
Failure {
message: None,
}
}
}

View File

@@ -0,0 +1,30 @@
/*
* Gamenight
*
* Api specification for a Gamenight server
*
* The version of the OpenAPI document: 1.0
* Contact: dennis@brentj.es
* Generated by: https://openapi-generator.tech
*/
use crate::models;
use serde::{Deserialize, Serialize};
#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
pub struct Game {
#[serde(rename = "id")]
pub id: String,
#[serde(rename = "name")]
pub name: String,
}
impl Game {
pub fn new(id: String, name: String) -> Game {
Game {
id,
name,
}
}
}

View File

@@ -0,0 +1,27 @@
/*
* Gamenight
*
* Api specification for a Gamenight server
*
* The version of the OpenAPI document: 1.0
* Contact: dennis@brentj.es
* Generated by: https://openapi-generator.tech
*/
use crate::models;
use serde::{Deserialize, Serialize};
#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
pub struct GameId {
#[serde(rename = "game_id")]
pub game_id: String,
}
impl GameId {
pub fn new(game_id: String) -> GameId {
GameId {
game_id,
}
}
}

View File

@@ -0,0 +1,36 @@
/*
* Gamenight
*
* Api specification for a Gamenight server
*
* The version of the OpenAPI document: 1.0
* Contact: dennis@brentj.es
* Generated by: https://openapi-generator.tech
*/
use crate::models;
use serde::{Deserialize, Serialize};
#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
pub struct Gamenight {
#[serde(rename = "id")]
pub id: String,
#[serde(rename = "name")]
pub name: String,
#[serde(rename = "datetime")]
pub datetime: String,
#[serde(rename = "owner_id")]
pub owner_id: String,
}
impl Gamenight {
pub fn new(id: String, name: String, datetime: String, owner_id: String) -> Gamenight {
Gamenight {
id,
name,
datetime,
owner_id,
}
}
}

View File

@@ -0,0 +1,27 @@
/*
* Gamenight
*
* Api specification for a Gamenight server
*
* The version of the OpenAPI document: 1.0
* Contact: dennis@brentj.es
* Generated by: https://openapi-generator.tech
*/
use crate::models;
use serde::{Deserialize, Serialize};
#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
pub struct GamenightId {
#[serde(rename = "gamenight_id")]
pub gamenight_id: String,
}
impl GamenightId {
pub fn new(gamenight_id: String) -> GamenightId {
GamenightId {
gamenight_id,
}
}
}

View File

@@ -0,0 +1,27 @@
/*
* Gamenight
*
* Api specifaction for a Gamenight server
*
* The version of the OpenAPI document: 1.0
* Contact: dennis@brentj.es
* Generated by: https://openapi-generator.tech
*/
use crate::models;
use serde::{Deserialize, Serialize};
#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
pub struct GetGamenightRequest {
#[serde(rename = "id", skip_serializing_if = "Option::is_none")]
pub id: Option<String>,
}
impl GetGamenightRequest {
pub fn new() -> GetGamenightRequest {
GetGamenightRequest {
id: None,
}
}
}

View File

@@ -0,0 +1,27 @@
/*
* Gamenight
*
* Api specification for a Gamenight server
*
* The version of the OpenAPI document: 1.0
* Contact: dennis@brentj.es
* Generated by: https://openapi-generator.tech
*/
use crate::models;
use serde::{Deserialize, Serialize};
#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
pub struct GetGamenightRequestBody {
#[serde(rename = "id", skip_serializing_if = "Option::is_none")]
pub id: Option<String>,
}
impl GetGamenightRequestBody {
pub fn new() -> GetGamenightRequestBody {
GetGamenightRequestBody {
id: None,
}
}
}

View File

@@ -0,0 +1,27 @@
/*
* Gamenight
*
* Api specifaction for a Gamenight server
*
* The version of the OpenAPI document: 1.0
* Contact: dennis@brentj.es
* Generated by: https://openapi-generator.tech
*/
use crate::models;
use serde::{Deserialize, Serialize};
#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
pub struct GetToken401Response {
#[serde(rename = "message")]
pub message: String,
}
impl GetToken401Response {
pub fn new(message: String) -> GetToken401Response {
GetToken401Response {
message,
}
}
}

View File

@@ -0,0 +1,36 @@
/*
* Gamenight
*
* Api specification for a Gamenight server
*
* The version of the OpenAPI document: 1.0
* Contact: dennis@brentj.es
* Generated by: https://openapi-generator.tech
*/
use crate::models;
use serde::{Deserialize, Serialize};
#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
pub struct Location {
#[serde(rename = "id")]
pub id: String,
#[serde(rename = "name")]
pub name: String,
#[serde(rename = "address", skip_serializing_if = "Option::is_none")]
pub address: Option<String>,
#[serde(rename = "note", skip_serializing_if = "Option::is_none")]
pub note: Option<String>,
}
impl Location {
pub fn new(id: String, name: String) -> Location {
Location {
id,
name,
address: None,
note: None,
}
}
}

View File

@@ -0,0 +1,27 @@
/*
* Gamenight
*
* Api specification for a Gamenight server
*
* The version of the OpenAPI document: 1.0
* Contact: dennis@brentj.es
* Generated by: https://openapi-generator.tech
*/
use crate::models;
use serde::{Deserialize, Serialize};
#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
pub struct LocationId {
#[serde(rename = "location_id")]
pub location_id: String,
}
impl LocationId {
pub fn new(location_id: String) -> LocationId {
LocationId {
location_id,
}
}
}

View File

@@ -0,0 +1,30 @@
/*
* Gamenight
*
* Api specification for a Gamenight server
*
* The version of the OpenAPI document: 1.0
* Contact: dennis@brentj.es
* Generated by: https://openapi-generator.tech
*/
use crate::models;
use serde::{Deserialize, Serialize};
#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
pub struct Login {
#[serde(rename = "username")]
pub username: String,
#[serde(rename = "password")]
pub password: String,
}
impl Login {
pub fn new(username: String, password: String) -> Login {
Login {
username,
password,
}
}
}

View File

@@ -0,0 +1,40 @@
pub mod add_game_request_body;
pub use self::add_game_request_body::AddGameRequestBody;
pub mod add_gamenight_request_body;
pub use self::add_gamenight_request_body::AddGamenightRequestBody;
pub mod add_location_request_body;
pub use self::add_location_request_body::AddLocationRequestBody;
pub mod authorize_location_request_body;
pub use self::authorize_location_request_body::AuthorizeLocationRequestBody;
pub mod failure;
pub use self::failure::Failure;
pub mod game;
pub use self::game::Game;
pub mod game_id;
pub use self::game_id::GameId;
pub mod gamenight;
pub use self::gamenight::Gamenight;
pub mod gamenight_id;
pub use self::gamenight_id::GamenightId;
pub mod get_gamenight_request_body;
pub use self::get_gamenight_request_body::GetGamenightRequestBody;
pub mod location;
pub use self::location::Location;
pub mod location_id;
pub use self::location_id::LocationId;
pub mod login;
pub use self::login::Login;
pub mod own_game_request_body;
pub use self::own_game_request_body::OwnGameRequestBody;
pub mod participants;
pub use self::participants::Participants;
pub mod registration;
pub use self::registration::Registration;
pub mod rename_game_request_body;
pub use self::rename_game_request_body::RenameGameRequestBody;
pub mod token;
pub use self::token::Token;
pub mod user;
pub use self::user::User;
pub mod user_id;
pub use self::user_id::UserId;

View File

@@ -0,0 +1,30 @@
/*
* Gamenight
*
* Api specification for a Gamenight server
*
* The version of the OpenAPI document: 1.0
* Contact: dennis@brentj.es
* Generated by: https://openapi-generator.tech
*/
use crate::models;
use serde::{Deserialize, Serialize};
#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
pub struct OwnGameRequestBody {
#[serde(rename = "game_id")]
pub game_id: String,
#[serde(rename = "location_id", skip_serializing_if = "Option::is_none")]
pub location_id: Option<String>,
}
impl OwnGameRequestBody {
pub fn new(game_id: String) -> OwnGameRequestBody {
OwnGameRequestBody {
game_id,
location_id: None,
}
}
}

View File

@@ -0,0 +1,27 @@
/*
* Gamenight
*
* Api specification for a Gamenight server
*
* The version of the OpenAPI document: 1.0
* Contact: dennis@brentj.es
* Generated by: https://openapi-generator.tech
*/
use crate::models;
use serde::{Deserialize, Serialize};
#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
pub struct Participants {
#[serde(rename = "participants")]
pub participants: Vec<String>,
}
impl Participants {
pub fn new(participants: Vec<String>) -> Participants {
Participants {
participants,
}
}
}

View File

@@ -0,0 +1,39 @@
/*
* Gamenight
*
* Api specification for a Gamenight server
*
* The version of the OpenAPI document: 1.0
* Contact: dennis@brentj.es
* Generated by: https://openapi-generator.tech
*/
use crate::models;
use serde::{Deserialize, Serialize};
#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
pub struct Registration {
#[serde(rename = "username")]
pub username: String,
#[serde(rename = "email")]
pub email: String,
#[serde(rename = "password")]
pub password: String,
#[serde(rename = "password_repeat")]
pub password_repeat: String,
#[serde(rename = "registration_token")]
pub registration_token: String,
}
impl Registration {
pub fn new(username: String, email: String, password: String, password_repeat: String, registration_token: String) -> Registration {
Registration {
username,
email,
password,
password_repeat,
registration_token,
}
}
}

View File

@@ -0,0 +1,30 @@
/*
* Gamenight
*
* Api specification for a Gamenight server
*
* The version of the OpenAPI document: 1.0
* Contact: dennis@brentj.es
* Generated by: https://openapi-generator.tech
*/
use crate::models;
use serde::{Deserialize, Serialize};
#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
pub struct RenameGameRequestBody {
#[serde(rename = "id")]
pub id: String,
#[serde(rename = "name")]
pub name: String,
}
impl RenameGameRequestBody {
pub fn new(id: String, name: String) -> RenameGameRequestBody {
RenameGameRequestBody {
id,
name,
}
}
}

View File

@@ -0,0 +1,27 @@
/*
* Gamenight
*
* Api specification for a Gamenight server
*
* The version of the OpenAPI document: 1.0
* Contact: dennis@brentj.es
* Generated by: https://openapi-generator.tech
*/
use crate::models;
use serde::{Deserialize, Serialize};
#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
pub struct Token {
#[serde(rename = "jwt_token", skip_serializing_if = "Option::is_none")]
pub jwt_token: Option<String>,
}
impl Token {
pub fn new() -> Token {
Token {
jwt_token: None,
}
}
}

View File

@@ -0,0 +1,33 @@
/*
* Gamenight
*
* Api specification for a Gamenight server
*
* The version of the OpenAPI document: 1.0
* Contact: dennis@brentj.es
* Generated by: https://openapi-generator.tech
*/
use crate::models;
use serde::{Deserialize, Serialize};
#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
pub struct User {
#[serde(rename = "id")]
pub id: String,
#[serde(rename = "username")]
pub username: String,
#[serde(rename = "email", skip_serializing_if = "Option::is_none")]
pub email: Option<String>,
}
impl User {
pub fn new(id: String, username: String) -> User {
User {
id,
username,
email: None,
}
}
}

View File

@@ -0,0 +1,27 @@
/*
* Gamenight
*
* Api specification for a Gamenight server
*
* The version of the OpenAPI document: 1.0
* Contact: dennis@brentj.es
* Generated by: https://openapi-generator.tech
*/
use crate::models;
use serde::{Deserialize, Serialize};
#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
pub struct UserId {
#[serde(rename = "user_id")]
pub user_id: String,
}
impl UserId {
pub fn new(user_id: String) -> UserId {
UserId {
user_id,
}
}
}

1
gamenight-cli/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
target

2019
gamenight-cli/Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

18
gamenight-cli/Cargo.toml Normal file
View File

@@ -0,0 +1,18 @@
[package]
name = "gamenight-cli"
version = "0.1.0"
edition = "2024"
[dependencies]
gamenight-api-client-rs = { path = "../gamenight-api-client-rs" }
tokio = { version = "1", features = ["full"] }
inquire = { version = "0.7.5", features = ["date", "editor"] }
async-trait = "0.1"
dyn-clone = "1.0"
chrono = "0.4"
uuid = { version = "1.3.0", features = ["serde", "v4"] }
jsonwebtoken = "9.3"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
clear_screen = "0.1"
futures = "0.3"

View File

@@ -0,0 +1,95 @@
use std::{env, fs::{self}, path::{Path, PathBuf}};
use serde::{Deserialize, Serialize};
#[derive(Debug)]
pub struct ConfigError(pub String);
impl From<serde_json::Error> for ConfigError {
fn from(value: serde_json::Error) -> Self {
Self(value.to_string())
}
}
impl From<std::io::Error> for ConfigError {
fn from(value: std::io::Error) -> Self {
Self(value.to_string())
}
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Instance {
pub name: String,
pub url: String,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
pub token: Option<String>
}
impl Instance {
pub fn new(name: String, url: String) -> Self {
Self {
name,
url,
token: None
}
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Config {
pub instances: Vec<Instance>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
pub last_instance: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
pub editor: Option<String>,
}
impl Config {
fn config_path() -> PathBuf {
let mut prefix = Path::new(&env::var("HOME").expect("HOME environment variable was not set")).join(".config");
if let Ok(config_home) = env::var("XDG_CONFIG_HOME") {
prefix = config_home.into();
}
prefix.join("gamenight-cli").join("config.json")
}
pub fn new() -> Config {
Config {
instances: vec![],
last_instance: None,
editor: None
}
}
pub fn load() -> Result<Config, ConfigError> {
let config_path = Self::config_path();
if !fs::exists(&config_path)? {
let config_error = ConfigError(format!("Cannot create parent directory for config file: {}", &config_path.display()).to_string());
fs::create_dir_all(config_path.parent().ok_or(config_error)?)?;
let config = Config::new();
fs::write(&config_path, serde_json::to_string_pretty(&config)?.as_bytes())?;
Ok(config)
}
else {
let config_string = fs::read_to_string(Self::config_path())?;
Ok(serde_json::from_str(&config_string)?)
}
}
pub fn save(gamenight_configuration: &Config) -> Result<(), ConfigError> {
let config_path = Self::config_path();
fs::write(&config_path, serde_json::to_string_pretty(gamenight_configuration)?.as_bytes())?;
Ok(())
}
}
impl Default for Config {
fn default() -> Self {
Self::new()
}
}

View File

@@ -0,0 +1,26 @@
use std::fmt::Display;
use gamenight_api_client_rs::models;
use uuid::Uuid;
#[derive(Clone)]
pub struct Game {
pub id: Uuid,
pub name: String
}
impl From<models::Game> for Game {
fn from(game: models::Game) -> Self {
Self {
id: Uuid::parse_str(&game.id).unwrap(),
name: game.name
}
}
}
impl Display for Game {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Name: {}", self.name)
}
}

View File

@@ -0,0 +1,27 @@
use std::fmt::{Display, Formatter};
use chrono::{DateTime, Local};
use uuid::Uuid;
use crate::domain::owned_games::OwnedGames;
use crate::domain::participants::Participants;
use crate::domain::user::User;
#[derive(Clone)]
pub struct Gamenight {
pub id: Uuid,
pub name: String,
pub start_time: DateTime<Local>,
pub organizer: User,
pub participants: Participants,
pub owned_games: OwnedGames,
}
impl Display for Gamenight {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
writeln!(f, "Name: {}", self.name)?;
writeln!(f, "Organizer: {}", self.organizer)?;
writeln!(f, "Start: {}", self.start_time.to_rfc3339())?;
writeln!(f, "Participants: {}", self.participants)?;
write!(f, "Owned games:\n{}", self.owned_games)?;
Ok(())
}
}

View File

@@ -0,0 +1,19 @@
use std::fmt::Display;
use chrono::{DateTime, Local};
use uuid::Uuid;
#[derive(Clone)]
pub struct GamenightSelectData {
pub id: Uuid,
pub name: String,
pub start_time: DateTime<Local>,
pub owner_id: Uuid,
}
impl Display for GamenightSelectData {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, r#"Name: {}
When: {}"#, self.name, self.start_time.format("%d-%m-%Y %H:%M"))
}
}

View File

@@ -0,0 +1,31 @@
use std::fmt::Display;
use gamenight_api_client_rs::models;
use uuid::Uuid;
#[derive(Clone)]
pub struct Location {
pub id: Uuid,
pub name: String,
pub address: Option<String>,
pub note: Option<String>,
}
impl From<models::Location> for Location {
fn from(location: models::Location) -> Self {
Self {
id: Uuid::parse_str(&location.id).unwrap(),
name: location.name,
address: location.address,
note: location.note
}
}
}
impl Display for Location {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, r#"name: {}
address: {}
note: {}"#, &self.name, &<Option<String> as Clone>::clone(&self.address).unwrap_or_default(), &<Option<String> as Clone>::clone(&self.note).unwrap_or_default())
}
}

View File

@@ -0,0 +1,25 @@
use std::fmt::{Display, Formatter};
use gamenight_api_client_rs::models::Location;
use uuid::Uuid;
use crate::flows::FlowError;
pub struct LocationSelectData {
pub id: Uuid,
pub name: String,
}
impl Display for LocationSelectData {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.name) }
}
impl TryFrom<&Location> for LocationSelectData {
type Error = FlowError;
fn try_from(value: &Location) -> Result<Self, Self::Error> {
let uuid = Uuid::parse_str(&value.id)?;
Ok(LocationSelectData {
id: uuid,
name: value.name.clone(),
})
}
}

View File

@@ -0,0 +1,9 @@
pub mod gamenight_select_data;
pub mod user;
pub mod config;
pub mod participants;
pub mod game;
pub mod owned_games;
pub mod location;
pub mod location_select_data;
pub mod gamenight;

View File

@@ -0,0 +1,18 @@
use std::{collections::HashMap, fmt::Display};
use crate::domain::game::Game;
#[derive(Clone)]
pub struct OwnedGames(pub HashMap<String, Vec<Game>>);
impl Display for OwnedGames {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for (k,v) in &self.0 {
write!(f, "{k}:\n")?;
for g in v {
write!(f, "\t{}\n", g.name)?;
}
}
Ok(())
}
}

View File

@@ -0,0 +1,13 @@
use std::fmt::Display;
use crate::domain::user::User;
#[derive(Clone)]
pub struct Participants(pub Vec<User>);
impl<'a> Display for Participants {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let string_list: Vec<String> = self.0.iter().map(|x| {format!("{x}")}).collect();
write!(f, "{}", string_list.join(", "))
}
}

View File

@@ -0,0 +1,28 @@
use std::fmt::Display;
use gamenight_api_client_rs::models;
use uuid::Uuid;
use crate::flows::FlowError;
#[derive(Clone)]
pub struct User {
pub id: Uuid,
pub username: String
}
impl Display for User {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.username)
}
}
impl TryFrom<models::User> for User {
type Error = FlowError;
fn try_from(user: models::User) -> Result<Self, Self::Error> {
Ok(Self {
username: user.username,
id: Uuid::parse_str(&user.id)?
})
}
}

View File

@@ -0,0 +1,40 @@
use std::fmt::Display;
use super::*;
use crate::flows::own::Own;
use async_trait::async_trait;
use gamenight_api_client_rs::{apis::default_api::game_post, models::AddGameRequestBody};
use inquire::Text;
#[derive(Clone)]
pub struct AddGame {
}
impl AddGame {
pub fn new() -> Self {
Self {}
}
}
#[async_trait]
impl<'a> Flow<'a> for AddGame {
async fn run(&self, state: &'a mut GamenightState) -> FlowResult<'a> {
if let Some(name) = Text::new("What is the game you want to add").prompt_skippable()? {
let add_game_request = AddGameRequestBody {
name
};
let game_id_response = game_post(&state.api_configuration, Some(add_game_request)).await?;
let own_flow = Own::new(Uuid::parse_str(&game_id_response.game_id)?);
return self.continue_with(state, &own_flow).await;
}
Ok((FlowOutcome::Cancelled, state))
}
}
impl Display for AddGame {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Add Game")
}
}

View File

@@ -0,0 +1,51 @@
use gamenight_api_client_rs::{apis::default_api::post_gamenight, models};
use inquire::{CustomType, DateSelect, Text};
use chrono::{self, Local, NaiveTime};
use super::*;
#[derive(Clone)]
pub struct AddGamenight {
}
impl AddGamenight {
pub fn new() -> Self {
Self {}
}
}
#[async_trait]
impl<'a> Flow<'a> for AddGamenight {
async fn run(&self, state: &'a mut GamenightState) -> FlowResult<'a> {
if let Some(name) = Text::new("What should we call your gamenight").prompt_skippable()? {
if let Some(naive_date) = DateSelect::new("When is your gamenight").prompt_skippable()? {
if let Some(naive_time) = CustomType::<NaiveTime>::new("At What time").prompt_skippable()? {
let datetime = naive_date
.and_time(naive_time)
.and_local_timezone(Local)
.earliest()
.unwrap()
.to_utc()
.to_rfc3339();
let add_gamenight = models::AddGamenightRequestBody::new(name, datetime);
post_gamenight(&state.api_configuration, Some(add_gamenight)).await?;
clear_screen::clear();
return Ok((FlowOutcome::Successful, state))
}
}
}
Ok((FlowOutcome::Cancelled, state))
}
}
impl Display for AddGamenight {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Add Gamenight.")
}
}

View File

@@ -0,0 +1,65 @@
use std::{ffi::OsStr, fmt::Display};
use async_trait::async_trait;
use gamenight_api_client_rs::{apis::default_api::{location_authorize_post, location_post}, models::{authorize_location_request_body::Op::Grant, AddLocationRequestBody, AuthorizeLocationRequestBody}};
use inquire::{Editor, Text};
use super::*;
#[derive(Clone)]
pub struct AddLocation {
}
impl AddLocation {
pub fn new() -> Self {
Self {}
}
}
#[async_trait]
impl<'a> Flow<'a> for AddLocation {
async fn run(&self, state: &'a mut GamenightState) -> FlowResult<'a> {
if let Some(name) = Text::new("What is the name of the location you want to add?").prompt_skippable()? {
let address;
let note;
{
address = Text::new("Optional: What is the address?").prompt_skippable()?;
let mut editor_prompt = Editor::new("What is the name of the location you want to add?");
let editor_command;
if let Some(editor) = &state.gamenight_configuration.editor {
editor_command = editor.clone();
editor_prompt = editor_prompt.with_editor_command(OsStr::new(&editor_command))
}
note = editor_prompt.prompt_skippable()?;
}
let add_location_request = AddLocationRequestBody {
name,
address,
note
};
let location_id = location_post(&state.api_configuration, Some(add_location_request)).await?;
let add_authorize_request = AuthorizeLocationRequestBody {
location_id: location_id.location_id.to_string(),
user_id: state.get_user_id()?.to_string(),
op: Grant
};
location_authorize_post(&state.api_configuration, Some(add_authorize_request)).await?;
}
Ok((FlowOutcome::Cancelled, state))
}
}
impl Display for AddLocation {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Add Location")
}
}

Some files were not shown because too many files have changed in this diff Show More