Compare commits
7 Commits
fc001933d1
...
97d19a5b80
Author | SHA1 | Date |
---|---|---|
meeg_leeto | 97d19a5b80 | |
meeg_leeto | 2f3884b223 | |
meeg_leeto | ddff9bdbfb | |
meeg_leeto | b53646cb64 | |
meeg_leeto | 7220663240 | |
meeg_leeto | dc5a74c6b3 | |
meeg_leeto | a19170db81 |
|
@ -57,15 +57,6 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atomic"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b88d82667eca772c4aa12f0f1348b3ae643424c8876448f3f7bd5787032e234c"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
|
@ -250,20 +241,6 @@ dependencies = [
|
|||
"instant",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "figment"
|
||||
version = "0.10.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "790b4292c72618abbab50f787a477014fe15634f96291de45672ce46afe122df"
|
||||
dependencies = [
|
||||
"atomic",
|
||||
"pear",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"uncased",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
|
@ -490,12 +467,6 @@ dependencies = [
|
|||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inlinable_string"
|
||||
version = "0.1.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb"
|
||||
|
||||
[[package]]
|
||||
name = "instant"
|
||||
version = "0.1.12"
|
||||
|
@ -581,7 +552,6 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"argh",
|
||||
"base64",
|
||||
"figment",
|
||||
"redis",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
@ -769,29 +739,6 @@ dependencies = [
|
|||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pear"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "15e44241c5e4c868e3eaa78b7c1848cadd6344ed4f54d029832d32b415a58702"
|
||||
dependencies = [
|
||||
"inlinable_string",
|
||||
"pear_codegen",
|
||||
"yansi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pear_codegen"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "82a5ca643c2303ecb740d506539deba189e16f2754040a42901cd8105d0282d0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"proc-macro2-diagnostics",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.1.0"
|
||||
|
@ -865,19 +812,6 @@ dependencies = [
|
|||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2-diagnostics"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4bf29726d67464d49fa6224a1d07936a8c08bb3fba727c7493f6cf1616fdaada"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"version_check",
|
||||
"yansi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick-error"
|
||||
version = "1.2.3"
|
||||
|
@ -1363,15 +1297,6 @@ version = "1.15.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
|
||||
|
||||
[[package]]
|
||||
name = "uncased"
|
||||
version = "0.9.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5baeed7327e25054889b9bd4f975f32e5f4c5d434042d59ab6cd4142c0a76ed0"
|
||||
dependencies = [
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicase"
|
||||
version = "2.6.0"
|
||||
|
@ -1593,9 +1518,3 @@ name = "windows_x86_64_msvc"
|
|||
version = "0.32.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "504a2476202769977a040c6364301a3f65d0cc9e3fb08600b2bda150a0488316"
|
||||
|
||||
[[package]]
|
||||
name = "yansi"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
|
||||
|
|
|
@ -8,7 +8,6 @@ edition = "2021"
|
|||
[dependencies]
|
||||
argh = "0.1.7"
|
||||
base64 = "0.13.0"
|
||||
figment = { version = "0.10.6", features = ["json", "env"] }
|
||||
redis = { version = "~0.21.5", features = ["tokio-comp"] }
|
||||
serde = { version = "~1.0.136", features = ["derive"] }
|
||||
serde_json = "1.0.79"
|
||||
|
|
13
Dockerfile
13
Dockerfile
|
@ -1,12 +1,12 @@
|
|||
# Create the build container to compile
|
||||
FROM rust:latest as builder
|
||||
# Prepare container
|
||||
FROM rust:slim-buster
|
||||
RUN USER=root cargo new --bin lonk
|
||||
WORKDIR lonk
|
||||
WORKDIR /lonk
|
||||
|
||||
# Compile dependencies
|
||||
|
||||
COPY ./Cargo.lock ./Cargo.lock
|
||||
COPY ./Cargo.toml ./Cargo.toml
|
||||
COPY ./Cargo.lock /lonk/Cargo.lock
|
||||
COPY ./Cargo.toml /lonk/Cargo.toml
|
||||
|
||||
ARG PROFILE
|
||||
|
||||
|
@ -17,9 +17,10 @@ RUN rm src/*.rs
|
|||
COPY ./src ./src
|
||||
RUN rm ./target/${PROFILE:-release}/deps/lonk*
|
||||
RUN cargo build
|
||||
RUN cp /lonk/target/${PROFILE:-debug}/lonk /bin/lonk
|
||||
|
||||
# Execution container
|
||||
FROM rust:latest
|
||||
ARG PROFILE
|
||||
COPY --from=builder /lonk/target/${PROFILE:-release}/lonk .
|
||||
CMD ["./lonk"]
|
||||
CMD ["./lonk"]
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"db": {
|
||||
"address": "redis://db",
|
||||
"address": "redis://db:6379",
|
||||
"worker_threads": 4
|
||||
},
|
||||
"slug_rules": {
|
||||
|
@ -8,6 +8,12 @@
|
|||
"chars": "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-"
|
||||
},
|
||||
"serve_rules": {
|
||||
"Dir": "/data/served"
|
||||
"dir": {
|
||||
"Dir": "/data/served"
|
||||
},
|
||||
"addr": {
|
||||
"ip": "127.0.0.1",
|
||||
"port": 8080
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
version: '3.9'
|
||||
version: "3.9"
|
||||
services:
|
||||
lonk:
|
||||
build:
|
||||
|
@ -9,6 +9,8 @@ services:
|
|||
- LONK_CONFIG="/data/config.json"
|
||||
volumes:
|
||||
- ./data:/data
|
||||
ports:
|
||||
- 8080:8892
|
||||
redis:
|
||||
image: 'redis:alpine'
|
||||
command: redis-server --save 20 1 --loglevel warning
|
||||
|
|
123
src/main.rs
123
src/main.rs
|
@ -1,8 +1,10 @@
|
|||
use argh::FromArgs;
|
||||
use figment::{providers::Format, Figment};
|
||||
use core::panic;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{collections::BTreeSet, path::PathBuf, str::FromStr, sync::Arc, io::BufRead};
|
||||
use tokio::{sync};
|
||||
use std::{
|
||||
collections::BTreeSet, io::BufRead, net::IpAddr, path::PathBuf, str::FromStr, sync::Arc,
|
||||
};
|
||||
use tokio::sync;
|
||||
use validators::prelude::*;
|
||||
use warp::{filters::BoxedFilter, hyper::StatusCode, Filter};
|
||||
|
||||
|
@ -20,6 +22,9 @@ macro_rules! clone_to_move {
|
|||
clone_to_move!($x);
|
||||
clone_to_move!($y)
|
||||
};
|
||||
(mut $x:ident) => {
|
||||
let mut $x = $x.clone();
|
||||
};
|
||||
($x:ident) => {
|
||||
let $x = $x.clone();
|
||||
};
|
||||
|
@ -34,14 +39,14 @@ struct Url {
|
|||
|
||||
#[derive(Deserialize, Serialize, Debug, Clone)]
|
||||
struct DbConfig {
|
||||
pub address: String,
|
||||
pub worker_threads: usize,
|
||||
address: String,
|
||||
worker_threads: usize,
|
||||
}
|
||||
|
||||
impl Default for DbConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
address: "redis://127.0.0.1".to_string(),
|
||||
address: "redis://127.0.0.1:6379".to_string(),
|
||||
worker_threads: 4,
|
||||
}
|
||||
}
|
||||
|
@ -63,23 +68,54 @@ impl Default for SlugRules {
|
|||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug, Clone)]
|
||||
pub enum ServeRules {
|
||||
pub enum ServeDirRules {
|
||||
File(PathBuf),
|
||||
Dir(PathBuf),
|
||||
}
|
||||
|
||||
impl ServeRules {
|
||||
impl ServeDirRules {
|
||||
pub fn to_filter(&self) -> BoxedFilter<(warp::fs::File,)> {
|
||||
match self {
|
||||
ServeRules::File(file) => warp::fs::file(file.clone()).boxed(),
|
||||
ServeRules::Dir(dir) => warp::fs::dir(dir.clone()).boxed(),
|
||||
ServeDirRules::File(file) => warp::fs::file(file.clone()).boxed(),
|
||||
ServeDirRules::Dir(dir) => warp::fs::dir(dir.clone()).boxed(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ServeDirRules {
|
||||
fn default() -> Self {
|
||||
ServeDirRules::Dir(PathBuf::from_str("/etc/lonk/served").unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Validator, Clone)]
|
||||
#[validator(ip(local(Allow), port(Must)))]
|
||||
struct ServeAddr {
|
||||
ip: IpAddr,
|
||||
port: u16,
|
||||
}
|
||||
|
||||
impl Default for ServeAddr {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
ip: [127, 0, 0, 1].into(),
|
||||
port: 8080,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug, Clone)]
|
||||
struct ServeRules {
|
||||
dir: ServeDirRules,
|
||||
addr: ServeAddr,
|
||||
}
|
||||
|
||||
impl Default for ServeRules {
|
||||
fn default() -> Self {
|
||||
ServeRules::Dir(PathBuf::from_str("/etc/lonk/served").unwrap())
|
||||
Self {
|
||||
dir: Default::default(),
|
||||
addr: ServeAddr::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -130,24 +166,24 @@ impl SlugDatabase {
|
|||
// I'm not sure this is the best way to implement this.
|
||||
// (Alternatively: is there a better architecture?)
|
||||
let rx = Arc::new(sync::Mutex::new(rx));
|
||||
|
||||
|
||||
for _ in 0..worker_threads {
|
||||
let mut connection = client.get_connection().expect("Could not open connection to Redis server.");
|
||||
let mut connection = client
|
||||
.get_connection()
|
||||
.expect("Could not open connection to Redis server.");
|
||||
clone_to_move!(rx);
|
||||
tokio::spawn(async move {
|
||||
while let Some(msg) = {(*rx.lock().await).recv().await} {
|
||||
while let Some(msg) = { (*rx.lock().await).recv().await } {
|
||||
match msg {
|
||||
SlugDbMessage::Add(slug, url) => {
|
||||
todo!()
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
SlugDatabase {
|
||||
tx,
|
||||
}
|
||||
SlugDatabase { tx }
|
||||
}
|
||||
|
||||
fn insert_slug(&self, slug: Slug, url: Url) -> Result<(), ()> {
|
||||
|
@ -221,11 +257,45 @@ fn shorten(
|
|||
#[tokio::main]
|
||||
async fn serve() {
|
||||
// Read configuration
|
||||
let config_file = std::env::var("LONK_CONFIG").unwrap_or("lonk.json".to_string());
|
||||
let config: Config = Figment::new()
|
||||
.merge(figment::providers::Json::file(&config_file))
|
||||
.extract()
|
||||
.expect("Could not parse configuration file.");
|
||||
|
||||
let config: Config = {
|
||||
let config_file_name = std::env::var("LONK_CONFIG").unwrap_or("lonk.json".to_string());
|
||||
let config_file = std::fs::File::open(config_file_name.clone()).unwrap_or_else(|err| {
|
||||
match err.kind() {
|
||||
std::io::ErrorKind::NotFound => {
|
||||
panic!("Configuration file {} does not exist.", config_file_name)
|
||||
}
|
||||
std::io::ErrorKind::PermissionDenied => {
|
||||
panic!("Read permission to {} was denied.", config_file_name)
|
||||
}
|
||||
_ => panic!(
|
||||
"Error when trying to read configuration file {}: {}",
|
||||
config_file_name, err
|
||||
),
|
||||
};
|
||||
});
|
||||
let config_buf = std::io::BufReader::new(config_file);
|
||||
serde_json::from_reader(config_buf).unwrap_or_else(|err| match err.classify() {
|
||||
serde_json::error::Category::Io => panic!("IO error when reading configuration file."),
|
||||
serde_json::error::Category::Syntax => panic!(
|
||||
"Configuration file is syntactically incorrect.
|
||||
See {}:line {}, column {}.",
|
||||
&config_file_name,
|
||||
err.line(),
|
||||
err.column()
|
||||
),
|
||||
serde_json::error::Category::Data => panic!(
|
||||
"Error deserializing configuration file; expected different data type.
|
||||
See {}:line {}, column {}.",
|
||||
&config_file_name,
|
||||
err.line(),
|
||||
err.column()
|
||||
),
|
||||
serde_json::error::Category::Eof => {
|
||||
panic!("Unexpected end of file when reading configuration file.")
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
// Create slug factory
|
||||
let slug_factory = Arc::new(SlugFactory::from_rules(config.slug_rules));
|
||||
|
@ -233,12 +303,11 @@ async fn serve() {
|
|||
// Initialize database
|
||||
let db = {
|
||||
let client = redis::Client::open(config.db.address).expect("Error opening Redis database.");
|
||||
//let conn = Connection::open(config.db_location).expect("Could not open database.");
|
||||
Arc::new(SlugDatabase::from_client(client, config.db.worker_threads))
|
||||
};
|
||||
|
||||
// GET /
|
||||
let homepage = warp::path::end().and(config.serve_rules.to_filter());
|
||||
let homepage = warp::path::end().and(config.serve_rules.dir.to_filter());
|
||||
|
||||
// GET /shorten/:Base64WithoutPaddingUrl
|
||||
let shorten = warp::path!("shorten" / Base64WithoutPaddingUrl).map({
|
||||
|
@ -257,7 +326,9 @@ async fn serve() {
|
|||
|
||||
let routes = warp::get().and(homepage.or(shorten).or(link));
|
||||
|
||||
warp::serve(routes).run(([127, 0, 0, 1], 8892)).await;
|
||||
warp::serve(routes)
|
||||
.run((config.serve_rules.addr.ip, config.serve_rules.addr.port))
|
||||
.await;
|
||||
}
|
||||
|
||||
#[derive(FromArgs, PartialEq, Debug)]
|
||||
|
|
Loading…
Reference in New Issue