initial
Some checks failed
Run tests / cargo_test (push) Failing after 3m5s
Run tests / cargo_clippy (push) Failing after 3m4s
Run tests / cargo_fmt (push) Failing after 3m3s

This commit is contained in:
mrrpnya 2025-02-01 18:58:32 -08:00
parent 1124de1673
commit 7d0e1d2c73
15 changed files with 960 additions and 43 deletions

2
.env Normal file
View file

@ -0,0 +1,2 @@
# create sqlite if it doesn't already exist
DATABASE_URL=sqlite:///./test.db

View file

@ -0,0 +1,46 @@
name: Run tests
on:
push:
branches:
- "*"
pull_request:
branches:
- "*"
jobs:
cargo_test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Rust
uses: actions-rs/toolchain@v1
with:
toolchain: stable
override: true
- name: Run tests
run: cargo test --all
cargo_clippy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Rust
uses: actions-rs/toolchain@v1
with:
toolchain: stable
override: true
- name: Run clippy
run: cargo clippy --all -- -D warnings
cargo_fmt:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Rust
uses: actions-rs/toolchain@v1
with:
toolchain: stable
override: true
- name: Run fmt
run: cargo fmt --all -- --check

392
Cargo.lock generated
View file

@ -237,6 +237,21 @@ dependencies = [
"alloc-no-stdlib",
]
[[package]]
name = "android-tzdata"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
[[package]]
name = "android_system_properties"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
dependencies = [
"libc",
]
[[package]]
name = "arraydeque"
version = "0.5.1"
@ -326,6 +341,12 @@ dependencies = [
"alloc-stdlib",
]
[[package]]
name = "bumpalo"
version = "3.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
[[package]]
name = "byteorder"
version = "1.5.0"
@ -364,6 +385,20 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
version = "0.4.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825"
dependencies = [
"android-tzdata",
"iana-time-zone",
"js-sys",
"num-traits",
"wasm-bindgen",
"windows-targets",
]
[[package]]
name = "config"
version = "0.15.6"
@ -429,6 +464,12 @@ dependencies = [
"version_check",
]
[[package]]
name = "core-foundation-sys"
version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
[[package]]
name = "cpufeatures"
version = "0.2.16"
@ -463,6 +504,41 @@ dependencies = [
"typenum",
]
[[package]]
name = "darling"
version = "0.20.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989"
dependencies = [
"darling_core",
"darling_macro",
]
[[package]]
name = "darling_core"
version = "0.20.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5"
dependencies = [
"fnv",
"ident_case",
"proc-macro2",
"quote",
"strsim",
"syn",
]
[[package]]
name = "darling_macro"
version = "0.20.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
dependencies = [
"darling_core",
"quote",
"syn",
]
[[package]]
name = "deranged"
version = "0.3.11"
@ -485,6 +561,43 @@ dependencies = [
"syn",
]
[[package]]
name = "diesel"
version = "2.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccf1bedf64cdb9643204a36dd15b19a6ce8e7aa7f7b105868e9f1fad5ffa7d12"
dependencies = [
"bitflags",
"byteorder",
"diesel_derives",
"itoa",
"libsqlite3-sys",
"pq-sys",
"time",
]
[[package]]
name = "diesel_derives"
version = "2.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7f2c3de51e2ba6bf2a648285696137aaf0f5f487bcbea93972fe8a364e131a4"
dependencies = [
"diesel_table_macro_syntax",
"dsl_auto_type",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "diesel_table_macro_syntax"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "209c735641a413bc68c4923a9d6ad4bcb3ca306b794edaa7eb0b3228a99ffb25"
dependencies = [
"syn",
]
[[package]]
name = "digest"
version = "0.10.7"
@ -515,6 +628,32 @@ dependencies = [
"const-random",
]
[[package]]
name = "dotenvy"
version = "0.15.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b"
[[package]]
name = "dsl_auto_type"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c5d9abe6314103864cc2d8901b7ae224e0ab1a103a0a416661b4097b0779b607"
dependencies = [
"darling",
"either",
"heck",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "either"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
[[package]]
name = "encoding_rs"
version = "0.8.35"
@ -530,6 +669,15 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "fern"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4316185f709b23713e41e3195f90edef7fb00c3ed4adc79769cf09cc762a3b29"
dependencies = [
"log",
]
[[package]]
name = "flate2"
version = "1.0.35"
@ -551,9 +699,16 @@ name = "forgejo-pages"
version = "0.1.0"
dependencies = [
"actix-web",
"chrono",
"config",
"diesel",
"dotenvy",
"fern",
"git2",
"lazy_static",
"log",
"serde",
"toml",
]
[[package]]
@ -622,6 +777,21 @@ version = "0.31.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
[[package]]
name = "git2"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fda788993cc341f69012feba8bf45c0ba4f3291fcc08e214b4d5a7332d88aff"
dependencies = [
"bitflags",
"libc",
"libgit2-sys",
"log",
"openssl-probe",
"openssl-sys",
"url",
]
[[package]]
name = "h2"
version = "0.3.26"
@ -665,6 +835,12 @@ dependencies = [
"hashbrown 0.14.5",
]
[[package]]
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "http"
version = "0.2.12"
@ -688,6 +864,29 @@ version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
[[package]]
name = "iana-time-zone"
version = "0.1.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"iana-time-zone-haiku",
"js-sys",
"wasm-bindgen",
"windows-core",
]
[[package]]
name = "iana-time-zone-haiku"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
dependencies = [
"cc",
]
[[package]]
name = "icu_collections"
version = "1.5.0"
@ -806,6 +1005,12 @@ dependencies = [
"syn",
]
[[package]]
name = "ident_case"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]]
name = "idna"
version = "1.0.3"
@ -858,6 +1063,16 @@ dependencies = [
"libc",
]
[[package]]
name = "js-sys"
version = "0.3.77"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f"
dependencies = [
"once_cell",
"wasm-bindgen",
]
[[package]]
name = "json5"
version = "0.4.1"
@ -875,12 +1090,68 @@ version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388"
[[package]]
name = "lazy_static"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "libc"
version = "0.2.169"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
[[package]]
name = "libgit2-sys"
version = "0.18.0+1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1a117465e7e1597e8febea8bb0c410f1c7fb93b1e1cddf34363f8390367ffec"
dependencies = [
"cc",
"libc",
"libssh2-sys",
"libz-sys",
"openssl-sys",
"pkg-config",
]
[[package]]
name = "libsqlite3-sys"
version = "0.30.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149"
dependencies = [
"pkg-config",
"vcpkg",
]
[[package]]
name = "libssh2-sys"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dc8a030b787e2119a731f1951d6a773e2280c660f8ec4b0f5e1505a386e71ee"
dependencies = [
"cc",
"libc",
"libz-sys",
"openssl-sys",
"pkg-config",
"vcpkg",
]
[[package]]
name = "libz-sys"
version = "1.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df9b68e50e6e0b26f672573834882eb57759f6db9b3be2ea3c35c91188bb4eaa"
dependencies = [
"cc",
"libc",
"pkg-config",
"vcpkg",
]
[[package]]
name = "litemap"
version = "0.7.4"
@ -959,6 +1230,15 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
[[package]]
name = "num-traits"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
]
[[package]]
name = "object"
version = "0.36.7"
@ -974,6 +1254,24 @@ version = "1.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
[[package]]
name = "openssl-probe"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e"
[[package]]
name = "openssl-sys"
version = "0.9.104"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741"
dependencies = [
"cc",
"libc",
"pkg-config",
"vcpkg",
]
[[package]]
name = "ordered-multimap"
version = "0.7.3"
@ -1103,6 +1401,15 @@ dependencies = [
"zerocopy",
]
[[package]]
name = "pq-sys"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6cc05d7ea95200187117196eee9edd0644424911821aeb28a18ce60ea0b8793"
dependencies = [
"vcpkg",
]
[[package]]
name = "proc-macro2"
version = "1.0.93"
@ -1233,6 +1540,12 @@ dependencies = [
"semver",
]
[[package]]
name = "rustversion"
version = "1.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4"
[[package]]
name = "ryu"
version = "1.0.18"
@ -1372,6 +1685,12 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]]
name = "strsim"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "syn"
version = "2.0.96"
@ -1601,6 +1920,12 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
[[package]]
name = "vcpkg"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "version_check"
version = "0.9.5"
@ -1613,6 +1938,73 @@ version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
dependencies = [
"cfg-if",
"once_cell",
"rustversion",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6"
dependencies = [
"bumpalo",
"log",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
dependencies = [
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d"
dependencies = [
"unicode-ident",
]
[[package]]
name = "windows-core"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-sys"
version = "0.52.0"

View file

@ -10,3 +10,10 @@ actix-web = "4"
config = "0.15"
log = "0.4"
serde = "1"
lazy_static = "1.5.0"
git2 = "*"
chrono = "*"
diesel = { version = "2.2.0", features = ["postgres", "sqlite"] }
dotenvy = "0.15"
toml = "*"
fern = "0.7"

9
diesel.toml Normal file
View file

@ -0,0 +1,9 @@
# For documentation on how to configure this file,
# see https://diesel.rs/guides/configuring-diesel-cli
[print_schema]
file = "src/schema.rs"
custom_type_derives = ["diesel::query_builder::QueryId", "Clone"]
[migrations_directory]
dir = "/home/aces/forgejo-pages/migrations"

239
src/backends/git.rs Normal file
View file

@ -0,0 +1,239 @@
use std::borrow::BorrowMut;
use std::cell::Ref;
use std::cell::RefCell;
use std::ops::DerefMut;
use std::sync::Arc;
use diesel::Connection;
use diesel::QueryResult;
use diesel::prelude::*;
use crate::conf;
use crate::conf::CONFIG;
use crate::data::PageError;
use crate::data::{self, Page, PageManager};
use crate::schema::pages;
#[derive(diesel::MultiConnection)]
enum AnyConnection {
Postgresql(PgConnection),
Sqlite(SqliteConnection),
}
pub struct GitPage {
conn: Arc<AnyConnection>,
id: i32,
}
impl GitPage {
fn get_connection(&self) -> &mut AnyConnection {
}
}
impl Page for GitPage {
type Id = i32;
fn id(&self) -> Self::Id {
self.id.clone()
}
fn author(&self) -> String {
use crate::schema::pages::dsl::*;
let connection = self.get_connection();
let page: Vec<String> = pages
.filter(id.eq(self.id))
.limit(1)
.select(author)
.load(connection)
.expect("Failed to load page");
// Return the author, assuming at least one result
page.get(0).cloned().unwrap_or_else(|| "Unknown author".to_string())
}
fn routing(&self) -> crate::data::PageRouting {
todo!()
}
fn version(&self) -> String {
use crate::schema::git_page_source::dsl::*;
let connection = self.get_connection();
let git_page_source: Vec<String> = crate::schema::git_page_source::table
.filter(page_id.eq(self.id))
.limit(1)
.select(repo_url)
.load(connection)
.expect("Failed to load git page source");
git_page_source.get(0).cloned().unwrap_or_else(|| "Unknown version".to_string())
}
fn update(&mut self) -> Result<(), crate::data::PageError> {
todo!()
}
}
pub struct GitPageManager<'a> {
pub connection: RefCell<AnyConnection>,
pages: Vec<GitPage<'a>>,
}
impl GitPageManager<'_> {
pub fn new() -> Self {
let connection = AnyConnection::establish("sqlite://git.db").unwrap();
GitPageManager {
connection: RefCell::new(connection),
pages: Vec::new(),
}
}
}
// Filesystem-based storage
// - D [PAGES_TEMP]
// - D [git_url]
// - D [PAGES_ROOT]
// - D [UUID]
// - F? CNAME
// - F? All other files
// - F? .git
impl<'a> GitPageManager<'a> {
fn create(&mut self, url: String, owner: String) -> Result<&GitPage<'a>, PageError> {
// - Prepare data
// - Insert page into database (STATUS: FETCHING)
// - Clone repo to local storage (temp)
// - Gather CNAME and other data from repo
// - Validate data (Perform checks)
// - Update page in database (STATUS: APPLYING)
// - Move repo to final storage location
// - Validate
// - Update page in database (STATUS: READY)
// Insert the page into the database
let connection = &mut *self.connection.borrow_mut();
let page_id: i32 = 0;
{
use crate::schema::pages::dsl::*;
let new_page = crate::models::NewPage {
name: url.clone(),
author: owner.clone(),
status: 1,
};
let res_insert = diesel::insert_into(pages)
.values(&new_page)
.execute(connection);
if res_insert.is_err() {
return Err(PageError::InternalServerError(res_insert.err().unwrap().to_string()));
}
// Get the ID of the page, given name and author
let res_page_id: QueryResult<i32> = pages
.filter(name.eq(url.clone()))
.filter(author.eq(owner.clone()))
.select(id)
.first(connection);
}
{
// Git tracking attachment
use crate::schema::git_page_source::dsl::*;
let res_insert_git = diesel::insert_into(git_page_source)
.values(&crate::models::NewGitPageSource {
page_id: 0,
repo_url: url.clone(),
branch: "master".to_string(),
})
.execute(connection);
}
// Clone to filesystem (temp)
let temp_path = format!("{}/{}", CONFIG.temp_dir, url);
let _ = std::fs::create_dir_all(&temp_path);
let res_git_repo = git2::Repository::clone(&url, &temp_path);
if res_git_repo.is_err() {
return Err(PageError::FetchError(res_git_repo.err().unwrap().to_string()));
}
// Open CNAME file
let cname_path = format!("{}/CNAME", temp_path);
let cname = std::fs::read_to_string(&cname_path);
// CNAME follows GitHub's rules
// Each line is a domain
let domains: Vec<String> = cname.unwrap_or("".to_string())
.split("\n")
.map(|s| s.to_string())
.collect();
// TODO: Validation step here - Put this off for now
// Move to final storage location
let final_path = format!("{}/{}", CONFIG.pages_root, page_id);
let _ = std::fs::create_dir_all(&final_path);
let _ = std::fs::rename(&temp_path, &final_path);
// Update the page in the database (we are now ready)
{
use crate::schema::pages::dsl::*;
let res_update = diesel::update(pages)
.filter(id.eq(page_id))
.set(status.eq(3))
.execute(connection);
if res_update.is_err() {
return Err(PageError::InternalServerError(res_update.err().unwrap().to_string()));
}
}
// Create the page object
let page = GitPage {
conn:
id: page_id,
};
// Add the page to the list of pages
self.pages.push(page);
// Return the page
let res_page = self.page(page_id);
match res_page {
Ok(page) => Ok(page),
Err(err) => Err(PageError::InternalServerError("Failed to create page".to_string())),
}
}
}
impl<'a> PageManager<'a, GitPage<'a>> for GitPageManager<'a> {
fn query(&self, author: String, path: String, host: Option<String>) -> Result<crate::data::PageQueryResult, crate::data::PageError> {
todo!()
}
fn page(&self, id: <GitPage<'_> as data::Page>::Id) -> Result<&GitPage<'a>, crate::data::PageError> {
let page = self.pages.iter().find(|page| page.id() == id);
match page {
Some(page) => Ok(page),
None => Err(crate::data::PageError::NotFound(format!("Page not found: {}", id))),
}
}
fn page_mut(&mut self, id: <GitPage<'_> as data::Page>::Id) -> Result<&mut GitPage<'a>, crate::data::PageError> {
let page = self.pages.iter_mut().find(|page| page.id() == id);
match page {
Some(page) => Ok(page),
None => Err(crate::data::PageError::NotFound(format!("Page not found: {}", id))),
}
}
fn pages_iter(&'a self) -> impl Iterator<Item = &'a GitPage<'a>> {
self.pages.iter()
}
}

1
src/backends/mod.rs Normal file
View file

@ -0,0 +1 @@
pub mod git;

View file

@ -1,11 +1,44 @@
use lazy_static::lazy_static;
use serde::Deserialize;
#[derive(Deserialize)]
/* Variables */
const CONFIG_PREFIX: &str = "FJ_PAGES_";
const CONFIG_DEFAULT: &str = r#"
[domain]
# The domain of the website
domain = "*"
# The temporary directory for storing files
temp_dir = "/tmp"
"#;
lazy_static! {
#[derive(Debug)]
pub static ref CONFIG: Config = {
// Identify path of the configuration file
let path = std::env::var(format!("{}CONFIG_PATH", CONFIG_PREFIX)).unwrap_or("config.toml".to_string());
// Read the configuration file
let result_config = std::fs::read_to_string(path)
.unwrap_or(CONFIG_DEFAULT.to_string());
// Parse the configuration file
toml::from_str(&result_config)
.expect("Failed to parse configuration file")
};
}
/* Structures */
#[derive(Deserialize, Debug, Clone)]
pub struct Domain {
// Regex is allowed
domains: Vec<String>
}
#[derive(Deserialize)]
#[derive(Deserialize, Debug, Clone)]
pub struct Config {
pub domain: Domain
pub domain: Domain,
pub temp_dir: String,
pub pages_root: String,
}

98
src/data.rs Normal file
View file

@ -0,0 +1,98 @@
/// Data storage and retrieval
/* -------------------------------------------------------------------------- */
/* Data */
/* -------------------------------------------------------------------------- */
use serde::{Deserialize, Serialize};
pub enum PageError {
NotFound(String),
Unauthorized(String),
InternalServerError(String),
Corrupted(String),
FetchError(String),
}
pub trait Page: Sized {
type Id;
/// Gets the identifier of the page
/// This can be used to differentiate between pages
///
/// # Returns
///
fn id(&self) -> Self::Id;
fn author(&self) -> String;
fn routing(&self) -> PageRouting;
/// Get the revision of the page
///
/// # Returns
///
/// * `String` - The revision of the page - This is implementation defined
fn version(&self) -> String;
fn update(&mut self) -> Result<(), PageError>;
}
#[derive(Serialize, Deserialize)]
pub struct PageRouting {
pub hosts: Option<Vec<String>>,
}
pub enum PageStatus {
// 200
Ok = 200,
Created = 201,
Accepted = 202,
NoContent = 204,
ResetContent = 205,
// 300
// 400
BadRequest = 400,
Unauthorized = 401,
// No one cares about 402...
Forbidden = 403,
NotFound = 404
}
pub struct PageQueryResult {
pub content: Option<String>,
pub status: PageStatus
}
pub trait PageManager<'a, P: Page + 'a> {
/// Resolve a page
///
/// # Arguments
///
/// * `author` - The author of the repository - This is required
/// * `host` - The host of the repository - This allows CNAMEs to be used to point to the repository
/// * `path` - The query path - This is treated either as a file path (in the case of a user page),
/// or as a repository path (in the case of a project page)
///
/// # Returns
///
/// * `PageQueryResult` - The result of the query - This can be page contents, etc
/// * `PageError` - Any error that occurred processing the query
///
/// Note that page errors, such as Error 404, are not an error that occurs processing the query
/// Thus, they are returned via the `PageQueryResult` instead of the `PageError`
fn query(&self, author: String, path: String, host: Option<String>) -> Result<PageQueryResult, PageError>;
fn page(&self, id: P::Id) -> Result<&P, PageError>;
fn page_mut(&mut self, id: P::Id) -> Result<&mut P, PageError>;
fn pages_iter(&'a self) -> impl Iterator<Item = &'a P>;
/// Delete the page
/// This will remove the page from being hosted or stored
///
/// # Returns
///
/// * `Result<(), (PageError, Self)>` - If the page was deleted successfully, this will return `Ok(())`
/// Otherwise, it will return `Err((PageError, Self))` with the error and the page
///
/// Consequently, since the page is not returned if it is deleted successfully, the page is consumed
/// And since it returns it if it fails, it is recoverable for failure handling
fn delete(&mut self, id: P::Id) -> Result<(), PageError>;
}

View file

@ -1,6 +1,36 @@
use conf::CONFIG;
pub mod backends;
pub mod conf;
pub mod sitedata;
pub mod data;
pub mod schema;
pub mod models;
fn prepare_logger() {
// Fern
fern::Dispatch::new()
.format(|out, message, record| {
out.finish(format_args!(
"{}[{}][{}] {}",
chrono::Local::now().format("[%Y-%m-%d][%H:%M:%S]"),
record.target(),
record.level(),
message
))
})
.level(log::LevelFilter::Debug)
.chain(std::io::stdout())
.apply()
.expect("Failed to initialize logger");
}
fn main() {
// Prepare logger
prepare_logger();
// Load configuration
let config = CONFIG.clone();
// Print configuration
println!("Configuration: {:#?}", config);
}

50
src/models.rs Normal file
View file

@ -0,0 +1,50 @@
use diesel::prelude::{Insertable, Queryable};
use crate::schema::*;
#[derive(Insertable)]
#[table_name = "pages"]
pub struct NewPage {
pub author: String,
pub name: String,
pub status: i32,
}
#[derive(Queryable)]
pub struct Page {
pub id: i32,
pub author: String,
pub name: String,
}
#[derive(Insertable)]
#[table_name = "routing_record"]
pub struct NewRoutingRecord {
pub page_id: i32,
pub record_type: String,
pub record_value: String,
}
#[derive(Queryable)]
pub struct RoutingRecord {
pub id: i32,
pub page_id: i32,
pub record_type: String,
pub record_value: String,
}
#[derive(Insertable)]
#[table_name = "git_page_source"]
pub struct NewGitPageSource {
pub page_id: i32,
pub repo_url: String,
pub branch: String,
}
#[derive(Queryable)]
pub struct GitPageSource {
pub id: i32,
pub page_id: i32,
pub repo_url: String,
pub branch: String,
}

46
src/schema.rs Normal file
View file

@ -0,0 +1,46 @@
// Diesel schema
diesel::table! {
pages (id) {
id -> Int4,
author -> Text,
name -> Text,
created_at -> Timestamp,
updated_at -> Timestamp,
// - 0: Invalid
// - 1: Fetching
// - 2: Validated
// - 3: Ready
status -> Integer
}
}
diesel::table! {
routing_record (id) {
id -> Int4,
page_id -> Int4,
record_value -> Text,
created_at -> Timestamp,
updated_at -> Timestamp,
}
}
diesel::table! {
git_page_source (id) {
id -> Int4,
page_id -> Int4,
repo_url -> Text,
branch -> Text,
created_at -> Timestamp,
updated_at -> Timestamp,
}
}
diesel::joinable!(routing_record -> pages (page_id));
diesel::joinable!(git_page_source -> pages (page_id));
diesel::allow_tables_to_appear_in_same_query!(
pages,
routing_record,
git_page_source,
);

View file

@ -1,17 +0,0 @@
pub enum SiteError {
PageNotFound(String),
DataError(String),
NetworkError(String)
}
pub trait Site {
fn get_page(&self, url: &str) -> Result<Vec<u8>, ()>;
}
pub trait SiteControl<S: Site> {
fn is_ok() -> bool;
fn site(&self) -> S;
fn can_update(&self) -> bool;
fn repair(&mut self) -> Result<(), ()>;
fn update(&mut self) -> Result<S, ()>;
}

View file

@ -1,17 +0,0 @@
pub struct GitSiteProvider {
pub current_commit: String
}
pub impl GitSiteProvider {
fn from_remote(url: &str) -> Self {
Self {
}
}
}
pub impl Site for GitSiteProvider {
}

View file

@ -1,2 +0,0 @@
pub mod control;
pub mod git;