initial commit of Hugotator POGGERS

This commit is contained in:
Matthieu Bessat 2022-12-18 00:51:23 +01:00
commit a99dfefeb3
8 changed files with 2846 additions and 0 deletions

1
.env Normal file
View file

@ -0,0 +1 @@
DATABASE_URL=sqlite://local.database

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
/target
tmp/*

2579
Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

17
Cargo.toml Normal file
View file

@ -0,0 +1,17 @@
[package]
name = "backend"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
actix-web = "^4.2"
sea-orm = { version = "^0", features = [ "sqlx-sqlite", "runtime-actix-native-tls", "macros" ] }
serde = { version = "^1", features = [ "derive" ] }
toml = { version = "^0.5" }
chrono = { version = "^0.4", features = [ "unstable-locales" ] }
git2 = { version = "0.15" }
log = { version = "^0.4" }

110
src/init_site.rs Normal file
View file

@ -0,0 +1,110 @@
use git2::{Repository, Error as Git2Error, Remote};
use git2::string_array::StringArray;
use super::{SiteConfig, Site};
use std::path::Path;
use crate::utils::{unwrap_opt_or_return, unwrap_or_return};
use log::error;
use log::info;
use log::warn;
const REPOSITORIES_CONTAINER_PATH: &str = "./tmp/repositories";
#[derive(Debug)]
struct DetailledRemote {
slug: String, // expected to be origin
url: String
}
#[derive(Debug)]
pub enum DetailledRemotesErr {
GitError(Git2Error),
CannotGetRemoteUrl
}
fn get_detailled_remotes(repo_handle: &Repository) -> Result<Vec<DetailledRemote>, DetailledRemotesErr> {
let raw_remotes: StringArray = match repo_handle.remotes() {
Ok(res) => res,
Err(err) => {
return Err(DetailledRemotesErr::GitError(err));
}
};
let mut detailled_remotes: Vec<DetailledRemote> = vec![];
for raw_remote in raw_remotes.iter().flatten() {
let slug = raw_remote.to_string();
let remote: Remote = unwrap_or_return!(
repo_handle.find_remote(&slug),
|err| DetailledRemotesErr::GitError(err)
);
let url = unwrap_opt_or_return!(remote.url(), DetailledRemotesErr::CannotGetRemoteUrl).to_string();
detailled_remotes.push(DetailledRemote {
slug,
url
})
}
Ok(detailled_remotes)
}
#[derive(Debug)]
pub enum InitSiteErr {
RepositoryCloneErr(Git2Error),
ExistingRepositoryInvalid(Git2Error),
ExistingRepositoryCannotGetRemotes(DetailledRemotesErr),
ExistingRepositoryInvalidRemoteLayout,
ExistingRepositoryInvalidMainRemoteUrl
}
pub fn init_site(site_conf: SiteConfig) -> Result<Site, InitSiteErr> {
let remote_url = &site_conf.git_remote_url;
// check if the path exists
// check if the path contain an actual repository refering to the same remote url
// clone the repo
// check if the cloned repo contain an actual hugo website (config.toml must be presents)
// try to find the binding in the `content` directory, check that the stru
let clone_destination: String = format!("{REPOSITORIES_CONTAINER_PATH}/{0}", site_conf.slug);
let repo = if !Path::new(&clone_destination).exists() {
match Repository::clone(&remote_url, clone_destination) {
Ok(repo) => repo,
Err(e) => {
return Err(InitSiteErr::RepositoryCloneErr(e))
}
}
} else {
match Repository::open(clone_destination) {
Ok(repo) => {
let remotes_details =
unwrap_or_return!(get_detailled_remotes(&repo), |e| InitSiteErr::ExistingRepositoryCannotGetRemotes(e));
// make sure that the origin remote is present and match the configured remote url
match remotes_details.iter().find(|r| r.slug == "origin") {
None => {
return Err(InitSiteErr::ExistingRepositoryInvalidRemoteLayout)
},
Some(DetailledRemote { url, .. }) if url != remote_url => {
dbg!(url);
dbg!(remote_url);
return Err(InitSiteErr::ExistingRepositoryInvalidMainRemoteUrl);
}
_ => ()
}
repo
},
Err(e) => {
return Err(InitSiteErr::ExistingRepositoryInvalid(e))
}
}
};
dbg!(&repo.state());
Ok(Site {
config: site_conf
})
}

110
src/main.rs Normal file
View file

@ -0,0 +1,110 @@
mod init_site;
mod utils;
use actix_web::{get, post, web, App, HttpResponse, HttpServer, Responder};
use chrono::prelude::{DateTime, Utc};
use serde::Deserialize;
use toml;
use std::fs;
#[derive(Debug)]
struct Post {
id: u64,
title: String,
description: String,
content: String,
created_at: DateTime<Utc>
}
#[get("/")]
async fn hello() -> impl Responder {
HttpResponse::Ok().body("Ignition sequence started")
}
#[get("/posts")]
async fn get_posts() -> impl Responder {
HttpResponse::Ok().body("test: get posts")
}
#[post("/echo")]
async fn echo(req_body: String) -> impl Responder {
HttpResponse::Ok().body(req_body)
}
#[derive(Debug, Clone)]
struct AppState {
}
#[derive(Default, Clone, Deserialize, Debug)]
struct ServerConfig {
host: Option<String>,
port: Option<u16>
}
#[derive(Clone, Deserialize, Debug)]
enum PostingKind {
/// a micro-bloging kind of post (less than 80 words)
Micro,
/// a full article (more than 80 words)
Article,
}
/// A hugo directory under `content`
#[derive(Clone, Deserialize, Debug)]
struct SiteContentBindingConfig {
slug: String,
posting_kind: PostingKind
}
#[derive(Clone, Deserialize, Debug)]
pub struct SiteConfig {
slug: String, // for example "werobot_blog"
git_remote_url: String,
content_path: String,
content_bindings: Vec<SiteContentBindingConfig>
}
#[derive(Debug)]
pub struct Site {
config: SiteConfig
}
#[derive(Clone, Deserialize, Debug)]
struct Config {
server: Option<ServerConfig>,
sites: Vec<SiteConfig>,
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let config: Config = toml::from_str(
&fs::read_to_string("./tmp/config.example.toml")?
)?;
dbg!(&config);
// initialize all the sites
for site_conf in config.sites {
println!("Initializing site {}...", site_conf.slug);
let site_res = init_site::init_site(site_conf);
dbg!(site_res);
}
let bind_config = (
config.server.as_ref().and_then(|sc| sc.host.clone()).unwrap_or_else(|| "127.0.0.1".to_string()),
config.server.as_ref().and_then(|sc| sc.port).unwrap_or(6968),
);
HttpServer::new(|| {
App::new()
.service(hello)
.service(echo)
.service(get_posts)
})
.bind(bind_config)?
.run()
.await
}

3
src/scan.rs Normal file
View file

@ -0,0 +1,3 @@
pub fn scan_directory() {
}

24
src/utils.rs Normal file
View file

@ -0,0 +1,24 @@
macro_rules! unwrap_or_return {
( $to_match: expr, $err_func: expr ) => {
match $to_match {
Ok(res) => res,
Err(err) => {
return Err($err_func(err))
}
}
}
}
pub(crate) use unwrap_or_return;
macro_rules! unwrap_opt_or_return {
( $e: expr, $err: expr ) => {
match $e {
Some(res) => res,
None => {
return Err($err);
}
}
}
}
pub(crate) use unwrap_opt_or_return;