Skip to content

database #4

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 15 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,986 changes: 1,832 additions & 154 deletions Cargo.lock

Large diffs are not rendered by default.

63 changes: 63 additions & 0 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
]
++ lib.optionals stdenv.isDarwin [
libiconv
darwin.apple_sdk.frameworks.Security
];

# the coverage report will run the tests
Expand All @@ -83,9 +84,67 @@
{
devShells.default = pkgs.mkShell {
inputsFrom = [ self'.packages.default ];
packages = [ pkgs.sqlx-cli ];
DATABASE_URL = "sqlite:items.db";
};

packages = {
run-stack = pkgs.writeShellApplication {
name = "run-stack";
runtimeInputs = [
pkgs.tinyproxy
pkgs.simple-http-server
self'.packages.run-migrations
];
text =
let
proxyConfig = pkgs.writeTextFile {
name = "proxy.conf";
text = ''
ReversePath "/" "http://0.0.0.0:8001/"
ReversePath "/api/" "http://0.0.0.0:8002/api/"
ReverseOnly Yes
Port 8000
ReverseBaseURL "http://0.0.0.0:8000/"
'';
};
in
''
database_file=$(mktemp database.XXXX)
run-migrations server/migrations "$database_file"

simple-http-server --index --port 8001 frontend &
PID_FRONTEND=$!
cargo run -- --listen-address 0.0.0.0:8002 --database-url "sqlite://$database_file" &
PID_BACKEND=$!
tinyproxy -d -c ${proxyConfig} &
PID_PROXY=$!

cleanup() {
kill $PID_FRONTEND $PID_BACKEND $PID_PROXY
wait $PID_FRONTEND $PID_BACKEND $PID_PROXY
exit 0
}

trap cleanup SIGINT

wait $PID_FRONTEND $PID_BACKEND $PID_PROXY
rm -rf "$database_file"
'';
};

run-migrations = pkgs.writeShellApplication {
name = "run-migrations";
runtimeInputs = [ pkgs.sqlite ];
text = ''
>&2 echo "Applying migrations"
for migration_file in "$1"/*.sql; do
>&2 echo "Applying migration: $migration_file"
sqlite3 "$2" < "$migration_file"
done
'';
};

server-deps = craneLib.buildDepsOnly commonAttrs;

server-docs = craneLib.cargoDoc (
Expand All @@ -100,6 +159,10 @@
// {
cargoArtifacts = self'.packages.server-deps;
meta.mainProgram = "server";
passthru = {
migrations = ./server/migrations;
inherit (self'.packages) run-migrations;
};
}
);

Expand Down
22 changes: 22 additions & 0 deletions frontend/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/style.css">
<script src="https://unpkg.com/[email protected]" integrity="sha384-Y7hw+L/jvKeWIRRkqWYfPcvVxHzVzn5REgzbawhxAuQGwX1XWe70vji+VSeHOThJ" crossorigin="anonymous"></script>
<title>Demo</title>
</head>
<body>
<button hx-post="/api/v1/items/new" hx-swap="none">New</button>
<div hx-get="/api/v1/items" hx-trigger="load, newItem from:body">
</div>
<noscript>
<p lang="en">
This website requires JavaScript. Here are the
<a href="https://www.enable-javascript.com/en/">instructions how to enable JavaScript in your web browser</a>.
Or perhaps you need to make an exception in your script blocker.
</p>
</noscript>
</body>
</html>
2 changes: 2 additions & 0 deletions frontend/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@import "https://unpkg.com/open-props";
@import "https://unpkg.com/open-props/normalize.min.css";
Binary file added items.db
Binary file not shown.
1 change: 1 addition & 0 deletions nixos/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
self.nixosModules.server
];
services.server.enable = true;
services.server.metrics.enable = true;
};
verifyServices = [ "server.service" ];
};
Expand Down
52 changes: 47 additions & 5 deletions nixos/modules/server.nix
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ let
types
mkOption
mkIf
mkMerge
mkEnableOption
escapeShellArgs
optionals
;
cfg = config.services.server;
stateDirectory = "/var/lib/server";
databasePath = "/var/lib/server/database.db";
in
{
options.services.server = {
Expand Down Expand Up @@ -37,6 +39,36 @@ in
'';
};

database_url = mkOption {
type = types.str;
default = "sqlite://${databasePath}";
example = "sqlite://${databasePath}";
description = ''
SQlite database to connect to.
'';
};

metrics = {
enable = lib.mkEnableOption "Prometheus metrics server";

address = mkOption {
type = types.str;
default = "0.0.0.0";
example = "0.0.0.0";
description = ''
Listen address of the metrics server.
'';
};

port = mkOption {
type = types.port;
default = 8081;
description = ''
Listen port of the metrics service.
'';
};
};

logLevel = mkOption {
type = types.str;
default = "info";
Expand All @@ -50,10 +82,18 @@ in

systemd.services.server =
let
args = escapeShellArgs [
"--listen-address"
"${cfg.address}:${toString cfg.port}"
];
args = escapeShellArgs (
[
"--listen-address"
"${cfg.address}:${toString cfg.port}"
"--database-url"
"sqlite://${databasePath}"
]
++ optionals cfg.metrics.enable [
"--metrics-listen-address"
"${cfg.metrics.address}:${toString cfg.metrics.port}"
]
);
in
{
description = "server";
Expand All @@ -65,9 +105,11 @@ in
RUST_LOG = cfg.logLevel;
};
serviceConfig = {
ExecStartPre = "${lib.getExe cfg.package.passthru.run-migrations} ${cfg.package.passthru.migrations} ${databasePath}";
ExecStart = "${lib.getExe cfg.package} ${args}";
Restart = "always";
DynamicUser = true;
StateDirectory = baseNameOf stateDirectory;
};
};
};
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,20 @@ version = "0.1.0"
edition = "2021"

[dependencies]
anyhow = "1"
askama = "0.12"
axum = { version = "0.8", features = ["tracing"] }
axum-extra = { version = "0.10", features = ["typed-routing"] }
clap = { version = "4", features = ["derive"] }
metrics = "0.23"
metrics-exporter-prometheus = "0.15"
sqlx = { version = "0.8", features = [ "runtime-tokio-native-tls", "sqlite", "uuid"] }
tokio = { version = "1", features = [ "full", "tracing"] }
tracing = "0.1"
tracing-subscriber = "0.3"
uuid = { version = "1", features = ["v4"] }

[dev-dependencies]
ureq = "2"
backoff = { version = "0.4", features = ["tokio"] }
tempfile = "3"
6 changes: 6 additions & 0 deletions server/migrations/20240916162916_create-items.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

CREATE TABLE items (
id TEXT PRIMARY KEY NOT NULL, -- Store UUID as TEXT in SQLite
name TEXT NOT NULL,
price TEXT NOT NULL -- Store u128 as TEXT
);
9 changes: 9 additions & 0 deletions server/src/app_state.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
use sqlx::SqlitePool;
use std::sync::Arc;

pub type AppState = Arc<AppConfig>;

#[derive(Debug)]
pub struct AppConfig {
pub db_pool: SqlitePool,
}
4 changes: 4 additions & 0 deletions server/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,8 @@ use std::net::{Ipv6Addr, SocketAddr, SocketAddrV6};
pub struct CliArgs {
#[arg(short, long, default_value_t = SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0), 8080, 0, 0)))]
pub listen_address: SocketAddr,
#[arg(short, long)]
pub metrics_listen_address: Option<SocketAddr>,
#[arg(short, long)]
pub database_url: String,
}
43 changes: 43 additions & 0 deletions server/src/db.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
use crate::item::Item;
use sqlx::{query_as, FromRow, SqlitePool};
use uuid::Uuid;

#[derive(FromRow)]
struct DbItem {
id: String,
name: String,
price: String,
}

impl From<DbItem> for Item {
fn from(db_item: DbItem) -> Item {
Item {
id: Uuid::parse_str(&db_item.id).expect(""),
name: db_item.name,
price: db_item.price.parse::<u128>().expect(""),
}
}
}

pub async fn new_item(pool: &SqlitePool, name: &str, price: u128) -> Result<Item, sqlx::Error> {
let id = Uuid::new_v4().to_string();
let price = price.to_string();
query_as!(
DbItem,
"insert into items (id, name, price) values ($1, $2, $3) returning *",
id,
name,
price
)
.fetch_one(pool)
.await
.map(Into::into)
}

/// Returns all known items
pub async fn get_items(pool: &SqlitePool) -> Result<Vec<Item>, sqlx::Error> {
query_as!(DbItem, "select * from items")
.fetch_all(pool)
.await
.map(|items| items.into_iter().map(Into::into).collect())
}
15 changes: 15 additions & 0 deletions server/src/item.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use askama::Template;
use uuid::Uuid;

#[derive(Debug, Clone)]
pub struct Item {
pub id: Uuid,
pub name: String,
pub price: u128,
}

#[derive(Template)]
#[template(path = "items.html")]
pub struct ItemsTemplate<'a> {
pub items: &'a Vec<Item>,
}
Loading
Loading