Compare commits

...

55 Commits

Author SHA1 Message Date
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
148 changed files with 8045 additions and 29900 deletions

3
.gitignore vendored
View File

@ -1 +1,4 @@
/target/
**/*.rs.bk
Cargo.lock
.vscode

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

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

File diff suppressed because it is too large Load Diff

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

@ -0,0 +1,20 @@
[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 = "9.3"
validator = { version = "0.20", features = ["derive"] }
rand_core = { version = "0.9" }
env_logger = "0.11"
tracing-actix-web = "0.7"

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

@ -0,0 +1,30 @@
use std::{fs::{exists, read_dir, remove_dir_all, File}, io::Write, process::Command};
fn main() {
println!("cargo::rerun-if-changed=gamenight-api.yaml");
if exists("src/models").unwrap() {
remove_dir_all("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,447 @@
openapi: 3.0.0
info:
title: Gamenight
version: '1.0'
contact:
name: Dennis Brentjes
email: dennis@brentj.es
url: 'https://brentj.es'
description: Api specifaction 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: []
/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':
description: "OK"
'401':
$ref: '#/components/responses/FailureResponse'
'422':
$ref: '#/components/responses/FailureResponse'
requestBody:
$ref: '#/components/requestBodies/AddGameRequest'
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
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
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'
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'
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'
securitySchemes:
JWT-Auth:
type: http
scheme: bearer
bearerFormat: JWT
description: ''

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

@ -0,0 +1,57 @@
#[allow(unused_imports)]
pub mod models;
pub mod request;
use actix_cors::Cors;
use actix_web::middleware::Logger;
use actix_web::HttpServer;
use actix_web::App;
use actix_web::http;
use actix_web::web;
use request::{*, login, register, gamenights};
use tracing_actix_web::TracingLogger;
use gamenight_database::*;
#[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("info"));
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_user_unauthenticated)
.service(post_join_gamenight)
.service(post_leave_gamenight)
.service(get_get_participants)
.service(get_games)
.service(post_game)
})
.bind(("::1", 8080))?
.run()
.await
}

View File

@ -0,0 +1,77 @@
use std::future::{Ready, ready};
use actix_web::{FromRequest, http, HttpRequest, dev::Payload, web::Data};
use chrono::Utc;
use jsonwebtoken::{encode, Header, EncodingKey, decode, DecodingKey, Validation};
use serde::{Serialize, Deserialize};
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);
// pub struct AuthUser {
// pub id: Uuid,
// pub username: String,
// pub email: String,
// pub role: Role,
// }
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,91 @@
use std::fmt::{Display, Formatter, Result};
use actix_web::{ResponseError, error::BlockingError, HttpResponse, http::{header::ContentType, StatusCode}};
use serde::{Serialize, Deserialize};
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,39 @@
use actix_web::{get, http::header::ContentType, post, web, HttpResponse, Responder};
use gamenight_database::{game::insert_game, DbPool, GetConnection};
use uuid::Uuid;
use crate::{models::{add_game_request_body::AddGameRequestBody, game::Game}, 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
}
}
}
#[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();
insert_game(&mut conn, game_data.0.into())?;
Ok(HttpResponse::Ok())
}

View File

@ -0,0 +1,70 @@
use actix_web::{get, web, Responder, http::header::ContentType, HttpResponse, post};
use chrono::{DateTime, ParseError};
use uuid::Uuid;
use gamenight_database::{gamenight, DbPool, GetConnection};
use crate::{models::{add_gamenight_request_body::AddGamenightRequestBody, gamenight::Gamenight, get_gamenight_request_body::GetGamenightRequestBody}, request::authorization::AuthUser};
use crate::request::error::ApiError;
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
})
}
}
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,37 @@
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
};
println!("{:?}", participant);
let x = delete_gamenight_participant(&mut conn, participant)?;
println!("Amount of deleted rows: {:?}", x);
Ok(x)
}).await??;
Ok(HttpResponse::Ok())
}

View File

@ -0,0 +1,22 @@
mod user_handlers;
mod gamenight_handlers;
mod error;
mod authorization;
mod join_gamenight;
mod participant_handlers;
mod game;
pub use user_handlers::login;
pub use user_handlers::refresh;
pub use user_handlers::register;
pub use gamenight_handlers::gamenights;
pub use gamenight_handlers::gamenight_post;
pub use gamenight_handlers::gamenight_get;
pub use user_handlers::get_user;
pub use user_handlers::get_user_unauthenticated;
pub use join_gamenight::post_join_gamenight;
pub use join_gamenight::post_leave_gamenight;
pub use participant_handlers::get_get_participants;
pub use game::get_games;
pub use game::post_game;

View File

@ -0,0 +1,17 @@
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,165 @@
use actix_web::http::header::ContentType;
use actix_web::{get, post, web, HttpResponse, Responder};
use gamenight_database::user::{count_users_with_email, count_users_with_username};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use validator::{Validate, ValidateArgs, ValidationError};
use crate::models::login::Login;
use crate::models::registration::Registration;
use crate::models::token::Token;
use crate::models::user::User;
use crate::models::user_id::UserId;
use crate::request::error::ApiError;
use crate::request::authorization::get_token;
use serde_json;
use gamenight_database::{DbPool, GetConnection};
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())
}

4
backend/.gitignore vendored
View File

@ -1,4 +0,0 @@
/target
.vscode
App.toml
*.sqlite

View File

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

View File

@ -1,24 +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"
password-hash = "0.4"
argon2 = "0.4"
rand_core = { version = "0.6", features = ["std"] }
diesel-derive-enum = { version = "1.1", features = ["sqlite"] }
jsonwebtoken = "8.1"
validator = { version = "0.14", features = ["derive"] }

View File

@ -1,12 +0,0 @@
-- Your SQL goes here
CREATE TABLE gamenight (
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
game text TEXT NOT NULL,
datetime TEXT NOT NULL
);
CREATE TABLE known_games (
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
game TEXT UNIQUE NOT NULL
);

View File

@ -1,12 +0,0 @@
CREATE TABLE user (
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL,
email TEXT UNIQUE NOT NULL,
role TEXT NOT NULL
);
CREATE TABLE pwd (
user_id INTEGER NOT NULL PRIMARY KEY,
password TEXT NOT NULL,
FOREIGN KEY (user_id) REFERENCES user(id) ON DELETE CASCADE
);

View File

@ -1,3 +0,0 @@
echo $JWT
curl -X GET -H "Authorization: Bearer ${JWT}" localhost:8000/api/gamenights

View File

@ -1 +0,0 @@
curl -X POST -H "Content-Type: application/json" -d '{"username": "a", "password": "c"}' localhost:8000/api/login

View File

@ -1 +0,0 @@
curl -X POST -H "Content-Type: application/json" -d '{"username": "roflin", "email": "user@example.com", "password": "oreokoekje123", "password_repeat": "oreokoekje123"}' localhost:8000/api/register

View File

@ -1,195 +0,0 @@
use crate::schema;
use crate::schema::DbConn;
use crate::AppConfig;
use chrono::Utc;
use jsonwebtoken::decode;
use jsonwebtoken::encode;
use jsonwebtoken::DecodingKey;
use jsonwebtoken::Validation;
use jsonwebtoken::{EncodingKey, Header};
use rocket::http::Status;
use rocket::request::Outcome;
use rocket::request::{FromRequest, Request};
use rocket::serde::json::{json, Json, Value};
use rocket::State;
use serde::{Deserialize, Serialize};
use std::borrow::Cow;
use validator::ValidateArgs;
#[derive(Debug, Responder)]
pub enum ApiResponseVariant {
Status(Status),
// Redirect(Redirect),
Value(Value),
// Flash(Flash<Redirect>)
}
#[derive(Serialize, Deserialize, Debug)]
struct ApiResponse {
result: Cow<'static, str>,
#[serde(skip_serializing_if = "Option::is_none")]
message: Option<Cow<'static, str>>,
#[serde(skip_serializing_if = "Option::is_none")]
jwt: Option<Cow<'static, str>>,
}
impl ApiResponse {
const SUCCES_RESULT: Cow<'static, str> = Cow::Borrowed("Ok");
const FAILURE_RESULT: Cow<'static, str> = Cow::Borrowed("Failure");
const SUCCES: Self = Self {
result: Self::SUCCES_RESULT,
message: None,
jwt: None,
};
fn error(message: String) -> Self {
Self {
result: Self::FAILURE_RESULT,
message: Some(Cow::Owned(message)),
jwt: None,
}
}
fn login_response(jwt: String) -> Self {
Self {
result: Self::SUCCES_RESULT,
message: None,
jwt: Some(Cow::Owned(jwt)),
}
}
}
#[derive(Debug)]
pub enum ApiError {
RequestError(String),
}
const AUTH_HEADER: &str = "Authorization";
const BEARER: &str = "Bearer ";
#[rocket::async_trait]
impl<'r> FromRequest<'r> for schema::User {
type Error = ApiError;
async fn from_request(req: &'r Request<'_>) -> Outcome<Self, Self::Error> {
let header = match req.headers().get_one(AUTH_HEADER) {
Some(header) => header,
None => {
return Outcome::Forward(())
}
};
if !header.starts_with(BEARER) {
return Outcome::Forward(());
};
let app_config = req.guard::<&State<AppConfig>>().await.unwrap().inner();
let jwt = header.trim_start_matches(BEARER).to_owned();
let token = match decode::<Claims>(
&jwt,
&DecodingKey::from_secret(app_config.jwt_secret.as_bytes()),
&Validation::default(),
) {
Ok(token) => token,
Err(_) => {
return Outcome::Forward(())
}
};
let id = token.claims.uid;
let conn = req.guard::<DbConn>().await.unwrap();
return Outcome::Success(schema::get_user(conn, id).await);
}
}
#[get("/gamenights")]
pub async fn gamenights(conn: DbConn, _user: schema::User) -> ApiResponseVariant {
let gamenights = schema::get_all_gamenights(conn).await;
ApiResponseVariant::Value(json!(gamenights))
}
#[get("/gamenights", rank = 2)]
pub async fn gamenights_unauthorized() -> ApiResponseVariant {
ApiResponseVariant::Status(Status::Unauthorized)
}
#[post("/gamenight", format = "application/json", data = "<gamenight_json>")]
pub async fn gamenight_post_json(
conn: DbConn,
user: Option<schema::User>,
gamenight_json: Json<schema::GameNightNoId>,
) -> ApiResponseVariant {
if user.is_some() {
schema::insert_gamenight(conn, gamenight_json.into_inner()).await;
ApiResponseVariant::Value(json!(ApiResponse::SUCCES))
} else {
ApiResponseVariant::Status(Status::Unauthorized)
}
}
#[post("/register", format = "application/json", data = "<register_json>")]
pub async fn register_post_json(
conn: DbConn,
register_json: Json<schema::Register>,
) -> ApiResponseVariant {
let register = register_json.into_inner();
let register_clone = register.clone();
match conn
.run(move |c| register_clone.validate_args((c, c)))
.await
{
Ok(()) => (),
Err(error) => {
return ApiResponseVariant::Value(json!(ApiResponse::error(error.to_string())))
}
}
match schema::insert_user(conn, register).await {
Ok(_) => ApiResponseVariant::Value(json!(ApiResponse::SUCCES)),
Err(err) => ApiResponseVariant::Value(json!(ApiResponse::error(err.to_string()))),
}
}
#[derive(Debug, Serialize, Deserialize)]
struct Claims {
exp: i64,
uid: i32,
role: schema::Role,
}
#[post("/login", format = "application/json", data = "<login_json>")]
pub async fn login_post_json(
conn: DbConn,
config: &State<AppConfig>,
login_json: Json<schema::Login>,
) -> ApiResponseVariant {
match schema::login(conn, login_json.into_inner()).await {
Err(err) => ApiResponseVariant::Value(json!(ApiResponse::error(err.to_string()))),
Ok(login_result) => {
if !login_result.result {
return ApiResponseVariant::Value(json!(ApiResponse::error(String::from(
"username and password didn't match"
))));
}
let my_claims = Claims {
exp: Utc::now().timestamp() + chrono::Duration::days(7).num_seconds(),
uid: login_result.id.unwrap(),
role: login_result.role.unwrap(),
};
let secret = &config.inner().jwt_secret;
match encode(
&Header::default(),
&my_claims,
&EncodingKey::from_secret(secret.as_bytes()),
) {
Ok(token) => ApiResponseVariant::Value(json!(ApiResponse::login_response(token))),
Err(error) => {
ApiResponseVariant::Value(json!(ApiResponse::error(error.to_string())))
}
}
}
}
}

View File

@ -1,69 +0,0 @@
#[macro_use]
extern crate rocket;
#[macro_use]
extern crate diesel_migrations;
#[macro_use]
extern crate diesel;
use rocket::{
fairing::AdHoc,
figment::{
providers::{Env, Format, Serialized, Toml},
Figment, Profile,
},
};
use rocket_dyn_templates::Template;
use serde::{Deserialize, Serialize};
mod api;
pub mod schema;
mod site;
#[derive(Debug, Deserialize, Serialize)]
pub struct AppConfig {
jwt_secret: String,
}
impl Default for AppConfig {
fn default() -> AppConfig {
AppConfig {
jwt_secret: String::from("secret"),
}
}
}
#[launch]
fn rocket() -> _ {
let figment = Figment::from(rocket::Config::default())
.merge(Serialized::defaults(AppConfig::default()))
.merge(Toml::file("App.toml").nested())
.merge(Env::prefixed("APP_").global())
.select(Profile::from_env_or("APP_PROFILE", "default"));
let rocket = rocket::custom(figment)
.attach(schema::DbConn::fairing())
.attach(Template::fairing())
.attach(AdHoc::on_ignite("Run Migrations", schema::run_migrations))
.attach(AdHoc::config::<AppConfig>())
.mount(
"/",
routes![
site::index,
site::gamenights,
site::add_game_night,
site::register
],
)
.mount(
"/api",
routes![
api::gamenights,
api::gamenights_unauthorized,
api::gamenight_post_json,
api::register_post_json,
api::login_post_json
],
);
rocket
}

View File

@ -1,319 +0,0 @@
use crate::diesel::BoolExpressionMethods;
use crate::diesel::Connection;
use crate::diesel::ExpressionMethods;
use crate::diesel::QueryDsl;
use argon2::password_hash::SaltString;
use argon2::PasswordHash;
use argon2::PasswordVerifier;
use argon2::{
password_hash::{rand_core::OsRng, PasswordHasher},
Argon2,
};
use diesel::dsl::count;
use diesel::RunQueryDsl;
use diesel_derive_enum::DbEnum;
use rocket::{Build, Rocket};
use rocket_sync_db_pools::database;
use serde::{Deserialize, Serialize};
use std::ops::Deref;
use validator::{Validate, ValidationError};
#[database("gamenight_database")]
pub struct DbConn(diesel::SqliteConnection);
impl Deref for DbConn {
type Target = rocket_sync_db_pools::Connection<DbConn, diesel::SqliteConnection>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
table! {
gamenight (id) {
id -> Integer,
game -> Text,
datetime -> Text,
}
}
table! {
known_games (game) {
id -> Integer,
game -> Text,
}
}
table! {
use diesel::sql_types::Integer;
use diesel::sql_types::Text;
use super::RoleMapping;
user(id) {
id -> Integer,
username -> Text,
email -> Text,
role -> RoleMapping,
}
}
table! {
pwd(user_id) {
user_id -> Integer,
password -> Text,
}
}
allow_tables_to_appear_in_same_query!(gamenight, known_games,);
pub enum DatabaseError {
Hash(password_hash::Error),
Query(String),
}
impl std::fmt::Display for DatabaseError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
match self {
DatabaseError::Hash(err) => write!(f, "{}", err),
DatabaseError::Query(err) => write!(f, "{}", err),
}
}
}
pub async fn get_all_gamenights(conn: DbConn) -> Vec<GameNight> {
conn.run(|c| gamenight::table.load::<GameNight>(c).unwrap())
.await
}
pub async fn insert_gamenight(conn: DbConn, new_gamenight: GameNightNoId) -> () {
conn.run(|c| {
diesel::insert_into(gamenight::table)
.values(new_gamenight)
.execute(c)
.unwrap()
})
.await;
}
pub async fn insert_user(conn: DbConn, new_user: Register) -> Result<(), DatabaseError> {
let salt = SaltString::generate(&mut OsRng);
let argon2 = Argon2::default();
let password_hash = match argon2.hash_password(new_user.password.as_bytes(), &salt) {
Ok(hash) => hash.to_string(),
Err(error) => return Err(DatabaseError::Hash(error)),
};
let user_insert_result = conn
.run(move |c| {
c.transaction(|| {
diesel::insert_into(user::table)
.values((
user::username.eq(&new_user.username),
user::email.eq(&new_user.email),
user::role.eq(Role::User),
))
.execute(c)?;
let ids: Vec<i32> = match user::table
.filter(
user::username
.eq(&new_user.username)
.and(user::email.eq(&new_user.email)),
)
.select(user::id)
.get_results(c)
{
Ok(id) => id,
Err(e) => return Err(e),
};
diesel::insert_into(pwd::table)
.values((pwd::user_id.eq(ids[0]), pwd::password.eq(&password_hash)))
.execute(c)
})
})
.await;
match user_insert_result {
Err(e) => Err(DatabaseError::Query(e.to_string())),
_ => Ok(()),
}
}
pub async fn login(conn: DbConn, login: Login) -> Result<LoginResult, DatabaseError> {
conn.run(move |c| -> Result<LoginResult, DatabaseError> {
let id: i32 = match user::table
.filter(user::username.eq(&login.username))
.or_filter(user::email.eq(&login.username))
.select(user::id)
.first(c)
{
Ok(id) => id,
Err(error) => return Err(DatabaseError::Query(error.to_string())),
};
let pwd: String = match pwd::table
.filter(pwd::user_id.eq(id))
.select(pwd::password)
.first(c)
{
Ok(pwd) => pwd,
Err(error) => return Err(DatabaseError::Query(error.to_string())),
};
let parsed_hash = match PasswordHash::new(&pwd) {
Ok(hash) => hash,
Err(error) => return Err(DatabaseError::Hash(error)),
};
if Argon2::default()
.verify_password(&login.password.as_bytes(), &parsed_hash)
.is_ok()
{
let role: Role = match user::table
.filter(user::id.eq(id))
.select(user::role)
.first(c)
{
Ok(role) => role,
Err(error) => return Err(DatabaseError::Query(error.to_string())),
};
Ok(LoginResult {
result: true,
id: Some(id),
role: Some(role),
})
} else {
Ok(LoginResult {
result: false,
id: None,
role: None,
})
}
})
.await
}
pub async fn get_user(conn: DbConn, id: i32) -> User {
conn.run(move |c| user::table.filter(user::id.eq(id)).first(c).unwrap())
.await
}
pub fn unique_username(
username: &String,
conn: &diesel::SqliteConnection,
) -> Result<(), ValidationError> {
match user::table
.select(count(user::username))
.filter(user::username.eq(username))
.execute(conn)
{
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,
conn: &diesel::SqliteConnection,
) -> Result<(), ValidationError> {
match user::table
.select(count(user::email))
.filter(user::email.eq(email))
.execute(conn)
{
Ok(0) => Ok(()),
Ok(_) => Err(ValidationError::new("email already exists")),
Err(_) => Err(ValidationError::new(
"Database error while validating email",
)),
}
}
pub async fn run_migrations(rocket: Rocket<Build>) -> Rocket<Build> {
// This macro from `diesel_migrations` defines an `embedded_migrations`
// module containing a function named `run`. This allows the example to be
// run and tested without any outside setup of the database.
embed_migrations!();
let conn = DbConn::get_one(&rocket).await.expect("database connection");
conn.run(|c| embedded_migrations::run(c))
.await
.expect("can run migrations");
rocket
}
#[derive(Debug, Serialize, Deserialize, DbEnum, Clone)]
pub enum Role {
Admin,
User,
}
#[derive(Serialize, Deserialize, Debug, Insertable, Queryable)]
#[table_name = "user"]
pub struct User {
pub id: i32,
pub username: String,
pub email: String,
pub role: Role,
}
#[derive(Serialize, Deserialize, Debug, FromForm, Insertable)]
#[table_name = "known_games"]
pub struct GameNoId {
pub game: String,
}
#[derive(Serialize, Deserialize, Debug, FromForm, Queryable)]
pub struct Game {
pub id: i32,
pub game: String,
}
#[derive(Serialize, Deserialize, Debug, FromForm, Insertable)]
#[table_name = "gamenight"]
pub struct GameNightNoId {
pub game: String,
pub datetime: String,
}
#[derive(Serialize, Deserialize, Debug, FromForm, Queryable)]
pub struct GameNight {
pub id: i32,
pub game: String,
pub datetime: String,
}
#[derive(Serialize, Deserialize, Debug, Validate, Clone)]
pub struct Register {
#[validate(
length(min = 1),
custom(function = "unique_username", arg = "&'v_a diesel::SqliteConnection")
)]
pub username: String,
#[validate(
email,
custom(function = "unique_email", arg = "&'v_a diesel::SqliteConnection")
)]
pub email: String,
#[validate(length(min = 10), must_match = "password_repeat")]
pub password: String,
pub password_repeat: String,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Login {
pub username: String,
pub password: String,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct LoginResult {
pub result: bool,
pub id: Option<i32>,
pub role: Option<Role>,
}

View File

@ -1,90 +0,0 @@
use crate::schema;
use rocket::request::FlashMessage;
use rocket::response::Redirect;
use rocket_dyn_templates::Template;
use serde::{Deserialize, Serialize};
use std::borrow::Cow;
#[derive(Serialize, Deserialize, Debug)]
struct FlashData {
has_data: bool,
kind: Cow<'static, str>,
message: Cow<'static, str>,
}
impl FlashData {
const EMPTY: Self = Self {
has_data: false,
message: Cow::Borrowed(""),
kind: Cow::Borrowed(""),
};
}
#[derive(Serialize, Deserialize, Debug)]
struct GameNightsData {
gamenights: Vec<schema::GameNight>,
flash: FlashData,
}
#[get("/gamenights")]
pub async fn gamenights(conn: schema::DbConn) -> Template {
let gamenights = schema::get_all_gamenights(conn).await;
let data = GameNightsData {
gamenights: gamenights,
flash: FlashData::EMPTY,
};
Template::render("gamenights", &data)
}
#[get("/")]
pub async fn index() -> Redirect {
Redirect::to(uri!(gamenights))
}
#[derive(Serialize, Deserialize, Debug)]
struct GameNightAddData {
post_url: String,
flash: FlashData,
}
#[get("/gamenight/add")]
pub async fn add_game_night(flash: Option<FlashMessage<'_>>) -> Template {
let flash_data = match flash {
None => FlashData::EMPTY,
Some(flash) => FlashData {
has_data: true,
message: Cow::Owned(flash.message().to_string()),
kind: Cow::Owned(flash.kind().to_string()),
},
};
let data = GameNightAddData {
post_url: "/api/gamenight".to_string(),
flash: flash_data,
};
Template::render("gamenight_add", &data)
}
#[derive(Serialize, Deserialize, Debug)]
struct RegisterData {
flash: FlashData,
}
#[get("/register")]
pub async fn register(flash: Option<FlashMessage<'_>>) -> Template {
let flash_data = match flash {
None => FlashData::EMPTY,
Some(flash) => FlashData {
has_data: true,
message: Cow::Owned(flash.message().to_string()),
kind: Cow::Owned(flash.kind().to_string()),
},
};
let data = RegisterData { flash: flash_data };
Template::render("register", &data)
}

View File

@ -1,5 +0,0 @@
{{#if has_data}}
<div>
<p>{{kind}}: {{message}}</p>
</div>
{{/if}}

View File

@ -1,16 +0,0 @@
<html>
<head>
</head>
<body>
{{> flash flash }}
<form action="{{post_url}}" method="post">
<label for="game">Game:</label><br>
<input type="text" id="game" name="game"><br>
<label for="datetime">Wanneer:</label><br>
<input type="text" id="datetime" name="datetime">
<input type="submit" value="Submit">
</form>
</body>
</html>

View File

@ -1,14 +0,0 @@
<html>
<head>
</head>
<body>
{{> flash flash }}
{{#each gamenights}}
<div>
<span>game: {{this.game}}</span>
<span>when: {{this.datetime}}</span>
</div>
{{/each}}
</body>
</html>

View File

@ -1,19 +0,0 @@
<html>
<head>
</head>
<body>
{{> flash flash }}
<form action="/api/register" method="post">
<label for="username">Username:</label><br>
<input type="text" id="username" name="username" required><br>
<label for="email">Email:</label><br>
<input type="text" id="email" name="email" required><br>
<label for="password">Password:</label><br>
<input type="password" id="password" name="password" required><br>
<label for="password_repeat">Repeat password:</label><br>
<input type="password" id="password_repeat" name="password_repeat" required><br>
<input type="submit">
</form>
</body>
</html>

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"

23
frontend/.gitignore vendored
View File

@ -1,23 +0,0 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*

View File

@ -1,70 +0,0 @@
# Getting Started with Create React App
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
## Available Scripts
In the project directory, you can run:
### `npm start`
Runs the app in the development mode.\
Open [http://localhost:3000](http://localhost:3000) to view it in your browser.
The page will reload when you make changes.\
You may also see any lint errors in the console.
### `npm test`
Launches the test runner in the interactive watch mode.\
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
### `npm run build`
Builds the app for production to the `build` folder.\
It correctly bundles React in production mode and optimizes the build for the best performance.
The build is minified and the filenames include the hashes.\
Your app is ready to be deployed!
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
### `npm run eject`
**Note: this is a one-way operation. Once you `eject`, you can't go back!**
If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own.
You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it.
## Learn More
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
To learn React, check out the [React documentation](https://reactjs.org/).
### Code Splitting
This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting)
### Analyzing the Bundle Size
This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size)
### Making a Progressive Web App
This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app)
### Advanced Configuration
This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration)
### Deployment
This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment)
### `npm run build` fails to minify
This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify)

27260
frontend/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,38 +0,0 @@
{
"name": "frontend",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^13.0.1",
"@testing-library/user-event": "^13.5.0",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

View File

@ -1,43 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

View File

@ -1,25 +0,0 @@
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}

View File

@ -1,3 +0,0 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:

View File

@ -1,38 +0,0 @@
.App {
text-align: center;
}
.App-logo {
height: 40vmin;
pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
}
.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}
.App-link {
color: #61dafb;
}
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}

View File

@ -1,25 +0,0 @@
import logo from './logo.svg';
import './App.css';
function App() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
);
}
export default App;

View File

@ -1,8 +0,0 @@
import { render, screen } from '@testing-library/react';
import App from './App';
test('renders learn react link', () => {
render(<App />);
const linkElement = screen.getByText(/learn react/i);
expect(linkElement).toBeInTheDocument();
});

View File

@ -1,13 +0,0 @@
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}

View File

@ -1,17 +0,0 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3"><g fill="#61DAFB"><path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/><circle cx="420.9" cy="296.5" r="45.7"/><path d="M520.5 78.1z"/></g></svg>

Before

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -1,13 +0,0 @@
const reportWebVitals = onPerfEntry => {
if (onPerfEntry && onPerfEntry instanceof Function) {
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
getCLS(onPerfEntry);
getFID(onPerfEntry);
getFCP(onPerfEntry);
getLCP(onPerfEntry);
getTTFB(onPerfEntry);
});
}
};
export default reportWebVitals;

View File

@ -1,5 +0,0 @@
// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom';

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,39 @@
.gitignore
.travis.yml
Cargo.toml
README.md
docs/AddGameRequestBody.md
docs/AddGamenightRequestBody.md
docs/DefaultApi.md
docs/Failure.md
docs/Game.md
docs/GameId.md
docs/Gamenight.md
docs/GamenightId.md
docs/GetGamenightRequestBody.md
docs/Login.md
docs/Participants.md
docs/Registration.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/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/login.rs
src/models/mod.rs
src/models/participants.rs
src/models/registration.rs
src/models/token.rs
src/models/user.rs
src/models/user_id.rs

View File

@ -0,0 +1 @@
7.13.0

View File

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

View File

@ -0,0 +1,14 @@
[package]
name = "gamenight-api-client-rs"
version = "0.1.0"
authors = ["dennis@brentj.es"]
description = "Api specifaction 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"] }

View File

@ -0,0 +1,72 @@
# Rust API client for gamenight-api-client-rs
Api specifaction 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.13.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* | [**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* | [**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* | [**user_get**](docs/DefaultApi.md#user_get) | **GET** /user |
## Documentation For Models
- [AddGameRequestBody](docs/AddGameRequestBody.md)
- [AddGamenightRequestBody](docs/AddGamenightRequestBody.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)
- [Login](docs/Login.md)
- [Participants](docs/Participants.md)
- [Registration](docs/Registration.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,390 @@
# \DefaultApi
All URIs are relative to *http://localhost:8080*
Method | HTTP request | Description
------------- | ------------- | -------------
[**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 |
[**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 |
[**user_get**](DefaultApi.md#user_get) | **GET** /user |
## 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
> game_post(add_game_request_body)
### Parameters
Name | Type | Description | Required | Notes
------------- | ------------- | ------------- | ------------- | -------------
**add_game_request_body** | Option<[**AddGameRequestBody**](AddGameRequestBody.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)
## 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)
## 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)
## 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)

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,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,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,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 specifaction 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,
}
}
}

View File

@ -0,0 +1,593 @@
/*
* 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 reqwest;
use serde::{Deserialize, Serialize, de::Error as _};
use crate::{apis::ResponseContent, models};
use super::{Error, configuration, ContentType};
/// struct for typed errors of method [`game_get`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum GameGetError {
Status401(models::Failure),
Status422(models::Failure),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`game_post`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum GamePostError {
Status401(models::Failure),
Status422(models::Failure),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`games_get`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum GamesGetError {
Status401(models::Failure),
Status422(models::Failure),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`get_gamenight`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum GetGamenightError {
Status401(models::Failure),
Status422(models::Failure),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`get_gamenights`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum GetGamenightsError {
Status400(models::Failure),
Status401(models::Failure),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`get_token`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum GetTokenError {
Status401(models::Failure),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`join_post`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum JoinPostError {
Status401(models::Failure),
Status422(models::Failure),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`leave_post`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum LeavePostError {
Status401(models::Failure),
Status422(models::Failure),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`participants_get`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ParticipantsGetError {
Status400(models::Failure),
Status401(models::Failure),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`post_gamenight`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum PostGamenightError {
Status401(models::Failure),
Status422(models::Failure),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`post_register`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum PostRegisterError {
Status422(models::Failure),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`post_token`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum PostTokenError {
Status401(models::Failure),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`user_get`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum UserGetError {
Status401(models::Failure),
Status404(models::Failure),
Status422(models::Failure),
UnknownValue(serde_json::Value),
}
pub async fn game_get(configuration: &configuration::Configuration, game_id: Option<models::GameId>) -> Result<models::Game, Error<GameGetError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_game_id = game_id;
let uri_str = format!("{}/game", configuration.base_path);
let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
if let Some(ref user_agent) = configuration.user_agent {
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
}
if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned());
};
req_builder = req_builder.json(&p_game_id);
let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?;
let status = resp.status();
let content_type = resp
.headers()
.get("content-type")
.and_then(|v| v.to_str().ok())
.unwrap_or("application/octet-stream");
let content_type = super::ContentType::from(content_type);
if !status.is_client_error() && !status.is_server_error() {
let content = resp.text().await?;
match content_type {
ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::Game`"))),
ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::Game`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<GameGetError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent { status, content, entity }))
}
}
pub async fn game_post(configuration: &configuration::Configuration, add_game_request_body: Option<models::AddGameRequestBody>) -> Result<(), Error<GamePostError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_add_game_request_body = add_game_request_body;
let uri_str = format!("{}/game", configuration.base_path);
let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str);
if let Some(ref user_agent) = configuration.user_agent {
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
}
if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned());
};
req_builder = req_builder.json(&p_add_game_request_body);
let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?;
let status = resp.status();
if !status.is_client_error() && !status.is_server_error() {
Ok(())
} else {
let content = resp.text().await?;
let entity: Option<GamePostError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent { status, content, entity }))
}
}
pub async fn games_get(configuration: &configuration::Configuration, ) -> Result<Vec<models::Game>, Error<GamesGetError>> {
let uri_str = format!("{}/games", configuration.base_path);
let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
if let Some(ref user_agent) = configuration.user_agent {
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
}
if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned());
};
let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?;
let status = resp.status();
let content_type = resp
.headers()
.get("content-type")
.and_then(|v| v.to_str().ok())
.unwrap_or("application/octet-stream");
let content_type = super::ContentType::from(content_type);
if !status.is_client_error() && !status.is_server_error() {
let content = resp.text().await?;
match content_type {
ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `Vec&lt;models::Game&gt;`"))),
ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `Vec&lt;models::Game&gt;`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<GamesGetError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent { status, content, entity }))
}
}
pub async fn get_gamenight(configuration: &configuration::Configuration, get_gamenight_request_body: Option<models::GetGamenightRequestBody>) -> Result<models::Gamenight, Error<GetGamenightError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_get_gamenight_request_body = get_gamenight_request_body;
let uri_str = format!("{}/gamenight", configuration.base_path);
let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
if let Some(ref user_agent) = configuration.user_agent {
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
}
if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned());
};
req_builder = req_builder.json(&p_get_gamenight_request_body);
let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?;
let status = resp.status();
let content_type = resp
.headers()
.get("content-type")
.and_then(|v| v.to_str().ok())
.unwrap_or("application/octet-stream");
let content_type = super::ContentType::from(content_type);
if !status.is_client_error() && !status.is_server_error() {
let content = resp.text().await?;
match content_type {
ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::Gamenight`"))),
ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::Gamenight`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<GetGamenightError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent { status, content, entity }))
}
}
/// Retrieve the list of gamenights on this gamenight server. Requires authorization.
pub async fn get_gamenights(configuration: &configuration::Configuration, ) -> Result<Vec<models::Gamenight>, Error<GetGamenightsError>> {
let uri_str = format!("{}/gamenights", configuration.base_path);
let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
if let Some(ref user_agent) = configuration.user_agent {
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
}
if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned());
};
let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?;
let status = resp.status();
let content_type = resp
.headers()
.get("content-type")
.and_then(|v| v.to_str().ok())
.unwrap_or("application/octet-stream");
let content_type = super::ContentType::from(content_type);
if !status.is_client_error() && !status.is_server_error() {
let content = resp.text().await?;
match content_type {
ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `Vec&lt;models::Gamenight&gt;`"))),
ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `Vec&lt;models::Gamenight&gt;`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<GetGamenightsError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent { status, content, entity }))
}
}
/// Submit your credentials to get a JWT-token to use with the rest of the api.
pub async fn get_token(configuration: &configuration::Configuration, login: Option<models::Login>) -> Result<models::Token, Error<GetTokenError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_login = login;
let uri_str = format!("{}/token", configuration.base_path);
let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
if let Some(ref user_agent) = configuration.user_agent {
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
}
req_builder = req_builder.json(&p_login);
let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?;
let status = resp.status();
let content_type = resp
.headers()
.get("content-type")
.and_then(|v| v.to_str().ok())
.unwrap_or("application/octet-stream");
let content_type = super::ContentType::from(content_type);
if !status.is_client_error() && !status.is_server_error() {
let content = resp.text().await?;
match content_type {
ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::Token`"))),
ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::Token`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<GetTokenError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent { status, content, entity }))
}
}
pub async fn join_post(configuration: &configuration::Configuration, gamenight_id: Option<models::GamenightId>) -> Result<(), Error<JoinPostError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_gamenight_id = gamenight_id;
let uri_str = format!("{}/join", configuration.base_path);
let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str);
if let Some(ref user_agent) = configuration.user_agent {
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
}
if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned());
};
req_builder = req_builder.json(&p_gamenight_id);
let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?;
let status = resp.status();
if !status.is_client_error() && !status.is_server_error() {
Ok(())
} else {
let content = resp.text().await?;
let entity: Option<JoinPostError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent { status, content, entity }))
}
}
pub async fn leave_post(configuration: &configuration::Configuration, gamenight_id: Option<models::GamenightId>) -> Result<(), Error<LeavePostError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_gamenight_id = gamenight_id;
let uri_str = format!("{}/leave", configuration.base_path);
let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str);
if let Some(ref user_agent) = configuration.user_agent {
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
}
if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned());
};
req_builder = req_builder.json(&p_gamenight_id);
let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?;
let status = resp.status();
if !status.is_client_error() && !status.is_server_error() {
Ok(())
} else {
let content = resp.text().await?;
let entity: Option<LeavePostError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent { status, content, entity }))
}
}
/// Retrieve the participants of a single gamenight by id.
pub async fn participants_get(configuration: &configuration::Configuration, gamenight_id: Option<models::GamenightId>) -> Result<models::Participants, Error<ParticipantsGetError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_gamenight_id = gamenight_id;
let uri_str = format!("{}/participants", configuration.base_path);
let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
if let Some(ref user_agent) = configuration.user_agent {
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
}
if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned());
};
req_builder = req_builder.json(&p_gamenight_id);
let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?;
let status = resp.status();
let content_type = resp
.headers()
.get("content-type")
.and_then(|v| v.to_str().ok())
.unwrap_or("application/octet-stream");
let content_type = super::ContentType::from(content_type);
if !status.is_client_error() && !status.is_server_error() {
let content = resp.text().await?;
match content_type {
ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::Participants`"))),
ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::Participants`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<ParticipantsGetError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent { status, content, entity }))
}
}
/// Add a gamenight by providing a name and a date, only available when providing an JWT token.
pub async fn post_gamenight(configuration: &configuration::Configuration, add_gamenight_request_body: Option<models::AddGamenightRequestBody>) -> Result<(), Error<PostGamenightError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_add_gamenight_request_body = add_gamenight_request_body;
let uri_str = format!("{}/gamenight", configuration.base_path);
let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str);
if let Some(ref user_agent) = configuration.user_agent {
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
}
if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned());
};
req_builder = req_builder.json(&p_add_gamenight_request_body);
let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?;
let status = resp.status();
if !status.is_client_error() && !status.is_server_error() {
Ok(())
} else {
let content = resp.text().await?;
let entity: Option<PostGamenightError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent { status, content, entity }))
}
}
/// Create a new user given a registration token and user information, username and email must be unique, and password and password_repeat must match.
pub async fn post_register(configuration: &configuration::Configuration, registration: Option<models::Registration>) -> Result<(), Error<PostRegisterError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_registration = registration;
let uri_str = format!("{}/user", configuration.base_path);
let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str);
if let Some(ref user_agent) = configuration.user_agent {
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
}
if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned());
};
req_builder = req_builder.json(&p_registration);
let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?;
let status = resp.status();
if !status.is_client_error() && !status.is_server_error() {
Ok(())
} else {
let content = resp.text().await?;
let entity: Option<PostRegisterError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent { status, content, entity }))
}
}
/// Refresh your JWT-token without logging in again.
pub async fn post_token(configuration: &configuration::Configuration, ) -> Result<models::Token, Error<PostTokenError>> {
let uri_str = format!("{}/token", configuration.base_path);
let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str);
if let Some(ref user_agent) = configuration.user_agent {
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
}
if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned());
};
let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?;
let status = resp.status();
let content_type = resp
.headers()
.get("content-type")
.and_then(|v| v.to_str().ok())
.unwrap_or("application/octet-stream");
let content_type = super::ContentType::from(content_type);
if !status.is_client_error() && !status.is_server_error() {
let content = resp.text().await?;
match content_type {
ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::Token`"))),
ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::Token`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<PostTokenError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent { status, content, entity }))
}
}
/// Get a user from primary id
pub async fn user_get(configuration: &configuration::Configuration, user_id: Option<models::UserId>) -> Result<models::User, Error<UserGetError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_user_id = user_id;
let uri_str = format!("{}/user", configuration.base_path);
let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
if let Some(ref user_agent) = configuration.user_agent {
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
}
if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned());
};
req_builder = req_builder.json(&p_user_id);
let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?;
let status = resp.status();
let content_type = resp
.headers()
.get("content-type")
.and_then(|v| v.to_str().ok())
.unwrap_or("application/octet-stream");
let content_type = super::ContentType::from(content_type);
if !status.is_client_error() && !status.is_server_error() {
let content = resp.text().await?;
match content_type {
ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::User`"))),
ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::User`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<UserGetError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent { status, content, entity }))
}
}

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 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 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 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 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,29 @@
/*
* 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};
/// 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 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 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 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 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 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 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 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 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 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 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,30 @@
/*
* 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 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,28 @@
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 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 login;
pub use self::login::Login;
pub mod participants;
pub use self::participants::Participants;
pub mod registration;
pub use self::registration::Registration;
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,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 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 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 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,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 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 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 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 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 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

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