started work on db

This commit is contained in:
Adrian Gunnar Lauterer 2024-05-26 03:53:16 +02:00
parent 8593af2441
commit ab616cb181
Signed by: adriangl
GPG Key ID: D33368A59745C2F0
16 changed files with 1586 additions and 465 deletions

1082
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -20,16 +20,27 @@ clap = { version = "4.3.24", features = ["derive"] }
stv-rs = "0.3.0" stv-rs = "0.3.0"
serde = "1.0"
serde = { version = "1.0", features = ["derive"] }
serde_derive = "1.0" serde_derive = "1.0"
serde_yaml = "0.9.34" serde_yaml = "0.9.28"
serde_json = "1.0" serde_json = "1.0"
base64 = "~0.22.1" base64 = "~0.22.1"
futures = "0.3.30" futures = "0.3.30"
hyper = "1.3.1" hyper = "1.3.1"
url = "2.5.0" url = "2.5.0"
sqlx = "0.7.4" oauth2 = "4.4.2"
reqwest = { version = "0.12", features = ["blocking", "json"] }
jsonwebtoken = "9.3.0"
tokio = { version = "1", features = ["full"] }
sqlx = { version = "0.7", features = ["sqlite", "runtime-tokio-native-tls"] }
anyhow = "1.0"
bcrypt = "0.15.1"
itertools = "0.13"
indoc = "1.0.3"
[dev-dependencies] [dev-dependencies]
tokio-core = "*" tokio-core = "*"

View File

@ -18,5 +18,5 @@ the aim of this is rather to create the webui for participants to submit their v
## Building ## Building
```bash ```bash
cargo +nightly -Z unstable-options build --out-dir ./build cargo -Z unstable-options build --out-dir ./build
``` ```

View File

@ -1,88 +1,5 @@
{ {
"nodes": { "nodes": {
"cachix": {
"inputs": {
"devenv": "devenv_2",
"flake-compat": [
"devenv",
"flake-compat"
],
"nixpkgs": [
"devenv",
"nixpkgs"
],
"pre-commit-hooks": [
"devenv",
"pre-commit-hooks"
]
},
"locked": {
"lastModified": 1712055811,
"narHash": "sha256-7FcfMm5A/f02yyzuavJe06zLa9hcMHsagE28ADcmQvk=",
"owner": "cachix",
"repo": "cachix",
"rev": "02e38da89851ec7fec3356a5c04bc8349cae0e30",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "cachix",
"type": "github"
}
},
"devenv": {
"inputs": {
"cachix": "cachix",
"flake-compat": "flake-compat_2",
"nix": "nix_2",
"nixpkgs": "nixpkgs_2",
"pre-commit-hooks": "pre-commit-hooks"
},
"locked": {
"lastModified": 1716484006,
"narHash": "sha256-2gtN5jf21HS9TAZXhf9G+OSUY1TQ/95n6clcuFjYQ58=",
"owner": "cachix",
"repo": "devenv",
"rev": "800f19d1b999f89464fd8e0226abf4b3b444b0fa",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "devenv",
"type": "github"
}
},
"devenv_2": {
"inputs": {
"flake-compat": [
"devenv",
"cachix",
"flake-compat"
],
"nix": "nix",
"nixpkgs": "nixpkgs",
"poetry2nix": "poetry2nix",
"pre-commit-hooks": [
"devenv",
"cachix",
"pre-commit-hooks"
]
},
"locked": {
"lastModified": 1708704632,
"narHash": "sha256-w+dOIW60FKMaHI1q5714CSibk99JfYxm0CzTinYWr+Q=",
"owner": "cachix",
"repo": "devenv",
"rev": "2ee4450b0f4b95a1b90f2eb5ffea98b90e48c196",
"type": "github"
},
"original": {
"owner": "cachix",
"ref": "python-rewrite",
"repo": "devenv",
"type": "github"
}
},
"fenix": { "fenix": {
"inputs": { "inputs": {
"nixpkgs": [ "nixpkgs": [
@ -104,254 +21,7 @@
"type": "github" "type": "github"
} }
}, },
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1673956053,
"narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"flake-compat_2": {
"flake": false,
"locked": {
"lastModified": 1696426674,
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1689068808,
"narHash": "sha256-6ixXo3wt24N/melDWjq70UuHQLxGV8jZvooRanIHXw0=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "919d646de7be200f3bf08cb76ae1f09402b6f9b4",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"flake-utils_2": {
"inputs": {
"systems": "systems_2"
},
"locked": {
"lastModified": 1710146030,
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"gitignore": {
"inputs": {
"nixpkgs": [
"devenv",
"pre-commit-hooks",
"nixpkgs"
]
},
"locked": {
"lastModified": 1709087332,
"narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
"owner": "hercules-ci",
"repo": "gitignore.nix",
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "gitignore.nix",
"type": "github"
}
},
"nix": {
"inputs": {
"flake-compat": "flake-compat",
"nixpkgs": [
"devenv",
"cachix",
"devenv",
"nixpkgs"
],
"nixpkgs-regression": "nixpkgs-regression"
},
"locked": {
"lastModified": 1712911606,
"narHash": "sha256-BGvBhepCufsjcUkXnEEXhEVjwdJAwPglCC2+bInc794=",
"owner": "domenkozar",
"repo": "nix",
"rev": "b24a9318ea3f3600c1e24b4a00691ee912d4de12",
"type": "github"
},
"original": {
"owner": "domenkozar",
"ref": "devenv-2.21",
"repo": "nix",
"type": "github"
}
},
"nix-github-actions": {
"inputs": {
"nixpkgs": [
"devenv",
"cachix",
"devenv",
"poetry2nix",
"nixpkgs"
]
},
"locked": {
"lastModified": 1688870561,
"narHash": "sha256-4UYkifnPEw1nAzqqPOTL2MvWtm3sNGw1UTYTalkTcGY=",
"owner": "nix-community",
"repo": "nix-github-actions",
"rev": "165b1650b753316aa7f1787f3005a8d2da0f5301",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nix-github-actions",
"type": "github"
}
},
"nix_2": {
"inputs": {
"flake-compat": [
"devenv",
"flake-compat"
],
"nixpkgs": [
"devenv",
"nixpkgs"
],
"nixpkgs-regression": "nixpkgs-regression_2"
},
"locked": {
"lastModified": 1712911606,
"narHash": "sha256-BGvBhepCufsjcUkXnEEXhEVjwdJAwPglCC2+bInc794=",
"owner": "domenkozar",
"repo": "nix",
"rev": "b24a9318ea3f3600c1e24b4a00691ee912d4de12",
"type": "github"
},
"original": {
"owner": "domenkozar",
"ref": "devenv-2.21",
"repo": "nix",
"type": "github"
}
},
"nixpkgs": { "nixpkgs": {
"locked": {
"lastModified": 1692808169,
"narHash": "sha256-x9Opq06rIiwdwGeK2Ykj69dNc2IvUH1fY55Wm7atwrE=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "9201b5ff357e781bf014d0330d18555695df7ba8",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-regression": {
"locked": {
"lastModified": 1643052045,
"narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
"type": "github"
}
},
"nixpkgs-regression_2": {
"locked": {
"lastModified": 1643052045,
"narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
"type": "github"
}
},
"nixpkgs-stable": {
"locked": {
"lastModified": 1710695816,
"narHash": "sha256-3Eh7fhEID17pv9ZxrPwCLfqXnYP006RKzSs0JptsN84=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "614b4613980a522ba49f0d194531beddbb7220d3",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-23.11",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1713361204,
"narHash": "sha256-TA6EDunWTkc5FvDCqU3W2T3SFn0gRZqh6D/hJnM02MM=",
"owner": "cachix",
"repo": "devenv-nixpkgs",
"rev": "285676e87ad9f0ca23d8714a6ab61e7e027020c6",
"type": "github"
},
"original": {
"owner": "cachix",
"ref": "rolling",
"repo": "devenv-nixpkgs",
"type": "github"
}
},
"nixpkgs_3": {
"locked": { "locked": {
"lastModified": 1716509168, "lastModified": 1716509168,
"narHash": "sha256-4zSIhSRRIoEBwjbPm3YiGtbd8HDWzFxJjw5DYSDy1n8=", "narHash": "sha256-4zSIhSRRIoEBwjbPm3YiGtbd8HDWzFxJjw5DYSDy1n8=",
@ -367,64 +37,10 @@
"type": "github" "type": "github"
} }
}, },
"poetry2nix": {
"inputs": {
"flake-utils": "flake-utils",
"nix-github-actions": "nix-github-actions",
"nixpkgs": [
"devenv",
"cachix",
"devenv",
"nixpkgs"
]
},
"locked": {
"lastModified": 1692876271,
"narHash": "sha256-IXfZEkI0Mal5y1jr6IRWMqK8GW2/f28xJenZIPQqkY0=",
"owner": "nix-community",
"repo": "poetry2nix",
"rev": "d5006be9c2c2417dafb2e2e5034d83fabd207ee3",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "poetry2nix",
"type": "github"
}
},
"pre-commit-hooks": {
"inputs": {
"flake-compat": [
"devenv",
"flake-compat"
],
"flake-utils": "flake-utils_2",
"gitignore": "gitignore",
"nixpkgs": [
"devenv",
"nixpkgs"
],
"nixpkgs-stable": "nixpkgs-stable"
},
"locked": {
"lastModified": 1713775815,
"narHash": "sha256-Wu9cdYTnGQQwtT20QQMg7jzkANKQjwBD9iccfGKkfls=",
"owner": "cachix",
"repo": "pre-commit-hooks.nix",
"rev": "2ac4dcbf55ed43f3be0bae15e181f08a57af24a4",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "pre-commit-hooks.nix",
"type": "github"
}
},
"root": { "root": {
"inputs": { "inputs": {
"devenv": "devenv",
"fenix": "fenix", "fenix": "fenix",
"nixpkgs": "nixpkgs_3" "nixpkgs": "nixpkgs"
} }
}, },
"rust-analyzer-src": { "rust-analyzer-src": {
@ -443,36 +59,6 @@
"repo": "rust-analyzer", "repo": "rust-analyzer",
"type": "github" "type": "github"
} }
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"systems_2": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
} }
}, },
"root": "root", "root": "root",

View File

@ -1,28 +1,37 @@
{ {
inputs = { inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
devenv.url = "github:cachix/devenv";
fenix.url = "github:nix-community/fenix"; fenix.url = "github:nix-community/fenix";
fenix.inputs.nixpkgs.follows = "nixpkgs"; fenix.inputs.nixpkgs.follows = "nixpkgs";
}; };
outputs = { self, nixpkgs, devenv, ... }@inputs:
outputs = { self, nixpkgs, fenix }@inputs:
let let
systems = [
"x86_64-linux"
"aarch64-linux"
"aarch64-darwin"
];
forAllSystems = f: nixpkgs.lib.genAttrs systems (system: let
toolchain = fenix.packages.${system}.complete;
pkgs = import nixpkgs { pkgs = import nixpkgs {
system = "x86_64-linux"; inherit system;
}; overlays = [
in { (_: super: let pkgs = fenix.inputs.nixpkgs.legacyPackages.${system}; in fenix.overlays.default pkgs pkgs)
devShell.x86_64-linux = devenv.lib.mkShell {
inherit inputs pkgs;
modules = [
{
languages.rust = {
enable = true;
channel = "stable";
};
}
]; ];
}; };
in f system pkgs toolchain);
in {
devShell = forAllSystems (system: pkgs: toolchain: pkgs.mkShell {
packages = [
(toolchain.withComponents [
"cargo" "rustc" "rustfmt" "clippy"
])
pkgs.openssl
pkgs.pkg-config
];
RUST_SRC_PATH = "${toolchain.rust-src}/lib/rustlib/src/rust/library";
});
}; };
} }

View File

@ -7,6 +7,9 @@
use rocket::http::Status; use rocket::http::Status;
use rocket_contrib::json::{Json, JsonValue}; use rocket_contrib::json::{Json, JsonValue};
use crate::auth::login;
// Define data models // Define data models
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
struct User { struct User {
@ -49,15 +52,19 @@ struct Vote {
data: Vec<VoteItem>, data: Vec<VoteItem>,
} }
// Routes
#[post("/auth/login", format = "application/json", data = "<credentials>")] #[post("/auth/login", format = "application/json", data = "<credentials>")]
fn login(credentials: Json<User>) -> JsonValue { async fn handle_login(credentials: Json<User>, db: Db) -> JsonValue {
// Authentication logic here match login(credentials.email, credentials.password, db).await {
json!({ Ok(token) => json!({
"token": "your_generated_token" "token": token
}) }),
Err(error) => json!({
"error": error
}),
}
} }
#[post("/auth/token", format = "application/json", data = "<token>")] #[post("/auth/token", format = "application/json", data = "<token>")]
fn generate_token(token: Json<Authorization>) -> JsonValue { fn generate_token(token: Json<Authorization>) -> JsonValue {
// Token generation logic here // Token generation logic here

33
src/auth.rs Normal file
View File

@ -0,0 +1,33 @@
use jsonwebtoken::{encode, Header, EncodingKey};
use serde::{Serialize, Deserialize};
use chrono::{Utc, Duration};
use std::error::Error;
#[derive(Debug, Serialize, Deserialize)]
struct Claims {
sub: String,
exp: usize,
}
pub async fn login(username: &str, _password: &str, _db: &sqlx::SqlitePool) -> Result<String, Box<dyn Error>> {
// Normally, you would validate the username and password against the database here.
// For the mock, we just generate a token for any username.
let expiration = Utc::now()
.checked_add_signed(Duration::minutes(60))
.expect("valid timestamp")
.timestamp() as usize;
let claims = Claims {
sub: username.to_owned(),
exp: expiration,
};
let token = encode(
&Header::default(),
&claims,
&EncodingKey::from_secret("secret".as_ref()),
)?;
Ok(token)
}

239
src/db.rs Normal file
View File

@ -0,0 +1,239 @@
use sqlx::{sqlite::SqlitePool, Pool, Row};
use std::error::Error;
use serde::{Deserialize, Serialize};
use indoc::indoc;
pub struct Connection {
pool: Pool<sqlx::Sqlite>,
}
pub async fn connect() -> Result<Connection, Box<dyn Error>> {
let database_url = "sqlite::memory:"; // Use an in-memory SQLite database for testing
let pool = SqlitePool::connect(database_url).await?;
Ok(Connection { pool })
}
pub async fn init_db(conn: &Connection) -> Result<(), Box<dyn Error>> {
sqlx::query(indoc! {"
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL UNIQUE,
jwt_token TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS elections (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
username TEXT NOT NULL,
namespace TEXT NOT NULL,
description TEXT,
start_date TEXT NOT NULL,
end_date TEXT NOT NULL,
FOREIGN KEY (username) REFERENCES users(username)
);
CREATE TABLE IF NOT EXISTS authorizations (
id INTEGER PRIMARY KEY AUTOINCREMENT,
from_date TEXT NOT NULL,
to_date TEXT NOT NULL,
from_user TEXT NOT NULL,
to_user TEXT NOT NULL,
namespace TEXT NOT NULL,
FOREIGN KEY (from_user) REFERENCES users(username),
FOREIGN KEY (to_user) REFERENCES users(username)
);
CREATE TABLE IF NOT EXISTS votes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
value INTEGER NOT NULL,
option_id INTEGER NOT NULL,
election_id INTEGER NOT NULL,
option_name TEXT NOT NULL,
user TEXT NOT NULL,
namespace TEXT NOT NULL,
date TEXT NOT NULL,
FOREIGN KEY (election_id) REFERENCES elections(id),
FOREIGN KEY (user) REFERENCES users(username)
);
"})
.execute(&conn.pool)
.await?;
Ok(())
}
// Users
#[derive(Serialize, Deserialize)]
pub struct User {
pub id: i64,
pub username: String,
pub jwt_token: String,
}
pub async fn insert_user(conn: &Connection, username: &str, jwt_token: &str) -> Result<(), Box<dyn Error>> {
sqlx::query("INSERT INTO users (username, jwt_token) VALUES (?, ?)")
.bind(username)
.bind(jwt_token)
.execute(&conn.pool)
.await?;
Ok(())
}
pub async fn get_user(conn: &Connection, username: &str) -> Result<Option<User>, Box<dyn Error>> {
let row = sqlx::query("SELECT id, username, jwt_token FROM users WHERE username = ?")
.bind(username)
.fetch_optional(&conn.pool)
.await?;
if let Some(row) = row {
Ok(Some(User {
id: row.get("id"),
username: row.get("username"),
jwt_token: row.get("jwt_token"),
}))
} else {
Ok(None)
}
}
// Elections
#[derive(Serialize, Deserialize)]
pub struct Election {
pub id: i64,
pub name: String,
pub username: String,
pub namespace: String,
pub description: String,
pub start_date: String,
pub end_date: String,
}
pub async fn insert_election(conn: &Connection, election: &Election) -> Result<(), Box<dyn Error>> {
sqlx::query(indoc! {"
INSERT INTO elections (name, username, namespace, description, start_date, end_date)
VALUES (?, ?, ?, ?, ?, ?)
"})
.bind(&election.name)
.bind(&election.username)
.bind(&election.namespace)
.bind(&election.description)
.bind(&election.start_date)
.bind(&election.end_date)
.execute(&conn.pool)
.await?;
Ok(())
}
pub async fn get_election(conn: &Connection, id: i64) -> Result<Option<Election>, Box<dyn Error>> {
let row = sqlx::query("SELECT id, name, username, namespace, description, start_date, end_date FROM elections WHERE id = ?")
.bind(id)
.fetch_optional(&conn.pool)
.await?;
if let Some(row) = row {
Ok(Some(Election {
id: row.get("id"),
name: row.get("name"),
username: row.get("username"),
namespace: row.get("namespace"),
description: row.get("description"),
start_date: row.get("start_date"),
end_date: row.get("end_date"),
}))
} else {
Ok(None)
}
}
// Authorizations
#[derive(Serialize, Deserialize)]
pub struct Authorization {
pub id: i64,
pub from_date: String,
pub to_date: String,
pub from_user: String,
pub to_user: String,
pub namespace: String,
}
pub async fn insert_authorization(conn: &Connection, authorization: &Authorization) -> Result<(), Box<dyn Error>> {
sqlx::query(indoc! {"
INSERT INTO authorizations (from_date, to_date, from_user, to_user, namespace)
VALUES (?, ?, ?, ?, ?)
"})
.bind(&authorization.from_date)
.bind(&authorization.to_date)
.bind(&authorization.from_user)
.bind(&authorization.to_user)
.bind(&authorization.namespace)
.execute(&conn.pool)
.await?;
Ok(())
}
pub async fn get_authorization(conn: &Connection, id: i64) -> Result<Option<Authorization>, Box<dyn Error>> {
let row = sqlx::query("SELECT id, from_date, to_date, from_user, to_user, namespace FROM authorizations WHERE id = ?")
.bind(id)
.fetch_optional(&conn.pool)
.await?;
if let Some(row) = row {
Ok(Some(Authorization {
id: row.get("id"),
from_date: row.get("from_date"),
to_date: row.get("to_date"),
from_user: row.get("from_user"),
to_user: row.get("to_user"),
namespace: row.get("namespace"),
}))
} else {
Ok(None)
}
}
// Votes
#[derive(Serialize, Deserialize)]
pub struct Vote {
pub id: i64,
pub value: i32,
pub option_id: i64,
pub election_id: i64,
pub option_name: String,
pub user: String,
pub namespace: String,
pub date: String,
}
pub async fn insert_vote(conn: &Connection, vote: &Vote) -> Result<(), Box<dyn Error>> {
sqlx::query(indoc! {"
INSERT INTO votes (value, option_id, election_id, option_name, user, namespace, date)
VALUES (?, ?, ?, ?, ?, ?, ?)
"})
.bind(vote.value)
.bind(vote.option_id)
.bind(vote.election_id)
.bind(&vote.option_name)
.bind(&vote.user)
.bind(&vote.namespace)
.bind(&vote.date)
.execute(&conn.pool)
.await?;
Ok(())
}
pub async fn get_vote(conn: &Connection, id: i64) -> Result<Option<Vote>, Box<dyn Error>> {
let row = sqlx::query("SELECT id, value, option_id, election_id, option_name, user, namespace, date FROM votes WHERE id = ?")
.bind(id)
.fetch_optional(&conn.pool)
.await?;
if let Some(row) = row {
Ok(Some(Vote {
id: row.get("id"),
value: row.get("value"),
option_id: row.get("option_id"),
election_id: row.get("election_id"),
option_name: row.get("option_name"),
user: row.get("user"),
namespace: row.get("namespace"),
date: row.get("date"),
}))
} else {
Ok(None)
}
}

View File

@ -1,11 +0,0 @@
#[macro_use]
extern crate serde_derive;
extern crate hyper;
extern crate serde;
extern crate serde_json;
extern crate futures;
extern crate url;
pub mod apis;
pub mod models;

186
src/main.rs Normal file
View File

@ -0,0 +1,186 @@
// main.rs
mod db;
#[cfg(test)]
mod tests {
use super::db;
use tokio;
#[tokio::test]
async fn test_connection() {
let result = db::connect().await;
assert!(result.is_ok(), "Database connection failed");
}
#[tokio::test]
async fn test_insert_user() {
let conn = db::connect().await.unwrap();
db::init_db(&conn).await.unwrap();
let result = db::insert_user(&conn, "test_user", "test_jwt").await;
assert!(result.is_ok(), "Insert user operation failed");
}
#[tokio::test]
async fn test_get_user() {
let conn = db::connect().await.unwrap();
db::init_db(&conn).await.unwrap();
db::insert_user(&conn, "test_user", "test_jwt").await.unwrap();
let result = db::get_user(&conn, "test_user").await;
assert!(result.is_ok(), "Get user operation failed");
let user = result.unwrap();
assert!(user.is_some(), "User not found");
let user = user.unwrap();
assert_eq!(user.username, "test_user", "Username does not match");
assert_eq!(user.jwt_token, "test_jwt", "JWT token does not match");
}
#[tokio::test]
async fn test_insert_election() {
let conn = db::connect().await.unwrap();
db::init_db(&conn).await.unwrap();
db::insert_user(&conn, "test_user", "test_jwt").await.unwrap();
let election = db::Election {
id: 0,
name: "Election 1".to_string(),
username: "test_user".to_string(),
namespace: "namespace2".to_string(),
description: "Description 1".to_string(),
start_date: "2024-05-01T08:00".to_string(),
end_date: "2024-05-10T20:00".to_string(),
};
let result = db::insert_election(&conn, &election).await;
assert!(result.is_ok(), "Insert election operation failed");
}
#[tokio::test]
async fn test_get_election() {
let conn = db::connect().await.unwrap();
db::init_db(&conn).await.unwrap();
db::insert_user(&conn, "test_user", "test_jwt").await.unwrap();
let election = db::Election {
id: 0,
name: "Election 1".to_string(),
username: "test_user".to_string(),
namespace: "namespace2".to_string(),
description: "Description 1".to_string(),
start_date: "2024-05-01T08:00".to_string(),
end_date: "2024-05-10T20:00".to_string(),
};
db::insert_election(&conn, &election).await.unwrap();
let result = db::get_election(&conn, 1).await;
assert!(result.is_ok(), "Get election operation failed");
let election = result.unwrap();
assert!(election.is_some(), "Election not found");
let election = election.unwrap();
assert_eq!(election.name, "Election 1", "Election name does not match");
}
#[tokio::test]
async fn test_insert_authorization() {
let conn = db::connect().await.unwrap();
db::init_db(&conn).await.unwrap();
db::insert_user(&conn, "user1", "jwt1").await.unwrap();
db::insert_user(&conn, "user2", "jwt2").await.unwrap();
let authorization = db::Authorization {
id: 0,
from_date: "2024-05-01T00:00".to_string(),
to_date: "2024-05-10T23:59".to_string(),
from_user: "user1".to_string(),
to_user: "user2".to_string(),
namespace: "namespace".to_string(),
};
let result = db::insert_authorization(&conn, &authorization).await;
assert!(result.is_ok(), "Insert authorization operation failed");
}
#[tokio::test]
async fn test_get_authorization() {
let conn = db::connect().await.unwrap();
db::init_db(&conn).await.unwrap();
db::insert_user(&conn, "user1", "jwt1").await.unwrap();
db::insert_user(&conn, "user2", "jwt2").await.unwrap();
let authorization = db::Authorization {
id: 0,
from_date: "2024-05-01T00:00".to_string(),
to_date: "2024-05-10T23:59".to_string(),
from_user: "user1".to_string(),
to_user: "user2".to_string(),
namespace: "namespace".to_string(),
};
db::insert_authorization(&conn, &authorization).await.unwrap();
let result = db::get_authorization(&conn, 1).await;
assert!(result.is_ok(), "Get authorization operation failed");
let authorization = result.unwrap();
assert!(authorization.is_some(), "Authorization not found");
let authorization = authorization.unwrap();
assert_eq!(authorization.from_user, "user1", "From user does not match");
assert_eq!(authorization.to_user, "user2", "To user does not match");
}
#[tokio::test]
async fn test_insert_vote() {
let conn = db::connect().await.unwrap();
db::init_db(&conn).await.unwrap();
db::insert_user(&conn, "user1", "jwt1").await.unwrap();
db::insert_election(&conn, &db::Election {
id: 0,
name: "Election 1".to_string(),
username: "user1".to_string(),
namespace: "namespace".to_string(),
description: "Description 1".to_string(),
start_date: "2024-05-01T08:00".to_string(),
end_date: "2024-05-10T20:00".to_string(),
}).await.unwrap();
let vote = db::Vote {
id: 0,
value: 1,
option_id: 1,
election_id: 1,
option_name: "Option 1".to_string(),
user: "user1".to_string(),
namespace: "namespace".to_string(),
date: "2024-05-01T12:00".to_string(),
};
let result = db::insert_vote(&conn, &vote).await;
assert!(result.is_ok(), "Insert vote operation failed");
}
#[tokio::test]
async fn test_get_vote() {
let conn = db::connect().await.unwrap();
db::init_db(&conn).await.unwrap();
db::insert_user(&conn, "user1", "jwt1").await.unwrap();
db::insert_election(&conn, &db::Election {
id: 0,
name: "Election 1".to_string(),
username: "user1".to_string(),
namespace: "namespace".to_string(),
description: "Description 1".to_string(),
start_date: "2024-05-01T08:00".to_string(),
end_date: "2024-05-10T20:00".to_string(),
}).await.unwrap();
let vote = db::Vote {
id: 0,
value: 1,
option_id: 1,
election_id: 1,
option_name: "Option 1".to_string(),
user: "user1".to_string(),
namespace: "namespace".to_string(),
date: "2024-05-01T12:00".to_string(),
};
db::insert_vote(&conn, &vote).await.unwrap();
let result = db::get_vote(&conn, 1).await;
assert!(result.is_ok(), "Get vote operation failed");
let vote = result.unwrap();
assert!(vote.is_some(), "Vote not found");
let vote = vote.unwrap();
assert_eq!(vote.option_name, "Option 1", "Option name does not match");
}
}
#[tokio::main]
async fn main() {
println!("This is the main function. Run `cargo test` to execute tests.");
}

View File

@ -59,6 +59,7 @@
<label for="namespace">Namespace:</label> <label for="namespace">Namespace:</label>
<input type="text" id="namespace" name="namespace" placeholder="Enter namespace" required> <input type="text" id="namespace" name="namespace" placeholder="Enter namespace" required>
<input type="submit" value="Give Acces"> <input type="submit" value="Give Acces">
</form> </form>
</div> </div>
</body> </body>

View File

@ -76,6 +76,8 @@
// Add more users as needed // Add more users as needed
]; ];
const usersContainer = document.getElementById("usersContainer"); const usersContainer = document.getElementById("usersContainer");
users.forEach(user => { users.forEach(user => {