Initial commit
This commit is contained in:
commit
ee500203e2
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
/target
|
||||||
|
.vscode
|
||||||
|
Rocket.toml
|
||||||
|
*.sqlite
|
2019
Cargo.lock
generated
Normal file
2019
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
18
Cargo.toml
Normal file
18
Cargo.toml
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
[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"
|
||||||
|
|
4
Rocket.toml.example
Normal file
4
Rocket.toml.example
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
#Copy this file over to Rocket.toml after changing all relevant values.
|
||||||
|
|
||||||
|
[global.databases]
|
||||||
|
gamenight_database = { url = "gamenight.sqlite" }
|
4
migrations/2022-03-19-191822_initial/down.sql
Normal file
4
migrations/2022-03-19-191822_initial/down.sql
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
-- This file should undo anything in `up.sql`
|
||||||
|
|
||||||
|
drop table gamenight;
|
||||||
|
drop table known_games;
|
12
migrations/2022-03-19-191822_initial/up.sql
Normal file
12
migrations/2022-03-19-191822_initial/up.sql
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
-- 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
|
||||||
|
);
|
30
readme.md
Normal file
30
readme.md
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
# Gamenight
|
||||||
|
|
||||||
|
Een online tooltje voor het organiseren van een \(board\)gamenight
|
||||||
|
|
||||||
|
Het doel, Je kan een GameNight organiseren, je geeft wanneer je zou willen spelen, je geeft aan waar je zin in hebt, welke games je hebt, en of je kan hosten. Mensen kunnen zich erbij klikken, hopelijk volgt er een spelletjes consensus, en go.
|
||||||
|
|
||||||
|
Ik wil iets maken wat georganiseerder is dan een mailthread en minder push bericht is dan een boargame appgroep.
|
||||||
|
|
||||||
|
Geplande features:
|
||||||
|
* Account systeem zodat het niet allemaal publiek is.
|
||||||
|
* Manier om een event toe te voegen.
|
||||||
|
* Manier voor owners om een event te verwijderen.
|
||||||
|
* Lijst aan upcoming events.
|
||||||
|
* Lijst aan archived events
|
||||||
|
* Manier om te koppelen aan je account welke games je hebt, zodat dit automatisch aangevult.wordt als je een party joined,
|
||||||
|
* manier om comments te plaatsen op een event.
|
||||||
|
* manier om een of meer spellen te selecten zodat mensen dit ook daadwerkelijk meenemen en niet iedereen alles hoeft mee te nemen #QOL
|
||||||
|
* manier om recurring game avonden te plannen.
|
||||||
|
|
||||||
|
Meta features:
|
||||||
|
* Api apart van de site ontwikkelen zodat je shit kan automagiseren, zelf push berichten kan fixen als je wil via de API.
|
||||||
|
* Een beetje sexy website bouwen zodat hij op zijn minst bruikbaar is op je mobiel.
|
||||||
|
|
||||||
|
# Mee devven?
|
||||||
|
|
||||||
|
Graag!
|
||||||
|
|
||||||
|
Belangrijkste devding dat je moet weten is dat je diesel migrations kan genereren en invulling kan geven, deze database migraties worden automatisch uitgevoerd als je de binary daarna start, of je kan ze handmatig uitvoeren met de diesel executable zelf. Ik weet niet zeker of je diesel nog handmatig moet installeren, maar ik denk het wel `cargo install diesel`
|
||||||
|
|
||||||
|
database migration genereren: `diesel migration generate <descriptive name>`
|
58
src/api.rs
Normal file
58
src/api.rs
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
use crate::schema;
|
||||||
|
use rocket::form::Form;
|
||||||
|
use rocket::serde::json::{Json, json, Value};
|
||||||
|
use rocket::http::Status;
|
||||||
|
use rocket::request::{self, Request, FromRequest};
|
||||||
|
use rocket::outcome::Outcome::{Success, Failure};
|
||||||
|
use rocket::response::{Redirect, Flash};
|
||||||
|
|
||||||
|
pub struct Referer(String);
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum ReferrerError {
|
||||||
|
Missing,
|
||||||
|
MoreThanOne
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Responder)]
|
||||||
|
pub enum ApiResponse {
|
||||||
|
Status(Status),
|
||||||
|
Redirect(Redirect),
|
||||||
|
Value(Value),
|
||||||
|
Flash(Flash<Redirect>)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rocket::async_trait]
|
||||||
|
impl<'r> FromRequest<'r> for Referer {
|
||||||
|
type Error = ReferrerError;
|
||||||
|
|
||||||
|
async fn from_request(req: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
|
||||||
|
let referers : Vec<_> = req.headers().get("Referer").collect();
|
||||||
|
match referers.len() {
|
||||||
|
0 => Failure((Status::BadRequest, ReferrerError::Missing)),
|
||||||
|
1 => Success(Referer(referers[0].to_string())),
|
||||||
|
_ => Failure((Status::BadRequest, ReferrerError::MoreThanOne)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/gamenights")]
|
||||||
|
pub async fn gamenights(conn: schema::DbConn) -> ApiResponse {
|
||||||
|
let gamenights = schema::get_all_gamenights(conn).await;
|
||||||
|
ApiResponse::Value(json!(gamenights))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[post("/gamenight", format = "application/json", data = "<gamenight_json>")]
|
||||||
|
pub async fn gamenight_post_json(conn: schema::DbConn, gamenight_json: Json<schema::GameNightNoId>) -> ApiResponse {
|
||||||
|
schema::insert_gamenight(conn, gamenight_json.into_inner()).await;
|
||||||
|
ApiResponse::Status(Status::Accepted)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[post("/gamenight", format = "application/x-www-form-urlencoded", data = "<gamenight_form>")]
|
||||||
|
pub async fn gamenight_post_form(referer: Option<Referer>, conn: schema::DbConn, gamenight_form: Form<schema::GameNightNoId>) -> ApiResponse {
|
||||||
|
schema::insert_gamenight(conn, gamenight_form.into_inner()).await;
|
||||||
|
match referer {
|
||||||
|
None => ApiResponse::Status(Status::Accepted),
|
||||||
|
Some(referer) => ApiResponse::Flash(Flash::success(Redirect::to(referer.0), "Added Gamenight."))
|
||||||
|
}
|
||||||
|
}
|
20
src/main.rs
Normal file
20
src/main.rs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#[macro_use] extern crate rocket;
|
||||||
|
#[macro_use] extern crate diesel_migrations;
|
||||||
|
#[macro_use] extern crate diesel;
|
||||||
|
|
||||||
|
use rocket::fairing::AdHoc;
|
||||||
|
use rocket_dyn_templates::Template;
|
||||||
|
|
||||||
|
mod api;
|
||||||
|
pub mod schema;
|
||||||
|
mod site;
|
||||||
|
|
||||||
|
#[launch]
|
||||||
|
fn rocket() -> _ {
|
||||||
|
rocket::build()
|
||||||
|
.attach(schema::DbConn::fairing())
|
||||||
|
.attach(Template::fairing())
|
||||||
|
.attach(AdHoc::on_ignite("Run Migrations", schema::run_migrations))
|
||||||
|
.mount("/", routes![site::index, site::gamenights, site::add_game_night])
|
||||||
|
.mount("/api", routes![api::gamenights, api::gamenight_post_form, api::gamenight_post_json])
|
||||||
|
}
|
83
src/schema.rs
Normal file
83
src/schema.rs
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
use rocket_sync_db_pools::database;
|
||||||
|
use serde::{Serialize, Deserialize};
|
||||||
|
use rocket::{Rocket, Build};
|
||||||
|
use diesel::RunQueryDsl;
|
||||||
|
|
||||||
|
|
||||||
|
#[database("gamenight_database")]
|
||||||
|
pub struct DbConn(diesel::SqliteConnection);
|
||||||
|
|
||||||
|
table! {
|
||||||
|
gamenight (id) {
|
||||||
|
id -> Integer,
|
||||||
|
game -> Text,
|
||||||
|
datetime -> Text,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
table! {
|
||||||
|
known_games (game) {
|
||||||
|
id -> Integer,
|
||||||
|
game -> Text,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
allow_tables_to_appear_in_same_query!(
|
||||||
|
gamenight,
|
||||||
|
known_games,
|
||||||
|
);
|
||||||
|
|
||||||
|
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 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(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,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
57
src/site.rs
Normal file
57
src/site.rs
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
use serde::{Serialize, Deserialize};
|
||||||
|
use rocket_dyn_templates::Template;
|
||||||
|
use rocket::response::{Redirect};
|
||||||
|
use rocket::request::{FlashMessage};
|
||||||
|
use crate::schema;
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
struct FlashData {
|
||||||
|
has_data: bool,
|
||||||
|
kind: String,
|
||||||
|
message: String
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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 { has_data: false, message: "".to_string(), kind: "".to_string() }
|
||||||
|
};
|
||||||
|
|
||||||
|
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 { has_data: false, message: "".to_string(), kind: "".to_string() },
|
||||||
|
Some(flash) => FlashData { has_data: true, message: flash.message().to_string(), kind: flash.kind().to_string() }
|
||||||
|
};
|
||||||
|
|
||||||
|
let data = GameNightAddData {
|
||||||
|
post_url: "/api/gamenight".to_string(),
|
||||||
|
flash: flash_data
|
||||||
|
};
|
||||||
|
|
||||||
|
Template::render("gamenight_add", &data)
|
||||||
|
}
|
5
templates/flash.html.hbs
Normal file
5
templates/flash.html.hbs
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{{#if has_data}}
|
||||||
|
<div>
|
||||||
|
<p>{{kind}}: {{message}}</p>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
16
templates/gamenight_add.html.hbs
Normal file
16
templates/gamenight_add.html.hbs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<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>
|
14
templates/gamenights.html.hbs
Normal file
14
templates/gamenights.html.hbs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
{{> flash flash }}
|
||||||
|
|
||||||
|
{{#each gamenights}}
|
||||||
|
<div>
|
||||||
|
<span>game: {{this.game}}</span>
|
||||||
|
<span>when: {{this.datetime}}</span>
|
||||||
|
</div>
|
||||||
|
{{/each}}
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in New Issue
Block a user