initial commit of Hugotator POGGERS
This commit is contained in:
commit
a99dfefeb3
8 changed files with 2846 additions and 0 deletions
1
.env
Normal file
1
.env
Normal file
|
@ -0,0 +1 @@
|
|||
DATABASE_URL=sqlite://local.database
|
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
/target
|
||||
tmp/*
|
2579
Cargo.lock
generated
Normal file
2579
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
17
Cargo.toml
Normal file
17
Cargo.toml
Normal 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
110
src/init_site.rs
Normal 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
110
src/main.rs
Normal 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
3
src/scan.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
pub fn scan_directory() {
|
||||
|
||||
}
|
24
src/utils.rs
Normal file
24
src/utils.rs
Normal 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;
|
Loading…
Reference in a new issue