diff --git a/.gitignore b/.gitignore index 4d2be69..33daff8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,7 @@ .idea -/target \ No newline at end of file +/target +# /nginx +database +database.bak +.env +proxies.txt diff --git a/Caddyfile b/Caddyfile index 0a55afb..10f884b 100644 --- a/Caddyfile +++ b/Caddyfile @@ -1,4 +1,4 @@ -http://127.0.0.1:8001 { +http://127.0.0.1:1235 { route /static/* { uri strip_prefix /static file_server { @@ -6,10 +6,10 @@ http://127.0.0.1:8001 { } } - reverse_proxy 127.0.0.1:5843 + reverse_proxy http://auth-frontend:5843 } -http://127.0.0.1:8002 { +http://127.0.0.1:1234 { route /static/* { uri strip_prefix /static file_server { @@ -17,5 +17,5 @@ http://127.0.0.1:8002 { } } - reverse_proxy 127.0.0.1:5842 + reverse_proxy http://frontend:5842 } diff --git a/Dockerfile.auth-frontend b/Dockerfile.auth-frontend new file mode 100644 index 0000000..053cc99 --- /dev/null +++ b/Dockerfile.auth-frontend @@ -0,0 +1,19 @@ +FROM rust:1.89.0 AS builder + +WORKDIR /usr/src/asklyphe/ +COPY asklyphe-auth-frontend asklyphe-auth-frontend +COPY asklyphe-common asklyphe-common +COPY lyphedb lyphedb + +RUN --mount=type=cache,target=$CARGO_HOME/registry,sharing=locked \ + --mount=type=cache,target=/usr/src/asklyphe/asklyphe-auth-frontend/target \ + --mount=type=cache,target=$CARGO_HOME/git/db,sharing=locked \ + cargo install --debug --path asklyphe-auth-frontend/ + +FROM debian:trixie-slim +RUN apt-get update && apt-get upgrade -y && apt-get install -y libssl3 && rm -rf /var/lib/apt-get/lists/* +COPY --from=builder /usr/local/cargo/bin/asklyphe-auth-frontend /usr/local/bin/ +COPY --from=builder /usr/src/asklyphe/asklyphe-auth-frontend/static /data/static +VOLUME /data + +CMD ["asklyphe-auth-frontend"] diff --git a/Dockerfile.authservice b/Dockerfile.authservice new file mode 100644 index 0000000..ab340c3 --- /dev/null +++ b/Dockerfile.authservice @@ -0,0 +1,24 @@ +FROM rust:1.89.0 AS builder + +WORKDIR /usr/src/asklyphe/ +COPY authservice authservice +COPY asklyphe-common asklyphe-common +COPY lyphedb lyphedb + + +RUN --mount=type=cache,target=$CARGO_HOME/registry,sharing=locked \ + --mount=type=cache,target=/usr/src/asklyphe/authservice/target \ + --mount=type=cache,target=$CARGO_HOME/git/db,sharing=locked \ + cargo install --debug --path authservice/ + +RUN --mount=type=cache,target=$CARGO_HOME/registry,sharing=locked \ + --mount=type=cache,target=/usr/src/asklyphe/authservice/migration/target \ + --mount=type=cache,target=$CARGO_HOME/git/db,sharing=locked \ + cargo install --debug --path authservice/migration/ + +FROM debian:trixie-slim +RUN apt-get update && apt-get upgrade -y && apt-get install -y libssl3 && rm -rf /var/lib/apt-get/lists/* +COPY --from=builder /usr/local/cargo/bin/authservice /usr/local/bin/ +COPY --from=builder /usr/local/cargo/bin/migration /usr/local/bin/ + +CMD ["authservice"] diff --git a/Dockerfile.bingservice b/Dockerfile.bingservice new file mode 100644 index 0000000..94cf1d0 --- /dev/null +++ b/Dockerfile.bingservice @@ -0,0 +1,17 @@ +FROM rust:1.89.0 AS builder + +WORKDIR /usr/src/asklyphe/ +COPY bingservice bingservice +COPY asklyphe-common asklyphe-common +COPY lyphedb lyphedb + +RUN --mount=type=cache,target=$CARGO_HOME/registry,sharing=locked \ + --mount=type=cache,target=/usr/src/asklyphe/bingservice/target \ + --mount=type=cache,target=$CARGO_HOME/git/db,sharing=locked \ + cargo install --debug --path bingservice/ + +FROM debian:trixie-slim +RUN apt-get update && apt-get upgrade -y && apt-get install -y libssl3 && rm -rf /var/lib/apt-get/lists/* +COPY --from=builder /usr/local/cargo/bin/bingservice /usr/local/bin/ + +CMD ["bingservice"] diff --git a/Dockerfile.frontend b/Dockerfile.frontend new file mode 100644 index 0000000..6aaa3e1 --- /dev/null +++ b/Dockerfile.frontend @@ -0,0 +1,20 @@ +FROM rust:1.89.0 AS builder + +WORKDIR /usr/src/asklyphe/ +COPY asklyphe-frontend asklyphe-frontend +COPY asklyphe-common asklyphe-common +COPY lyphedb lyphedb +COPY unit_converter unit_converter + +RUN --mount=type=cache,target=$CARGO_HOME/registry,sharing=locked \ + --mount=type=cache,target=/usr/src/asklyphe/asklyphe-frontend/target \ + --mount=type=cache,target=$CARGO_HOME/git/db,sharing=locked \ + cargo install --debug --path asklyphe-frontend/ + +FROM debian:trixie-slim +RUN apt-get update && apt-get upgrade -y && apt-get install -y libssl3 && rm -rf /var/lib/apt-get/lists/* +COPY --from=builder /usr/local/cargo/bin/asklyphe-frontend /usr/local/bin/ +COPY --from=builder /usr/src/asklyphe/asklyphe-frontend/static /data/static +VOLUME /data + +CMD ["asklyphe-frontend"] diff --git a/Dockerfile.googleservice b/Dockerfile.googleservice new file mode 100644 index 0000000..a4fd902 --- /dev/null +++ b/Dockerfile.googleservice @@ -0,0 +1,17 @@ +FROM rust:1.89.0 AS builder + +WORKDIR /usr/src/asklyphe/ +COPY googleservice googleservice +COPY asklyphe-common asklyphe-common +COPY lyphedb lyphedb + +RUN --mount=type=cache,target=$CARGO_HOME/registry,sharing=locked \ + --mount=type=cache,target=/usr/src/asklyphe/googleservice/target \ + --mount=type=cache,target=$CARGO_HOME/git/db,sharing=locked \ + cargo install --debug --path googleservice/ + +FROM debian:trixie-slim +RUN apt-get update && apt-get upgrade -y && apt-get install -y libssl3 && rm -rf /var/lib/apt-get/lists/* +COPY --from=builder /usr/local/cargo/bin/googleservice /usr/local/bin/ + +CMD ["googleservice"] diff --git a/Dockerfile.vorebot b/Dockerfile.vorebot new file mode 100644 index 0000000..b0d54b7 --- /dev/null +++ b/Dockerfile.vorebot @@ -0,0 +1,17 @@ +FROM rust:1.89.0 AS builder + +WORKDIR /usr/src/asklyphe/ +COPY vorebot vorebot +COPY asklyphe-common asklyphe-common +COPY lyphedb lyphedb + +RUN --mount=type=cache,target=$CARGO_HOME/registry,sharing=locked \ + --mount=type=cache,target=/usr/src/asklyphe/vorebot/target \ + --mount=type=cache,target=$CARGO_HOME/git/db,sharing=locked \ + cargo install --debug --path vorebot/ + +FROM debian:trixie-slim +RUN apt-get update && apt-get upgrade -y && apt-get install -y libssl3 && rm -rf /var/lib/apt-get/lists/* +COPY --from=builder /usr/local/cargo/bin/vorebot /usr/local/bin/ + +CMD ["vorebot"] diff --git a/asklyphe-auth-frontend/src/main.rs b/asklyphe-auth-frontend/src/main.rs index cd16d94..d8324f0 100644 --- a/asklyphe-auth-frontend/src/main.rs +++ b/asklyphe-auth-frontend/src/main.rs @@ -17,7 +17,7 @@ mod login; use std::{env, process}; use std::collections::HashMap; -use std::net::SocketAddr; +use std::net::{SocketAddr, ToSocketAddrs}; use std::ops::Deref; use std::sync::Arc; use std::sync::atomic::{AtomicU64, Ordering}; @@ -66,7 +66,7 @@ async fn main() { env_logger::init(); let opts = Opts { bind_addr: env::var("BIND_ADDR").unwrap_or("0.0.0.0:5843".to_string()).parse().expect("Badly formed BIND_ADDR (Needs to be SocketAddr)"), - nats_addr: env::var("NATS_ADDR").unwrap_or("127.0.0.1:4222".to_string()).parse().expect("Badly formed NATS_ADDR (Needs to be SocketAddr)"), + nats_addr: env::var("NATS_ADDR").unwrap_or("127.0.0.1:4222".to_string()).to_socket_addrs().expect("Badly formed NATS_ADDR (Needs to be SocketAddr)").nth(0).unwrap(), nats_cert: env::var("NATS_CERT").expect("NATS_CERT needs to be set"), nats_key: env::var("NATS_KEY").expect("NATS_KEY needs to be set"), asklyphe_url: env::var("ASKLYPHE_URL").unwrap_or("https://asklyphe.com".to_string()), diff --git a/asklyphe-frontend/src/bangs.rs b/asklyphe-frontend/src/bangs.rs new file mode 100644 index 0000000..ddd383b --- /dev/null +++ b/asklyphe-frontend/src/bangs.rs @@ -0,0 +1,106 @@ +use tracing::{debug, error}; +use once_cell::sync::Lazy; +use std::collections::BTreeMap; +use url_encoded_data; + +pub static BANG_PREFIX: &str = "!"; + +#[derive(Debug)] +struct Bang<'a> { + pub url: &'a str, + pub aliases: &'a [&'a str] +} + +impl<'a> Bang<'_> { + fn new(url: &'a str, aliases: &'a [&'a str]) -> Bang<'a> { + Bang {url, aliases} + } +} + +static BUILTIN_BANGS: Lazy> = Lazy::new(|| { + let mut bangs = BTreeMap::new(); + bangs.insert("Google", Bang::new("https://google.com/search?q={}", &["g", "google"] as &[&str])); + + bangs.insert("DuckDuckGo", Bang::new("https://duckduckgo.com/?q={}", &["d", "ddg", "duckduckgo"] as &[&str])); + + bangs.insert("Wikipedia", Bang::new("https://wikipedia.org/w/index.php?search={}", &["w", "wiki", "wikipedia"] as &[&str])); + bangs +}); + +#[derive(Debug, Clone)] +struct BangLoc<'b> { + pub url: &'b str, + pub start_idx: usize, + pub len: usize +} + +impl<'b> BangLoc<'_> { + fn new(url: &'b str, start_idx: usize, len: usize) -> BangLoc<'b> { + BangLoc {url, start_idx, len} + } +} + +pub fn redirect_bang(query: &String) -> Option { + if !query.contains(BANG_PREFIX) { + return None; + } + let bangs = query.match_indices(BANG_PREFIX).filter(|(bang_start_idx, _)| { + if *bang_start_idx == 0 || query.chars().nth(*bang_start_idx - 1).unwrap().is_whitespace() { + true + } else { + false + } + }).map(|(bang_start_idx, _)| { + let rest = query.get(bang_start_idx + 1..query.len()).unwrap(); + BUILTIN_BANGS.iter().map(|(_, bang)| { + let alias = bang.aliases.iter() + .filter(|alias| rest.starts_with(**alias)) + .filter( + |alias| rest.chars() + .nth(alias.len()) + .unwrap_or(' ') + .is_whitespace()) + .max_by(|a, b| a.len().cmp(&b.len()))?; + Some(BangLoc::new(bang.url, bang_start_idx, alias.len())) + }).filter(|bang| bang.is_some()).map(|bang| bang.unwrap()).next() + }).filter(|bang| bang.is_some()) + .map(|bang| bang.unwrap()) + .collect::>(); + + + let bang = bangs.first()?; + let end_idx = { + let mut end_idx = bang.start_idx + 1 + bang.len; + if end_idx < query.len() { + end_idx += 1; + } + end_idx + }; + + let start_idx = if end_idx == query.len() && bang.start_idx > 0 { + bang.start_idx - 1 + } else { + bang.start_idx + }; + + + let query_split = query.split_once(query.get(start_idx..end_idx).unwrap()).unwrap(); + + let query_trimmed = format!("{}{}", query_split.0, query_split.1); + + // A hack to get URL escaping without using a proper URL layout, hopefully has no other issues apart from prepending '=' to the string + let query_encoded = url_encoded_data::stringify(&[("", query_trimmed.as_str())]); + let query_encoded = query_encoded.get(1..query_encoded.len()).unwrap().to_owned(); + + + let bang_url_split = bang.url.split_once("{}").unwrap(); + + let bang_url = format!( + "{}{}{}", + bang_url_split.0, + query_encoded, + bang_url_split.1 + ); + + Some(bang_url) +} diff --git a/asklyphe-frontend/src/main.rs b/asklyphe-frontend/src/main.rs index 59e5e40..1f4a038 100644 --- a/asklyphe-frontend/src/main.rs +++ b/asklyphe-frontend/src/main.rs @@ -14,12 +14,13 @@ pub mod searchbot; pub mod wikipedia; pub mod unit_converter; +pub mod bangs; pub mod math; pub mod routes; use std::{env, process}; use std::collections::HashMap; -use std::net::SocketAddr; +use std::net::{SocketAddr, ToSocketAddrs}; use std::ops::Deref; use std::sync::Arc; use std::sync::atomic::{AtomicU64, Ordering}; @@ -85,7 +86,7 @@ async fn main() { env_logger::init(); let opts = Opts { bind_addr: env::var("BIND_ADDR").unwrap_or("0.0.0.0:5842".to_string()).parse().expect("Badly formed BIND_ADDR (Needs to be SocketAddr)"), - nats_addr: env::var("NATS_ADDR").unwrap_or("127.0.0.1:4222".to_string()).parse().expect("Badly formed NATS_ADDR (Needs to be SocketAddr)"), + nats_addr: env::var("NATS_ADDR").unwrap_or("127.0.0.1:4222".to_string()).to_socket_addrs().expect("Badly formed NATS_ADDR (Needs to be SocketAddr)").nth(0).unwrap(), nats_cert: env::var("NATS_CERT").expect("NATS_CERT needs to be set"), nats_key: env::var("NATS_KEY").expect("NATS_KEY needs to be set"), auth_url: env::var("AUTH_URL").unwrap_or("https://auth.asklyphe.com".to_string()), diff --git a/asklyphe-frontend/src/routes/search.rs b/asklyphe-frontend/src/routes/search.rs index b1ff9a7..4e66275 100644 --- a/asklyphe-frontend/src/routes/search.rs +++ b/asklyphe-frontend/src/routes/search.rs @@ -18,6 +18,7 @@ use crate::unit_converter; use crate::unit_converter::UnitConversion; use crate::wikipedia::WikipediaSummary; use crate::{wikipedia, Opts, ALPHA, BUILT_ON, GIT_COMMIT, VERSION, YEAR}; +use crate::bangs; use crate::math; use crate::math::Calculation; use askama::Template; @@ -174,11 +175,15 @@ pub async fn search_js( let unit_comp = unit_converter::convert_unit(&unit_query); complications.unit_converter = unit_comp; + let bang_redirect = bangs::redirect_bang(&query); + if let Some(redirect) = bang_redirect { + return Redirect::to(&redirect).into_response(); + } + let mut calc_query = query.clone().to_lowercase(); calc_query = calc_query.replace("calculate", "").replace("what is", ""); let math = math::calculate(&calc_query); complications.math = math; - } else { complications.disabled = true; query = query.replace("-complications", ""); @@ -291,6 +296,11 @@ pub async fn search_nojs( unit_query = unit_query.replace("metre", "meter"); let unit_comp = unit_converter::convert_unit(&unit_query); complications.unit_converter = unit_comp; + + let bang_redirect = bangs::redirect_bang(&query); + if let Some(redirect) = bang_redirect { + return Redirect::to(&redirect).into_response(); + } } else { complications.disabled = true; query = query.replace("-complications", ""); diff --git a/authservice/src/db/user.rs b/authservice/src/db/user.rs index 989b58d..106060b 100644 --- a/authservice/src/db/user.rs +++ b/authservice/src/db/user.rs @@ -458,7 +458,7 @@ pub async fn user_count(db: &DatabaseConnection) -> Result Result { // dont fucking touch this, i don't know why it works but it does, it's actually evil // note: doesn't work - Ok(user::Entity::find().filter(user::Column::Flags.into_expr().binary(BinOper::LShift, Expr::value(63 - 2)).lt(1 << (63 - 2))) + Ok(user::Entity::find().filter(user::Column::Flags.into_expr().binary(BinOper::BitAnd, UserFlag::Administrator as i64).binary(BinOper::NotEqual, 0)) .count(db).await.map_err(|e| { error!("DATABASE ERROR WHILE ADMINCOUNT: {e}"); FetchUserError::DatabaseError diff --git a/bingservice/src/main.rs b/bingservice/src/main.rs index e8223fd..5fce86a 100644 --- a/bingservice/src/main.rs +++ b/bingservice/src/main.rs @@ -34,7 +34,7 @@ pub static PROXIES: Lazy> = Lazy::new(|| { let contents = std::fs::read_to_string(proxy_file); let mut proxies = vec![]; - for line in contents.expect("FAILED TO READ FILE").lines() { + for line in contents.expect("FAILED TO READ FILE").lines().filter(|l| l.len() > 0) { proxies.push(Proxy::from_str(line).expect("INVALID PROXY")); } diff --git a/bingservice/src/proxy.rs b/bingservice/src/proxy.rs index 4559a95..ce0c568 100644 --- a/bingservice/src/proxy.rs +++ b/bingservice/src/proxy.rs @@ -30,6 +30,7 @@ impl FromStr for Proxy { fn from_str(s: &str) -> Result { let mut parts = s.split(':'); + let protocol = parts.next().ok_or(ProxyError::InvalidProxyFormat)?; let host = parts.next().ok_or(ProxyError::InvalidProxyFormat)?; let port = parts.next().ok_or(ProxyError::InvalidProxyFormat)?; let auth = if let Some(user) = parts.next() { @@ -39,8 +40,8 @@ impl FromStr for Proxy { None }; Ok(Proxy { - address: format!("{}:{}", host, port), + address: format!("{}://{}:{}", protocol, host, port), credentials: auth, }) } -} \ No newline at end of file +} diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..089694d --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,129 @@ +services: + auth-frontend: + restart: unless-stopped + networks: + - lyphenet + env_file: ".env" + depends_on: + - nats + - authservice + build: + dockerfile: Dockerfile.auth-frontend + volumes: + - auth_frontend_data:/data + image: asklyphe/auth-frontend + authservice: + restart: unless-stopped + networks: + - lyphenet + env_file: ".env" + depends_on: + - nats + build: + dockerfile: Dockerfile.authservice + image: asklyphe/authservice + frontend: + restart: unless-stopped + networks: + - lyphenet + env_file: ".env" + depends_on: + - nats + build: + dockerfile: Dockerfile.frontend + volumes: + - frontend_data:/data + image: asklyphe/frontend +# vorebot: +# restart: unless-stopped +# networks: +# - lyphenet +# - outer +# env_file: ".env" +# depends_on: +# - nats +# build: +# dockerfile: Dockerfile.vorebot +# image: asklyphe/vorebot + + bingservice: + restart: unless-stopped + networks: + - lyphenet + - outer + env_file: ".env" + depends_on: + - nats + build: + dockerfile: Dockerfile.bingservice + volumes: + - ./proxies.txt:/data/proxies.txt + image: asklyphe/bingservice + googleservice: + restart: unless-stopped + networks: + - lyphenet + - outer + env_file: ".env" + depends_on: + - nats + - bingservice + build: + dockerfile: Dockerfile.googleservice + image: asklyphe/googleservice + + nats: + restart: unless-stopped + networks: + - lyphenet + depends_on: + - db + env_file: ".env" + image: nats:2.11.8 + command: "-js" + db: + image: postgres:17 + networks: + - lyphenet + env_file: ".env" + healthcheck: + test: ['CMD', 'pg_isready', '-U', 'postgres'] + volumes: + - ./database:/var/lib/postgresql/data + proxy: + image: nginx:latest + networks: + - lyphenet + - outer + depends_on: + - frontend + - auth-frontend + volumes: + - ./nginx:/etc/nginx/conf.d + - frontend_data:/data/frontend + - auth_frontend_data:/data/auth-frontend + ports: + - "1234:80" + - "1235:81" +# caddy: +# image: caddy:latest +# networks: +# - lyphenet +# - outer +# depends_on: +# - frontend +# - auth-frontend +# volumes: +# - ./Caddyfile:/etc/caddy/Caddyfile +# ports: +# - 1234:1234 +# - 1235:1235 + +networks: + outer: + lyphenet: + internal: true + +volumes: + frontend_data: + auth_frontend_data: diff --git a/nginx/default.conf b/nginx/default.conf new file mode 100644 index 0000000..aea95c8 --- /dev/null +++ b/nginx/default.conf @@ -0,0 +1,27 @@ +server { + listen 81; + server_name 0.0.0.0; + access_log off; + + location /static/ { + root /data/auth-frontend; + } + + location / { + proxy_pass http://auth-frontend:5843; + } +} + +server { + listen 80; + server_name 0.0.0.0; + access_log off; + + location /static/ { + root /data/frontend; + } + + location / { + proxy_pass http://frontend:5842; + } +}