Compare commits

...

5 commits

Author SHA1 Message Date
Aravinth Manivannan 3a2e6355da
feat: serve openapi docs
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2022-12-11 23:43:13 +05:30
Aravinth Manivannan a38411abaa
feat: openapi spec 2022-12-11 23:42:46 +05:30
Aravinth Manivannan b8246f0fd8
feat: restructure events schema for cleaner serialization
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2022-12-11 20:25:42 +05:30
Aravinth Manivannan 1fa28ef9b7
fix: use only one API key per conductor deployment
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2022-12-11 19:30:36 +05:30
Aravinth Manivannan a4f4903120
chore: lints and update config 11.x -> 13.x
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2022-12-11 19:27:59 +05:30
30 changed files with 737 additions and 154 deletions

293
Cargo.lock generated
View file

@ -75,7 +75,7 @@ dependencies = [
"bytestring",
"http",
"regex",
"serde 1.0.149",
"serde",
"tracing",
]
@ -161,7 +161,7 @@ dependencies = [
"once_cell",
"pin-project-lite",
"regex",
"serde 1.0.149",
"serde",
"serde_json",
"serde_urlencoded",
"smallvec",
@ -249,12 +249,6 @@ dependencies = [
"alloc-no-stdlib",
]
[[package]]
name = "arrayvec"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
[[package]]
name = "async-trait"
version = "0.1.59"
@ -272,7 +266,7 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7c57d12312ff59c811c0643f4d80830505833c9ffaebd193d819392b265be8e"
dependencies = [
"num-traits 0.2.15",
"num-traits",
]
[[package]]
@ -432,8 +426,10 @@ dependencies = [
"libconductor",
"libconfig",
"log",
"mime_guess",
"pretty_env_logger",
"serde 1.0.149",
"rust-embed",
"serde",
"serde_json",
"sqlx",
"url",
@ -441,15 +437,18 @@ dependencies = [
[[package]]
name = "config"
version = "0.11.0"
version = "0.13.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b1b9d958c2b1368a663f05538fc1b5975adce1e19f435acceae987aceeeb369"
checksum = "d379af7f68bfc21714c6c7dea883544201741d2ce8274bb12fa54f89507f52a7"
dependencies = [
"async-trait",
"json5",
"lazy_static",
"nom 5.1.2",
"nom",
"pathdiff",
"ron",
"rust-ini",
"serde 1.0.149",
"serde-hjson",
"serde",
"serde_json",
"toml",
"yaml-rust",
@ -644,6 +643,12 @@ dependencies = [
"winapi",
]
[[package]]
name = "dlv-list"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0688c2a7f92e427f44895cd63841bff7b29f8d7a1648b9e7e07a4a365b2e1257"
[[package]]
name = "dotenvy"
version = "0.15.6"
@ -656,7 +661,7 @@ version = "0.1.0"
dependencies = [
"async-trait",
"libconductor",
"serde 1.0.149",
"serde",
"serde_json",
]
@ -666,7 +671,7 @@ version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
dependencies = [
"serde 1.0.149",
"serde",
]
[[package]]
@ -1027,6 +1032,17 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "json5"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96b0db21af676c1ce64250b5f40f3ce2cf27e4e47cb91ed91eb6fe9350b430c1"
dependencies = [
"pest",
"pest_derive",
"serde",
]
[[package]]
name = "language-tags"
version = "0.3.2"
@ -1039,19 +1055,6 @@ version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "lexical-core"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe"
dependencies = [
"arrayvec",
"bitflags",
"cfg-if",
"ryu",
"static_assertions",
]
[[package]]
name = "libc"
version = "0.2.138"
@ -1064,16 +1067,16 @@ version = "0.1.0"
dependencies = [
"async-trait",
"libconfig",
"serde 1.0.149",
"serde",
"serde_json",
]
[[package]]
name = "libconfig"
version = "0.1.0"
source = "git+https://git.batsense.net/librepages/libconfig#76416ccdb622f48362502a24dbc4ba90ed054225"
source = "git+https://git.batsense.net/librepages/libconfig#f54290c4bae26b51a4945e0bf812e2b99856963b"
dependencies = [
"serde 1.0.149",
"serde",
]
[[package]]
@ -1146,6 +1149,16 @@ version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
[[package]]
name = "mime_guess"
version = "2.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef"
dependencies = [
"mime",
"unicase",
]
[[package]]
name = "minimal-lexical"
version = "0.2.1"
@ -1173,17 +1186,6 @@ dependencies = [
"windows-sys",
]
[[package]]
name = "nom"
version = "5.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af"
dependencies = [
"lexical-core",
"memchr",
"version_check",
]
[[package]]
name = "nom"
version = "7.1.1"
@ -1194,15 +1196,6 @@ dependencies = [
"minimal-lexical",
]
[[package]]
name = "num-traits"
version = "0.1.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31"
dependencies = [
"num-traits 0.2.15",
]
[[package]]
name = "num-traits"
version = "0.2.15"
@ -1228,6 +1221,16 @@ version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
[[package]]
name = "ordered-multimap"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccd746e37177e1711c20dd619a1620f34f5c8b569c53590a72dedd5344d8924a"
dependencies = [
"dlv-list",
"hashbrown",
]
[[package]]
name = "os_str_bytes"
version = "6.4.1"
@ -1288,12 +1291,62 @@ version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1"
[[package]]
name = "pathdiff"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd"
[[package]]
name = "percent-encoding"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
[[package]]
name = "pest"
version = "2.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc8bed3549e0f9b0a2a78bf7c0018237a2cdf085eecbbc048e52612438e4e9d0"
dependencies = [
"thiserror",
"ucd-trie",
]
[[package]]
name = "pest_derive"
version = "2.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdc078600d06ff90d4ed238f0119d84ab5d43dbaad278b0e33a8820293b32344"
dependencies = [
"pest",
"pest_generator",
]
[[package]]
name = "pest_generator"
version = "2.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28a1af60b1c4148bb269006a750cff8e2ea36aff34d2d96cf7be0b14d1bed23c"
dependencies = [
"pest",
"pest_meta",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "pest_meta"
version = "2.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fec8605d59fc2ae0c6c1aefc0c7c7a9769732017c0ce07f7a9cfffa7b4404f20"
dependencies = [
"once_cell",
"pest",
"sha1",
]
[[package]]
name = "pin-project-lite"
version = "0.2.9"
@ -1453,10 +1506,59 @@ dependencies = [
]
[[package]]
name = "rust-ini"
version = "0.13.0"
name = "ron"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e52c148ef37f8c375d49d5a73aa70713125b7f19095948a923f80afdeb22ec2"
checksum = "88073939a61e5b7680558e6be56b419e208420c2adb92be54921fa6b72283f1a"
dependencies = [
"base64",
"bitflags",
"serde",
]
[[package]]
name = "rust-embed"
version = "6.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "283ffe2f866869428c92e0d61c2f35dfb4355293cdfdc48f49e895c15f1333d1"
dependencies = [
"rust-embed-impl",
"rust-embed-utils",
"walkdir",
]
[[package]]
name = "rust-embed-impl"
version = "6.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31ab23d42d71fb9be1b643fe6765d292c5e14d46912d13f3ae2815ca048ea04d"
dependencies = [
"proc-macro2",
"quote",
"rust-embed-utils",
"syn",
"walkdir",
]
[[package]]
name = "rust-embed-utils"
version = "7.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1669d81dfabd1b5f8e2856b8bbe146c6192b0ba22162edc738ac0a5de18f054"
dependencies = [
"sha2",
"walkdir",
]
[[package]]
name = "rust-ini"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6d5f2436026b4f6e79dc829837d467cc7e9a55ee40e750d716713540715a2df"
dependencies = [
"cfg-if",
"ordered-multimap",
]
[[package]]
name = "rustc_version"
@ -1508,6 +1610,15 @@ version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
[[package]]
name = "same-file"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
dependencies = [
"winapi-util",
]
[[package]]
name = "scopeguard"
version = "1.1.0"
@ -1530,12 +1641,6 @@ version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4"
[[package]]
name = "serde"
version = "0.8.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9dad3f759919b92c3068c696c15c3d17238234498bbdcc80f2c469606f948ac8"
[[package]]
name = "serde"
version = "1.0.149"
@ -1545,18 +1650,6 @@ dependencies = [
"serde_derive",
]
[[package]]
name = "serde-hjson"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a3a4e0ea8a88553209f6cc6cfe8724ecad22e1acf372793c27d995290fe74f8"
dependencies = [
"lazy_static",
"num-traits 0.1.43",
"regex",
"serde 0.8.23",
]
[[package]]
name = "serde_derive"
version = "1.0.149"
@ -1576,7 +1669,7 @@ checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db"
dependencies = [
"itoa",
"ryu",
"serde 1.0.149",
"serde",
]
[[package]]
@ -1588,7 +1681,7 @@ dependencies = [
"form_urlencoded",
"itoa",
"ryu",
"serde 1.0.149",
"serde",
]
[[package]]
@ -1660,7 +1753,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f87e292b4291f154971a43c3774364e2cbcaec599d3f5bf6fa9d122885dbc38a"
dependencies = [
"itertools",
"nom 7.1.1",
"nom",
"unicode_categories",
]
@ -1712,7 +1805,7 @@ dependencies = [
"rand",
"rustls",
"rustls-pemfile",
"serde 1.0.149",
"serde",
"serde_json",
"sha1",
"sha2",
@ -1741,7 +1834,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
"serde 1.0.149",
"serde",
"serde_json",
"sha2",
"sqlx-core",
@ -1761,12 +1854,6 @@ dependencies = [
"tokio-rustls",
]
[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "stringprep"
version = "0.1.2"
@ -1836,7 +1923,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376"
dependencies = [
"itoa",
"serde 1.0.149",
"serde",
"time-core",
"time-macros",
]
@ -1932,7 +2019,7 @@ version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7"
dependencies = [
"serde 1.0.149",
"serde",
]
[[package]]
@ -1962,6 +2049,21 @@ version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
[[package]]
name = "ucd-trie"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81"
[[package]]
name = "unicase"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
dependencies = [
"version_check",
]
[[package]]
name = "unicode-bidi"
version = "0.3.8"
@ -2010,7 +2112,7 @@ dependencies = [
"form_urlencoded",
"idna",
"percent-encoding",
"serde 1.0.149",
"serde",
]
[[package]]
@ -2019,6 +2121,17 @@ version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "walkdir"
version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56"
dependencies = [
"same-file",
"winapi",
"winapi-util",
]
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
@ -2101,9 +2214,9 @@ dependencies = [
[[package]]
name = "webpki-roots"
version = "0.22.5"
version = "0.22.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "368bfe657969fb01238bb756d351dcade285e0f6fcbd36dcb23359a5169975be"
checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87"
dependencies = [
"webpki",
]

View file

@ -20,12 +20,14 @@ serde = { version = "1", features=["derive"]}
actix-web-codegen-const-routes = { version = "0.1.0", tag = "0.1.0", git = "https://github.com/realaravinth/actix-web-codegen-const-routes" }
libconfig = { version = "0.1.0", git = "https://git.batsense.net/librepages/libconfig" }
derive_builder = "0.11.2"
config = "0.11"
config = "0.13"
derive_more = "0.99.17"
url = { version = "2.2.2", features = ["serde"]}
serde_json = { version ="1", features = ["raw_value"]}
clap = { vesrion = "3.2.20", features = ["derive"]}
actix-web-httpauth = "0.8.0"
mime_guess = "2.0.4"
rust-embed = "6.4.2"
[dependencies.libconductor]
path = "./env/libconductor"

View file

@ -36,7 +36,7 @@ release: ## Build app with release optimizations
cargo build --release
run: ## Run app in debug mode
cargo run
cargo run -- serve
#sqlx-offline-data: ## prepare sqlx offline data

View file

@ -1,10 +1,10 @@
debug = true
source_code = "https://git.batsense.net/librepages/conductor"
conductor = "dummy"
api_keys = [
# CHANGE THIS!!
{ username = "librepages_api", password="longrandomlygeneratedpassword"}
]
[creds]
username = "librepages_api"
password="longrandomlygeneratedpassword"
[server]
# Please set a unique value, your mCaptcha instance's security depends on this being

Binary file not shown.

After

Width:  |  Height:  |  Size: 665 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 628 B

16
docs/openapi/index.css Normal file
View file

@ -0,0 +1,16 @@
html {
box-sizing: border-box;
overflow: -moz-scrollbars-vertical;
overflow-y: scroll;
}
*,
*:before,
*:after {
box-sizing: inherit;
}
body {
margin: 0;
background: #fafafa;
}

19
docs/openapi/index.html Normal file
View file

@ -0,0 +1,19 @@
<!-- HTML for static distribution bundle build -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>LibrePages Conductor | Swagger UI</title>
<link rel="stylesheet" type="text/css" href="/docs/openapi/swagger-ui.css" />
<link rel="stylesheet" type="text/css" href="/docs/openapi/index.css" />
<link rel="icon" type="image/png" href="/docs/openapi/favicon-32x32.png" sizes="32x32" />
<link rel="icon" type="image/png" href="/docs/openapi/favicon-16x16.png" sizes="16x16" />
</head>
<body>
<div id="swagger-ui"></div>
<script src="/docs/openapi/swagger-ui-bundle.js" charset="UTF-8"> </script>
<script src="/docs/openapi/swagger-ui-standalone-preset.js" charset="UTF-8"> </script>
<script src="/docs/openapi/swagger-initializer.js" charset="UTF-8"> </script>
</body>
</html>

View file

@ -0,0 +1,79 @@
<!doctype html>
<html lang="en-US">
<head>
<title>Swagger UI: OAuth2 Redirect</title>
</head>
<body>
<script>
'use strict';
function run () {
var oauth2 = window.opener.swaggerUIRedirectOauth2;
var sentState = oauth2.state;
var redirectUrl = oauth2.redirectUrl;
var isValid, qp, arr;
if (/code|token|error/.test(window.location.hash)) {
qp = window.location.hash.substring(1).replace('?', '&');
} else {
qp = location.search.substring(1);
}
arr = qp.split("&");
arr.forEach(function (v,i,_arr) { _arr[i] = '"' + v.replace('=', '":"') + '"';});
qp = qp ? JSON.parse('{' + arr.join() + '}',
function (key, value) {
return key === "" ? value : decodeURIComponent(value);
}
) : {};
isValid = qp.state === sentState;
if ((
oauth2.auth.schema.get("flow") === "accessCode" ||
oauth2.auth.schema.get("flow") === "authorizationCode" ||
oauth2.auth.schema.get("flow") === "authorization_code"
) && !oauth2.auth.code) {
if (!isValid) {
oauth2.errCb({
authId: oauth2.auth.name,
source: "auth",
level: "warning",
message: "Authorization may be unsafe, passed state was changed in server. The passed state wasn't returned from auth server."
});
}
if (qp.code) {
delete oauth2.state;
oauth2.auth.code = qp.code;
oauth2.callback({auth: oauth2.auth, redirectUrl: redirectUrl});
} else {
let oauthErrorMsg;
if (qp.error) {
oauthErrorMsg = "["+qp.error+"]: " +
(qp.error_description ? qp.error_description+ ". " : "no accessCode received from the server. ") +
(qp.error_uri ? "More info: "+qp.error_uri : "");
}
oauth2.errCb({
authId: oauth2.auth.name,
source: "auth",
level: "error",
message: oauthErrorMsg || "[Authorization failed]: no accessCode received from the server."
});
}
} else {
oauth2.callback({auth: oauth2.auth, token: qp, isValid: isValid, redirectUrl: redirectUrl});
}
window.close();
}
if (document.readyState !== 'loading') {
run();
} else {
document.addEventListener('DOMContentLoaded', function () {
run();
});
}
</script>
</body>
</html>

178
docs/openapi/openapi.yml Normal file
View file

@ -0,0 +1,178 @@
openapi: 3.0.3
info:
title: LibrePages Conductor - OpenAPI 3.0
description: |-
Conductor is the deployment manager used internally in LibrePages. It is
responsible for creating, updating and deleting websites that are deployed
with LibrePages
Some useful links:
- [LibrePages Conductor repository](https://git.batsense.net/LibrePages/conductor)
termsOfService: http://libreapages.org/terms/
contact:
email: contact@libreapages.org
license:
name: AGPLv3 or later version
url: https://www.gnu.org/licenses/agpl.html
version: 0.1.0
externalDocs:
description: LibrePages Conductor - internal service to update deployments
url: http://git.batsense.net/LibrePages/conductor
tags:
- name: meta
description: Information about the system
- name: site
description: Information about customer site deployments
paths:
/api/v1/events/new:
post:
tags:
- site
summary: Post new event to Conductor
description: Conductor schedules jobs based on events posted to it.
operationId: eventsNew
responses:
"201":
description: Successful operation
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/eventsNewPayloadNewSite"
- $ref: "#/components/schemas/eventsNewPayloadConfig"
- $ref: "#/components/schemas/eventsNewPayloadDeleteSite"
/api/v1/meta/build:
get:
tags:
- meta
summary: Get binary's build information
description: Update an existing pet by Idinformation
operationId: metaBuild
responses:
"200":
description: Successful operation
content:
application/json:
schema:
$ref: "#/components/schemas/BuildInformation"
/api/v1/meta/health:
get:
tags:
- meta
summary: Get instance's health information
description: Get instance's health information
operationId: metaHealth
responses:
"200":
description: Successful operation
content:
application/json:
schema:
$ref: "#/components/schemas/HealthInformation"
components:
schemas:
BuildInformation:
required:
- version
- git_commit_hash
- source_code
type: object
properties:
version:
type: string
example: v0.1.0
git_commit_hash:
type: string
example: 1fa28ef9b70bb04d6c76eee9e9bc5be77005b4b0
source_code:
type: string
example: https://git.batsense.net/LibrePages
HealthInformation:
required:
- conductor
type: object
properties:
conductor:
type: boolean
example: true
eventsNewPayloadConfig:
type: object
required:
- data
properties:
data:
$ref: '#/components/schemas/LibConfigConfig'
LibConfigConfig:
properties:
source:
$ref: '#/components/schemas/LibConfigSource'
forms:
$ref: '#/components/schemas/LibConfigForms'
domains:
type: array
items:
type: string
example: ["example.com", "testing.example.org"]
image_compression:
$ref: '#/components/schemas/LibConfigImageCompression'
redirects:
$ref: '#/components/schemas/LibConfigRedirects'
LibConfigSource:
properties:
production_branch:
type: string
example: "librepages"
staging_branch:
type: string
example: "librepages-staging"
LibConfigForms:
properties:
enabled:
type: boolean
example: false
LibConfigImageCompression:
properties:
enabled:
type: boolean
example: false
LibConfigRedirects:
properties:
from:
type: string
example: "/from"
to:
type: string
example: "/to"
eventsNewPayloadNewSite:
properties:
hostname:
type: string
example: "example.org"
path:
type: string
example: "/tmp/example.org"
branch:
type: string
example: "librepages"
eventsNewPayloadDeleteSite:
properties:
hostname:
type: string
example: "example.org"
securitySchemes:
basicAuth:
type: http
scheme: basic

View file

@ -0,0 +1,20 @@
window.onload = function() {
//<editor-fold desc="Changeable Configuration Block">
// the following lines will be replaced by docker/configurator, when it runs in a docker-container
window.ui = SwaggerUIBundle({
url: "/docs/openapi/openapi.yml",
dom_id: '#swagger-ui',
deepLinking: true,
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
],
plugins: [
SwaggerUIBundle.plugins.DownloadUrl
],
layout: "StandaloneLayout"
});
//</editor-fold>
};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -14,20 +14,22 @@
* 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 libconfig::Config as LibCOnfig;
use libconfig::Config as LibConfig;
use serde::{Deserialize, Serialize};
#[derive(Deserialize, Serialize, Debug, PartialEq, Eq, Clone)]
pub struct NewSite {
pub path: String,
pub branch: String,
pub hostname: String,
}
#[derive(Deserialize, Serialize, Debug, PartialEq, Eq, Clone)]
#[serde(untagged)]
pub enum EventType {
NewSite(NewSite),
DeleteSite(String),
Config(LibCOnfig),
NewSite {
path: String,
branch: String,
hostname: String,
},
DeleteSite {
hostname: String,
},
Config {
data: LibConfig,
},
}

View file

@ -56,9 +56,8 @@ async fn post_event(ctx: AppCtx, payload: web::Json<EventType>) -> ServiceResult
#[cfg(test)]
pub mod tests {
use actix_web::{http::StatusCode, test, App};
use super::*;
use actix_web::{http::StatusCode, test, App};
#[actix_rt::test]
async fn submit_works() {
@ -71,13 +70,17 @@ pub mod tests {
)
.await;
let creds = settings.api_keys.get(0).unwrap().clone();
let creds = settings.creds.clone();
let auth = format!(
"Basic {}",
base64::encode(format!("{}:{}", creds.username.clone(), creds.password))
);
let new_hostname = EventType::NewHostname("demo.librepages.org".into());
let msg = EventType::NewSite {
hostname: "demo.librepages.org".into(),
branch: "librepages".into(),
path: "/tmp/librepages".into(),
};
// upload json
let upload_json = test::call_service(
@ -85,7 +88,7 @@ pub mod tests {
test::TestRequest::post()
.append_header((actix_web::http::header::AUTHORIZATION, auth.clone()))
.uri(API_V1_ROUTES.webhook.post_event)
.set_json(&new_hostname)
.set_json(&msg)
.to_request(),
)
.await;

View file

@ -37,13 +37,9 @@ impl Ctx {
pub async fn new(s: &Settings) -> ArcCtx {
let source_code = {
let mut url = s.source_code.clone();
if !url.ends_with('/') {
url.push('/');
}
let mut base = url::Url::parse(&url).unwrap();
base = base.join("tree/").unwrap();
base = base.join(crate::GIT_COMMIT_HASH).unwrap();
base.into()
url = url.join("tree/").unwrap();
url = url.join(crate::GIT_COMMIT_HASH).unwrap();
url.into()
};
let conductor: Box<dyn Conductor> = match s.conductor {

136
src/docs.rs Normal file
View file

@ -0,0 +1,136 @@
/*
* 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::borrow::Cow;
use actix_web::body::BoxBody;
use actix_web::{http::header, web, HttpResponse, Responder};
use mime_guess::from_path;
use rust_embed::RustEmbed;
use crate::CACHE_AGE;
pub const DOCS: routes::Docs = routes::Docs::new();
pub mod routes {
pub struct Docs {
pub home: &'static str,
pub spec: &'static str,
pub assets: &'static str,
}
impl Docs {
pub const fn new() -> Self {
Docs {
home: "/docs/openapi",
spec: "/docs/openapi/openapi.yml",
assets: "/docs/openapi/{_:.*}",
}
}
}
}
pub fn services(cfg: &mut web::ServiceConfig) {
cfg.service(index).service(spec).service(dist);
}
#[derive(RustEmbed)]
#[folder = "docs/openapi/"]
struct Asset;
pub fn handle_embedded_file(path: &str) -> HttpResponse {
match Asset::get(path) {
Some(content) => {
let body: BoxBody = match content.data {
Cow::Borrowed(bytes) => BoxBody::new(bytes),
Cow::Owned(bytes) => BoxBody::new(bytes),
};
HttpResponse::Ok()
.insert_header(header::CacheControl(vec![
header::CacheDirective::Public,
header::CacheDirective::Extension("immutable".into(), None),
header::CacheDirective::MaxAge(CACHE_AGE),
]))
.content_type(from_path(path).first_or_octet_stream().as_ref())
.body(body)
}
None => HttpResponse::NotFound().body("404 Not Found"),
}
}
#[actix_web_codegen_const_routes::get(path = "DOCS.assets")]
async fn dist(path: web::Path<String>) -> impl Responder {
handle_embedded_file(&path)
}
const OPEN_API_SPEC: &str = include_str!("../docs/openapi/openapi.yml");
#[actix_web_codegen_const_routes::get(path = "DOCS.spec")]
async fn spec() -> HttpResponse {
HttpResponse::Ok()
.content_type("text/yaml")
.body(OPEN_API_SPEC)
}
#[actix_web_codegen_const_routes::get(path = "DOCS.home")]
async fn index() -> HttpResponse {
handle_embedded_file("index.html")
}
#[cfg(test)]
mod tests {
use actix_web::http::StatusCode;
use actix_web::test;
use super::*;
use crate::*;
#[actix_rt::test]
async fn docs_works() {
const FILE: &str = "openapi.yml";
let app = test::init_service(
App::new()
.wrap(actix_web::middleware::NormalizePath::new(
actix_web::middleware::TrailingSlash::Trim,
))
.configure(services),
)
.await;
let resp = test::call_service(
&app,
test::TestRequest::get().uri(DOCS.home).to_request(),
)
.await;
assert_eq!(resp.status(), StatusCode::OK);
let resp = test::call_service(
&app,
test::TestRequest::get().uri(DOCS.spec).to_request(),
)
.await;
assert_eq!(resp.status(), StatusCode::OK);
let uri = format!("{}/{}", DOCS.home, "favicon-32x32.png");
println!("{uri}");
let resp =
test::call_service(&app, test::TestRequest::get().uri(&uri).to_request())
.await;
assert_eq!(resp.status(), StatusCode::OK);
}
}

View file

@ -26,7 +26,7 @@ use lazy_static::lazy_static;
mod api;
mod ctx;
//mod docs;
mod docs;
#[cfg(not(tarpaulin_include))]
mod errors;
//#[macro_use]

View file

@ -17,5 +17,6 @@
use actix_web::web;
pub fn services(cfg: &mut web::ServiceConfig) {
crate::docs::services(cfg);
crate::api::v1::services(cfg);
}

View file

@ -17,7 +17,7 @@
use std::env;
use std::path::Path;
use config::{Config, ConfigError, Environment, File};
use config::{builder::DefaultState, Config, ConfigBuilder, ConfigError, Environment, File};
use derive_more::Display;
use log::info;
use log::warn;
@ -61,50 +61,47 @@ pub struct Creds {
#[derive(Debug, Clone, Deserialize)]
pub struct Settings {
pub debug: bool,
pub api_keys: Vec<Creds>,
pub creds: Creds,
pub server: Server,
pub source_code: String,
pub source_code: Url,
pub conductor: ConductorType,
}
#[cfg(not(tarpaulin_include))]
impl Settings {
pub fn authenticate(&self, username: &str, password: &str) -> bool {
self.api_keys
.iter()
.any(|c| c.username == username && c.password == password)
self.creds.username == username && self.creds.password == password
}
pub fn new() -> Result<Self, ConfigError> {
let mut s = Config::new();
let mut s = Config::builder();
const CURRENT_DIR: &str = "./config/config.toml";
const ETC: &str = "/etc/lpconductor/config.toml";
if let Ok(path) = env::var("LPCONDUCTOR_CONFIG") {
s.merge(File::with_name(&path))?;
s = s.add_source(File::with_name(&path));
} else if Path::new(CURRENT_DIR).exists() {
// merging default config from file
s.merge(File::with_name(CURRENT_DIR))?;
s = s.add_source(File::with_name(CURRENT_DIR));
} else if Path::new(ETC).exists() {
s.merge(File::with_name(ETC))?;
s = s.add_source(File::with_name(ETC));
} else {
warn!("configuration file not found");
}
s.merge(Environment::with_prefix(PREFIX).separator(SEPARATOR))?;
set_separator_field(&mut s);
check_url(&s);
s = s.add_source(Environment::with_prefix(PREFIX).separator(SEPARATOR));
s = set_separator_field(s);
match env::var("PORT") {
Ok(val) => {
s.set("server.port", val).unwrap();
s = s.set_override("server.port", val).unwrap();
}
Err(e) => warn!("couldn't interpret PORT: {}", e),
}
match s.try_into::<Self>() {
let s = s.build()?;
match s.try_deserialize::<Self>() {
Ok(val) => {
Ok(val)
},
@ -114,36 +111,34 @@ impl Settings {
}
#[cfg(not(tarpaulin_include))]
fn set_separator_field(s: &mut Config) {
fn set_separator_field(mut s: ConfigBuilder<DefaultState>) -> ConfigBuilder<DefaultState> {
// ref: https://github.com/mehcode/config-rs/issues/391
fn from_env(s: &mut Config, env_name: &str, config_name: &str) {
fn from_env(
s: ConfigBuilder<DefaultState>,
env_name: &str,
config_name: &str,
) -> ConfigBuilder<DefaultState> {
if let Ok(val) = env::var(env_name) {
info!("Overriding {config_name} with data from env var {env_name}");
s.set(config_name, val)
.unwrap_or_else(|_| panic!("Couldn't set {config_name} from env var {env_name}"));
s.set_override(config_name, val)
.unwrap_or_else(|_| panic!("Couldn't set {config_name} from env var {env_name}"))
} else {
s
}
}
from_env(s, &format!("{PREFIX}{SEPARATOR}SOURCE_CODE"), "source_code");
from_env(
s = from_env(s, &format!("{PREFIX}{SEPARATOR}SOURCE_CODE"), "source_code");
s = from_env(
s,
&format!("{PREFIX}{SEPARATOR}SERVER{SEPARATOR}URL_PREFIX"),
"server.url_prefix",
);
from_env(
s = from_env(
s,
&format!("{PREFIX}{SEPARATOR}SERVER{SEPARATOR}PROXY_HAS_TLS"),
"server.proxy_has_tls",
);
}
#[cfg(not(tarpaulin_include))]
fn check_url(s: &Config) {
let url = s
.get::<String>("source_code")
.expect("Couldn't access source_code");
Url::parse(&url).expect("Please enter a URL for source_code in settings");
s
}
#[cfg(test)]
@ -153,14 +148,14 @@ mod tests {
#[test]
fn creds_works() {
let settings = Settings::new().unwrap();
let mut creds = settings.api_keys.get(0).unwrap().clone();
let mut creds = settings.creds.clone();
assert!(settings.authenticate(&creds.username, &creds.password));
creds.username = "noexist".into();
assert!(!settings.authenticate(&creds.username, &creds.password));
let mut creds = settings.api_keys.get(0).unwrap().clone();
let mut creds = settings.creds.clone();
creds.password = "noexist".into();
assert!(!settings.authenticate(&creds.username, &creds.password));