feat: load templates
This commit is contained in:
parent
5877c39bef
commit
6cd0313a01
5 changed files with 399 additions and 9 deletions
14
src/main.rs
14
src/main.rs
|
@ -18,13 +18,17 @@
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use actix_web::{middleware, web::Data, App, HttpServer};
|
use actix_web::{middleware, web::Data, App, HttpServer};
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
|
||||||
pub mod ctx;
|
pub mod ctx;
|
||||||
pub mod db;
|
pub mod db;
|
||||||
|
pub mod errors;
|
||||||
pub mod federate;
|
pub mod federate;
|
||||||
pub mod forge;
|
pub mod forge;
|
||||||
|
pub mod pages;
|
||||||
pub mod settings;
|
pub mod settings;
|
||||||
pub mod spider;
|
pub mod spider;
|
||||||
|
pub mod static_assets;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
@ -34,22 +38,30 @@ use crate::federate::{get_federate, ArcFederate};
|
||||||
use ctx::Ctx;
|
use ctx::Ctx;
|
||||||
use db::{sqlite, BoxDB};
|
use db::{sqlite, BoxDB};
|
||||||
use settings::Settings;
|
use settings::Settings;
|
||||||
|
use static_assets::FileMap;
|
||||||
|
|
||||||
|
pub use crate::pages::routes::PAGES;
|
||||||
|
|
||||||
|
pub const CACHE_AGE: u32 = 60 * 60 * 24 * 30; // one month, I think?
|
||||||
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||||
pub const PKG_NAME: &str = env!("CARGO_PKG_NAME");
|
pub const PKG_NAME: &str = env!("CARGO_PKG_NAME");
|
||||||
pub const GIT_COMMIT_HASH: &str = env!("GIT_HASH");
|
pub const GIT_COMMIT_HASH: &str = env!("GIT_HASH");
|
||||||
pub const DOMAIN: &str = "developer-starchart.forgeflux.org";
|
pub const DOMAIN: &str = "developer-starchart.forgeflux.org";
|
||||||
|
|
||||||
pub type ArcCtx = Arc<Ctx>;
|
pub type ArcCtx = Arc<Ctx>;
|
||||||
|
|
||||||
pub type WebCtx = Data<ArcCtx>;
|
pub type WebCtx = Data<ArcCtx>;
|
||||||
pub type WebData = Data<BoxDB>;
|
pub type WebData = Data<BoxDB>;
|
||||||
pub type WebFederate = Data<ArcFederate>;
|
pub type WebFederate = Data<ArcFederate>;
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
pub static ref FILES: FileMap = FileMap::new();
|
||||||
|
}
|
||||||
|
|
||||||
#[actix_rt::main]
|
#[actix_rt::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
let settings = Settings::new().unwrap();
|
let settings = Settings::new().unwrap();
|
||||||
pretty_env_logger::init();
|
pretty_env_logger::init();
|
||||||
|
lazy_static::initialize(&pages::TEMPLATES);
|
||||||
|
|
||||||
let ctx = Ctx::new(settings.clone()).await;
|
let ctx = Ctx::new(settings.clone()).await;
|
||||||
let db = sqlite::get_data(Some(settings.clone())).await;
|
let db = sqlite::get_data(Some(settings.clone())).await;
|
||||||
|
|
106
src/pages/errors.rs
Normal file
106
src/pages/errors.rs
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
/*
|
||||||
|
* ForgeFlux StarChart - A federated software forge spider
|
||||||
|
* Copyright (C) 2022 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
use actix_web::{
|
||||||
|
error::ResponseError,
|
||||||
|
http::{header::ContentType, StatusCode},
|
||||||
|
HttpResponse, HttpResponseBuilder,
|
||||||
|
};
|
||||||
|
use derive_more::Display;
|
||||||
|
use derive_more::Error;
|
||||||
|
use serde::*;
|
||||||
|
|
||||||
|
use super::TemplateFile;
|
||||||
|
use crate::errors::ServiceError;
|
||||||
|
|
||||||
|
pub const ERROR_KEY: &str = "error";
|
||||||
|
|
||||||
|
pub const ERROR_TEMPLATE: TemplateFile = TemplateFile::new("error_comp", "components/error.html");
|
||||||
|
pub fn register_templates(t: &mut tera::Tera) {
|
||||||
|
ERROR_TEMPLATE.register(t).expect(ERROR_TEMPLATE.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Render template with error context
|
||||||
|
pub trait CtxError {
|
||||||
|
fn with_error(&self, e: &ReadableError) -> String;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Debug, Display, Clone)]
|
||||||
|
#[display(fmt = "title: {} reason: {}", title, reason)]
|
||||||
|
pub struct ReadableError {
|
||||||
|
pub reason: String,
|
||||||
|
pub title: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ReadableError {
|
||||||
|
pub fn new(e: &ServiceError) -> Self {
|
||||||
|
let reason = format!("{}", e);
|
||||||
|
let title = format!("{}", e.status_code());
|
||||||
|
|
||||||
|
Self { reason, title }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Error, Display)]
|
||||||
|
#[display(fmt = "{}", readable)]
|
||||||
|
pub struct PageError<T> {
|
||||||
|
#[error(not(source))]
|
||||||
|
template: T,
|
||||||
|
readable: ReadableError,
|
||||||
|
#[error(not(source))]
|
||||||
|
error: ServiceError,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> fmt::Debug for PageError<T> {
|
||||||
|
#[cfg(not(tarpaulin_include))]
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.debug_struct("PageError")
|
||||||
|
.field("readable", &self.readable)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: CtxError> PageError<T> {
|
||||||
|
/// create new instance of [PageError] from a template and an error
|
||||||
|
pub fn new(template: T, error: ServiceError) -> Self {
|
||||||
|
let readable = ReadableError::new(&error);
|
||||||
|
Self {
|
||||||
|
error,
|
||||||
|
template,
|
||||||
|
readable,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(tarpaulin_include))]
|
||||||
|
impl<T: CtxError> ResponseError for PageError<T> {
|
||||||
|
fn error_response(&self) -> HttpResponse {
|
||||||
|
HttpResponseBuilder::new(self.status_code())
|
||||||
|
.content_type(ContentType::html())
|
||||||
|
.body(self.template.with_error(&self.readable))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn status_code(&self) -> StatusCode {
|
||||||
|
self.error.status_code()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generic result data structure
|
||||||
|
#[cfg(not(tarpaulin_include))]
|
||||||
|
pub type PageResult<V, T> = std::result::Result<V, PageError<T>>;
|
175
src/pages/mod.rs
Normal file
175
src/pages/mod.rs
Normal file
|
@ -0,0 +1,175 @@
|
||||||
|
/*
|
||||||
|
* ForgeFlux StarChart - A federated software forge spider
|
||||||
|
* Copyright (C) 2022 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
use actix_web::*;
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
use rust_embed::RustEmbed;
|
||||||
|
use serde::*;
|
||||||
|
use tera::*;
|
||||||
|
|
||||||
|
use crate::settings::Settings;
|
||||||
|
use crate::static_assets::ASSETS;
|
||||||
|
use crate::PAGES;
|
||||||
|
use crate::{GIT_COMMIT_HASH, VERSION};
|
||||||
|
|
||||||
|
mod errors;
|
||||||
|
pub mod routes;
|
||||||
|
|
||||||
|
pub struct TemplateFile {
|
||||||
|
pub name: &'static str,
|
||||||
|
pub path: &'static str,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TemplateFile {
|
||||||
|
pub const fn new(name: &'static str, path: &'static str) -> Self {
|
||||||
|
Self { name, path }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn register(&self, t: &mut Tera) -> std::result::Result<(), tera::Error> {
|
||||||
|
t.add_raw_template(self.name, &Templates::get_template(self).expect(self.name))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn register_from_file(&self, t: &mut Tera) -> std::result::Result<(), tera::Error> {
|
||||||
|
use std::path::Path;
|
||||||
|
t.add_template_file(Path::new("templates/").join(self.path), Some(self.name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const PAYLOAD_KEY: &str = "payload";
|
||||||
|
|
||||||
|
pub const BASE: TemplateFile = TemplateFile::new("base", "components/base.html");
|
||||||
|
pub const FOOTER: TemplateFile = TemplateFile::new("footer", "components/footer.html");
|
||||||
|
pub const PUB_NAV: TemplateFile = TemplateFile::new("pub_nav", "components/nav/pub.html");
|
||||||
|
pub const AUTH_NAV: TemplateFile = TemplateFile::new("auth_nav", "components/nav/auth.html");
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
pub static ref TEMPLATES: Tera = {
|
||||||
|
let mut tera = Tera::default();
|
||||||
|
for t in [BASE, FOOTER, PUB_NAV, AUTH_NAV].iter() {
|
||||||
|
t.register(&mut tera).unwrap();
|
||||||
|
}
|
||||||
|
errors::register_templates(&mut tera);
|
||||||
|
tera.autoescape_on(vec![".html", ".sql"]);
|
||||||
|
//auth::register_templates(&mut tera);
|
||||||
|
//gists::register_templates(&mut tera);
|
||||||
|
tera
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(RustEmbed)]
|
||||||
|
#[folder = "templates/"]
|
||||||
|
pub struct Templates;
|
||||||
|
|
||||||
|
impl Templates {
|
||||||
|
pub fn get_template(t: &TemplateFile) -> Option<String> {
|
||||||
|
match Self::get(t.path) {
|
||||||
|
Some(file) => Some(String::from_utf8_lossy(&file.data).into_owned()),
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ctx(s: &Settings) -> Context {
|
||||||
|
let mut ctx = Context::new();
|
||||||
|
let footer = Footer::new(s);
|
||||||
|
ctx.insert("footer", &footer);
|
||||||
|
ctx.insert("page", &PAGES);
|
||||||
|
ctx.insert("assets", &*ASSETS);
|
||||||
|
ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct Footer<'a> {
|
||||||
|
version: &'a str,
|
||||||
|
admin_email: &'a str,
|
||||||
|
source_code: &'a str,
|
||||||
|
git_hash: &'a str,
|
||||||
|
settings: &'a Settings,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Footer<'a> {
|
||||||
|
pub fn new(settings: &'a Settings) -> Self {
|
||||||
|
Self {
|
||||||
|
version: VERSION,
|
||||||
|
source_code: &settings.source_code,
|
||||||
|
admin_email: &settings.admin_email,
|
||||||
|
git_hash: &GIT_COMMIT_HASH[..8],
|
||||||
|
settings,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn services(cfg: &mut web::ServiceConfig) {}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn templates_work_basic() {
|
||||||
|
use super::*;
|
||||||
|
use tera::Tera;
|
||||||
|
|
||||||
|
let mut tera = Tera::default();
|
||||||
|
let mut tera2 = Tera::default();
|
||||||
|
for t in [
|
||||||
|
BASE, FOOTER, PUB_NAV,
|
||||||
|
AUTH_NAV,
|
||||||
|
// auth::AUTH_BASE,
|
||||||
|
// auth::login::LOGIN,
|
||||||
|
// auth::register::REGISTER,
|
||||||
|
// errors::ERROR_TEMPLATE,
|
||||||
|
// gists::GIST_BASE,
|
||||||
|
// gists::GIST_EXPLORE,
|
||||||
|
// gists::new::NEW_GIST,
|
||||||
|
]
|
||||||
|
.iter()
|
||||||
|
{
|
||||||
|
t.register_from_file(&mut tera2).unwrap();
|
||||||
|
t.register(&mut tera).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod http_page_tests {
|
||||||
|
use actix_web::http::StatusCode;
|
||||||
|
use actix_web::test;
|
||||||
|
|
||||||
|
use crate::ctx::Ctx;
|
||||||
|
use crate::db::BoxDB;
|
||||||
|
use crate::tests::*;
|
||||||
|
use crate::*;
|
||||||
|
|
||||||
|
use super::PAGES;
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn sqlite_templates_work() {
|
||||||
|
let (db, data) = sqlx_sqlite::get_ctx().await;
|
||||||
|
templates_work(data, db).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn templates_work(data: Arc<Data>, db: BoxDB) {
|
||||||
|
let app = get_app!(data, db).await;
|
||||||
|
|
||||||
|
for file in [PAGES.auth.login, PAGES.auth.register].iter() {
|
||||||
|
let resp = get_request!(&app, file);
|
||||||
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
97
src/pages/routes.rs
Normal file
97
src/pages/routes.rs
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
/*
|
||||||
|
* ForgeFlux StarChart - A federated software forge spider
|
||||||
|
* Copyright (C) 2022 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
/// constant [Pages](Pages) instance
|
||||||
|
pub const PAGES: Pages = Pages::new();
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
/// Top-level routes data structure for V1 AP1
|
||||||
|
pub struct Pages {
|
||||||
|
/// home page
|
||||||
|
pub home: &'static str,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pages {
|
||||||
|
/// create new instance of Routes
|
||||||
|
const fn new() -> Pages {
|
||||||
|
let home = "/";
|
||||||
|
Pages { home }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
/// Authentication routes
|
||||||
|
pub struct Auth {
|
||||||
|
/// logout route
|
||||||
|
pub logout: &'static str,
|
||||||
|
/// login route
|
||||||
|
pub login: &'static str,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Auth {
|
||||||
|
/// create new instance of Authentication route
|
||||||
|
pub const fn new() -> Auth {
|
||||||
|
let login = "/login";
|
||||||
|
let logout = "/logout";
|
||||||
|
Auth { login, logout }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//#[cfg(test)]
|
||||||
|
//mod tests {
|
||||||
|
// use super::*;
|
||||||
|
// #[test]
|
||||||
|
// fn gist_route_substitution_works() {
|
||||||
|
// const NAME: &str = "bob";
|
||||||
|
// const GIST: &str = "foo";
|
||||||
|
// const FILE: &str = "README.md";
|
||||||
|
// let get_profile = format!("/~{NAME}");
|
||||||
|
// let view_gist = format!("/~{NAME}/{GIST}");
|
||||||
|
// let post_comment = format!("/~{NAME}/{GIST}/comment");
|
||||||
|
// let get_file = format!("/~{NAME}/{GIST}/contents/{FILE}");
|
||||||
|
//
|
||||||
|
// let profile_component = GistProfilePathComponent { username: NAME };
|
||||||
|
//
|
||||||
|
// assert_eq!(get_profile, PAGES.gist.get_profile_route(profile_component));
|
||||||
|
//
|
||||||
|
// let profile_component = PostCommentPath {
|
||||||
|
// username: NAME.into(),
|
||||||
|
// gist: GIST.into(),
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// assert_eq!(view_gist, PAGES.gist.get_gist_route(&profile_component));
|
||||||
|
//
|
||||||
|
// let post_comment_path = PostCommentPath {
|
||||||
|
// gist: GIST.into(),
|
||||||
|
// username: NAME.into(),
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// assert_eq!(
|
||||||
|
// post_comment,
|
||||||
|
// PAGES.gist.get_post_comment_route(&post_comment_path)
|
||||||
|
// );
|
||||||
|
//
|
||||||
|
// let file_component = GetFilePath {
|
||||||
|
// username: NAME.into(),
|
||||||
|
// gist: GIST.into(),
|
||||||
|
// file: FILE.into(),
|
||||||
|
// };
|
||||||
|
// assert_eq!(get_file, PAGES.gist.get_file_route(&file_component));
|
||||||
|
// }
|
||||||
|
//}
|
|
@ -21,11 +21,11 @@ use std::{env, fs};
|
||||||
use config::{Config, ConfigError, Environment, File};
|
use config::{Config, ConfigError, Environment, File};
|
||||||
use derive_more::Display;
|
use derive_more::Display;
|
||||||
use log::warn;
|
use log::warn;
|
||||||
use serde::Deserialize;
|
use serde::{Deserialize, Serialize};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
use validator::Validate;
|
use validator::Validate;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
pub struct Server {
|
pub struct Server {
|
||||||
pub port: u32,
|
pub port: u32,
|
||||||
pub domain: String,
|
pub domain: String,
|
||||||
|
@ -40,7 +40,7 @@ impl Server {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Display, Clone, Debug)]
|
#[derive(Debug, Display, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "lowercase")]
|
#[serde(rename_all = "lowercase")]
|
||||||
pub enum LogLevel {
|
pub enum LogLevel {
|
||||||
#[display(fmt = "debug")]
|
#[display(fmt = "debug")]
|
||||||
|
@ -64,7 +64,7 @@ impl LogLevel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize)]
|
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
|
||||||
pub struct Repository {
|
pub struct Repository {
|
||||||
pub root: String,
|
pub root: String,
|
||||||
}
|
}
|
||||||
|
@ -83,7 +83,7 @@ impl Repository {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Display, PartialEq, Clone, Debug)]
|
#[derive(Debug, Display, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "lowercase")]
|
#[serde(rename_all = "lowercase")]
|
||||||
pub enum DBType {
|
pub enum DBType {
|
||||||
#[display(fmt = "postgres")]
|
#[display(fmt = "postgres")]
|
||||||
|
@ -102,14 +102,14 @@ impl DBType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
pub struct Database {
|
pub struct Database {
|
||||||
pub url: String,
|
pub url: String,
|
||||||
pub pool: u32,
|
pub pool: u32,
|
||||||
pub database_type: DBType,
|
pub database_type: DBType,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Validate, Clone, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
pub struct Crawler {
|
pub struct Crawler {
|
||||||
pub ttl: u64,
|
pub ttl: u64,
|
||||||
pub client_timeout: u64,
|
pub client_timeout: u64,
|
||||||
|
@ -117,7 +117,7 @@ pub struct Crawler {
|
||||||
pub wait_before_next_api_call: u64,
|
pub wait_before_next_api_call: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Validate, Clone, Deserialize)]
|
#[derive(Debug, Validate, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
pub struct Settings {
|
pub struct Settings {
|
||||||
pub log: LogLevel,
|
pub log: LogLevel,
|
||||||
pub database: Database,
|
pub database: Database,
|
||||||
|
|
Loading…
Reference in a new issue