Compare commits

..

63 commits

Author SHA1 Message Date
8f766ae883 Merge pull request 'chore(deps): update rust crate base64 to 0.22.0' (#16) from renovate/base64-0.x into master
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
Reviewed-on: #16
2024-06-10 23:52:47 +05:30
a49b6eda71 Merge pull request 'fix(deps): update rust crate futures-util to v0.3.30' (#11) from renovate/rust-futures-monorepo into master
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
Reviewed-on: #11
2024-06-10 20:53:14 +05:30
61e1a04a74 Merge pull request 'fix(deps): update rust crate log to v0.4.21' (#12) from renovate/log-0.x-lockfile into master
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
Reviewed-on: #12
2024-06-10 20:53:07 +05:30
Renovate Bot
2951e6108b chore(deps): update rust crate base64 to 0.22.0
Some checks failed
renovate/artifacts Artifact file update failure
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/pr/woodpecker Pipeline was successful
ci/woodpecker/pull_request_closed/woodpecker Pipeline was successful
2024-06-10 14:00:47 +00:00
Renovate Bot
5be1af71f1 fix(deps): update rust crate log to v0.4.21
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/pr/woodpecker Pipeline was successful
ci/woodpecker/pull_request_closed/woodpecker Pipeline was successful
2024-06-10 14:00:42 +00:00
Renovate Bot
496a1015bd fix(deps): update rust crate futures-util to v0.3.30
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/pr/woodpecker Pipeline was successful
ci/woodpecker/pull_request_closed/woodpecker Pipeline was successful
2024-06-10 14:00:36 +00:00
97714408bc Merge pull request 'fix(deps): update rust crate rust-embed to v8' (#29) from renovate/rust-embed-8.x into master
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
Reviewed-on: #29
2024-06-10 19:05:03 +05:30
845d7b623b Merge pull request 'chore(deps): update debian docker tag to v12' (#28) from renovate/debian-12.x into master
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
Reviewed-on: #28
2024-06-10 19:04:58 +05:30
44bb60b819 Merge pull request 'fix(deps): update rust crate url to v2.5.1' (#27) from renovate/url-2.x-lockfile into master
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
Reviewed-on: #27
2024-06-10 19:04:56 +05:30
6fd98159f8 Merge pull request 'fix(deps): update rust crate derive_builder to 0.20.0' (#24) from renovate/derive_builder-0.x into master
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
Reviewed-on: #24
2024-06-10 19:04:49 +05:30
Renovate Bot
e9f1c04040 fix(deps): update rust crate rust-embed to v8
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/pr/woodpecker Pipeline was successful
ci/woodpecker/pull_request_closed/woodpecker Pipeline was successful
2024-06-10 09:30:47 +00:00
Renovate Bot
86e6b37baa chore(deps): update debian docker tag to v12
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/pr/woodpecker Pipeline was successful
ci/woodpecker/pull_request_closed/woodpecker Pipeline was successful
2024-06-10 09:30:44 +00:00
Renovate Bot
449f946c1a fix(deps): update rust crate url to v2.5.0
Some checks failed
ci/woodpecker/pr/woodpecker Pipeline was successful
renovate/artifacts Artifact file update failure
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/pull_request_closed/woodpecker Pipeline was successful
2024-06-10 09:30:41 +00:00
Renovate Bot
12326f8b22 fix(deps): update rust crate derive_builder to 0.20.0
All checks were successful
ci/woodpecker/pr/woodpecker Pipeline was successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/pull_request_closed/woodpecker Pipeline was successful
2024-06-10 09:30:34 +00:00
dcf93427a7 Merge pull request 'fix(deps): update rust crate rust-embed to v6.8.1' (#26) from renovate/rust-embed-6.x-lockfile into master
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
Reviewed-on: #26
2024-06-10 14:46:27 +05:30
8770086354 Merge pull request 'fix(deps): update rust crate pretty_env_logger to 0.5.0' (#25) from renovate/pretty_env_logger-0.x into master
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
Reviewed-on: #25
2024-06-10 14:46:23 +05:30
43827923d8 Merge pull request 'fix(deps): update rust crate config to 0.14' (#23) from renovate/config-0.x into master
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
Reviewed-on: #23
2024-06-10 14:46:12 +05:30
372fcd6688 Merge pull request 'chore(deps): update rust crate tokio to v1.38.0' (#18) from renovate/tokio-1.x-lockfile into master
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
Reviewed-on: #18
2024-06-10 14:35:55 +05:30
867375673e Merge pull request 'fix(deps): update rust crate async-trait to v0.1.80' (#10) from renovate/async-trait-0.x-lockfile into master
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
Reviewed-on: #10
2024-06-10 14:35:38 +05:30
Renovate Bot
374cec6eb5 fix(deps): update rust crate rust-embed to v6.8.1
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/pr/woodpecker Pipeline was successful
ci/woodpecker/pull_request_closed/woodpecker Pipeline was successful
2024-06-10 09:01:04 +00:00
Renovate Bot
200df429b8 fix(deps): update rust crate pretty_env_logger to 0.5.0
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/pr/woodpecker Pipeline was successful
ci/woodpecker/pull_request_closed/woodpecker Pipeline was successful
2024-06-10 09:00:56 +00:00
Renovate Bot
8c75edaec6 fix(deps): update rust crate config to 0.14
All checks were successful
ci/woodpecker/pr/woodpecker Pipeline was successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/pull_request_closed/woodpecker Pipeline was successful
2024-06-10 09:00:48 +00:00
Renovate Bot
e6fafdd9f1 chore(deps): update rust crate tokio to v1.38.0
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/pr/woodpecker Pipeline was successful
ci/woodpecker/pull_request_closed/woodpecker Pipeline was successful
2024-06-10 09:00:40 +00:00
Renovate Bot
00d1b90538 fix(deps): update rust crate async-trait to v0.1.80
All checks were successful
ci/woodpecker/pr/woodpecker Pipeline was successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/pull_request_closed/woodpecker Pipeline was successful
2024-06-10 09:00:29 +00:00
43b0226746 Merge pull request 'fix(deps): update rust crate actix-web-prom to 0.8.0' (#22) from renovate/actix-web-prom-0.x into master
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
Reviewed-on: #22
2024-06-10 14:17:35 +05:30
859c947db3 Merge pull request 'fix(deps): update rust crate serde_json to v1.0.117' (#14) from renovate/serde_json-1.x-lockfile into master
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
Reviewed-on: #14
2024-06-10 14:17:31 +05:30
f580c49a97 Merge pull request 'fix(deps): update rust crate serde to v1.0.203' (#13) from renovate/serde-monorepo into master
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
Reviewed-on: #13
2024-06-10 14:17:28 +05:30
b1d3c3e4a1 Merge pull request 'fix(deps): update rust crate actix-web-httpauth to v0.8.1' (#9) from renovate/actix-web-httpauth-0.x-lockfile into master
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
Reviewed-on: #9
2024-06-10 14:17:24 +05:30
Renovate Bot
9748936a72 fix(deps): update rust crate actix-web-prom to 0.8.0
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/pr/woodpecker Pipeline was successful
ci/woodpecker/pull_request_closed/woodpecker Pipeline was successful
2024-06-10 07:30:49 +00:00
Renovate Bot
3f05a42fa6 fix(deps): update rust crate serde_json to v1.0.117
All checks were successful
ci/woodpecker/pr/woodpecker Pipeline was successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/pull_request_closed/woodpecker Pipeline was successful
2024-06-10 07:30:41 +00:00
Renovate Bot
09756b6301 fix(deps): update rust crate serde to v1.0.203
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/pr/woodpecker Pipeline was successful
ci/woodpecker/pull_request_closed/woodpecker Pipeline was successful
2024-06-10 07:30:37 +00:00
Renovate Bot
7c038bae36 fix(deps): update rust crate actix-web-httpauth to v0.8.1
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/pr/woodpecker Pipeline was successful
ci/woodpecker/pull_request_closed/woodpecker Pipeline was successful
2024-06-10 07:30:31 +00:00
6aa3c4ffba Merge pull request 'fix(deps): update rust crate actix-web to v4.7.0' (#21) from renovate/actix-web-4.x-lockfile into master
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
Reviewed-on: #21
2024-06-10 12:51:00 +05:30
Renovate Bot
6377333b84 fix(deps): update rust crate actix-web to v4.7.0
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/pr/woodpecker Pipeline was successful
ci/woodpecker/pull_request_closed/woodpecker Pipeline was successful
2024-06-09 20:00:47 +00:00
a63d251f59 Merge pull request 'chore(deps): update rust crate actix-rt to v2.10.0' (#15) from renovate/actix-rt-2.x-lockfile into master
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
Reviewed-on: #15
2024-06-10 01:07:40 +05:30
Renovate Bot
4ac2b04efd chore(deps): update rust crate actix-rt to v2.10.0
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/pr/woodpecker Pipeline was successful
ci/woodpecker/pull_request_closed/woodpecker Pipeline was successful
2024-06-09 18:00:50 +00:00
5932a3e96d Merge pull request 'fix: CI: publish only on master' (#20) from fix-ci-deploy into master
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
Reviewed-on: #20
2024-06-08 02:19:24 +05:30
fdc7b10a6e
fix: CI: publish only on master
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/pr/woodpecker Pipeline was successful
ci/woodpecker/pull_request_closed/woodpecker Pipeline was successful
2024-06-07 23:20:53 +05:30
d25a4e9808 Merge pull request 'chore: Configure Renovate' (#8) from renovate/configure into master
Reviewed-on: #8
2024-06-04 10:15:06 +05:30
Renovate Bot
15b58dab9f Add renovate.json 2024-06-03 03:00:15 +00:00
b0d94f91dc
feat: run conductor as root
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2022-12-30 05:18:43 +05:30
d40e8642de
feat: publish systemd service file
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2022-12-30 04:44:39 +05:30
5851b686b4
feat: read config from /etc/librepages/conductor/ 2022-12-30 04:44:30 +05:30
db9115b90b
feat: read token from env var
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2022-12-29 18:34:39 +05:30
b15c72ef30
feat: read token from env var
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2022-12-29 17:48:30 +05:30
cd0589fb2e
feat: replace http auth with bearer auth
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2022-12-29 17:29:07 +05:30
58eef6b3fa
feat: add prometheus instrumentation
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2022-12-25 13:14:00 +05:30
158ec03aab
fix: CI: dl.librepages.org username and bin name
Some checks are pending
ci/woodpecker/push/woodpecker Pipeline is pending
2022-12-12 21:25:55 +05:30
0e388d4e1e
fix: CI: apt install cmd
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2022-12-12 21:08:38 +05:30
ef0175eca0
fix: build and publish bins
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2022-12-12 21:00:18 +05:30
838cb9387a
fix: CI: rm nginx workflows
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2022-12-12 20:31:55 +05:30
15a17a184d
feat: publish bins to dl.librepages.org
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2022-12-12 20:27:32 +05:30
96c1b807a7
feat: run tests on all workspaces and lints
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2022-12-12 15:15:11 +05:30
4db76a0705
fix: launch on localhost 2022-12-12 15:14:55 +05:30
ccb9f0f046
fix: dummy conductor test is async 2022-12-12 15:14:41 +05:30
3a2e6355da
feat: serve openapi docs
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2022-12-11 23:43:13 +05:30
a38411abaa
feat: openapi spec 2022-12-11 23:42:46 +05:30
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
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
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
1b40c44854
feat: delete stie event
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2022-12-11 18:49:04 +05:30
20aa88ca51
fix: new site deployment requires extra info
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2022-12-09 18:04:26 +05:30
8d9bc95bf6
feat: add event type to transmit deployment configuration 2022-12-09 18:02:48 +05:30
41 changed files with 1804 additions and 463 deletions

View file

@ -1,5 +1,5 @@
pipeline:
backend:
steps:
test:
image: rust
# environment:
# - DATABASE_URL=postgres://postgres:password@database:5432/postgres
@ -7,9 +7,25 @@ pipeline:
# - make migrate
- make
- make test
- make release
publish_bins:
image: rust
when:
event: [push, tag, deployment]
branch: master
commands:
- apt update
- apt-get -y --no-install-recommends install gpg tar curl wget
- echo -n "$RELEASE_BOT_GPG_SIGNING_KEY" | gpg --batch --import --pinentry-mode loopback
- ./scripts/bin-publish.sh publish master latest $DUMBSERVE_PASSWORD
secrets: [ RELEASE_BOT_GPG_SIGNING_KEY, DUMBSERVE_PASSWORD, GPG_PASSWORD ]
publish:
image: plugins/docker
when:
event: [push, tag, deployment]
branch: master
settings:
username: realaravinth
password:

1201
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -12,19 +12,23 @@ build = "build.rs"
[dependencies]
actix-web = "4"
actix-web-prom = "0.8.0"
futures-util = { version = "0.3.17", default-features = false, features = ["std"] }
lazy_static = "1.4.0"
log = "0.4.17"
pretty_env_logger = "0.4.0"
pretty_env_logger = "0.5.0"
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" }
derive_builder = "0.11.2"
config = "0.11"
libconfig = { version = "0.1.0", git = "https://git.batsense.net/librepages/libconfig" }
derive_builder = "0.20.0"
config = "0.14"
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 = "8.0.0"
[dependencies.libconductor]
path = "./env/libconductor"
@ -38,4 +42,4 @@ sqlx = { version = "0.6.1", features = [ "runtime-actix-rustls", "postgres", "ti
[dev-dependencies]
actix-rt = "2.7.0"
base64 = "0.13.0"
base64 = "0.22.0"

View file

@ -19,7 +19,7 @@ RUN cargo --version
#RUN make cache-bust
RUN cargo build --release
FROM debian:bullseye as conductor
FROM debian:bookworm as conductor
LABEL org.opencontainers.image.source https://git.batsense.net/librepages/conductor
RUN apt-get update && apt-get install -y ca-certificates
RUN useradd -ms /bin/bash -u 1001 conductor

View file

@ -1,3 +1,12 @@
define lint
cargo fmt -v --all -- --emit files
cargo clippy --workspace --tests --all-features
endef
define test
cargo test --no-fail-fast --workspace --tests --all-features
endef
default: ## Build app in debug mode
cargo build
@ -25,8 +34,9 @@ env: ## Setup development environtment
cargo fetch
lint: ## Lint codebase
cargo fmt -v --all -- --emit files
cargo clippy --workspace --tests --all-features
$(call lint)
cd env/dummy_conductor && $(call lint)
cd env/libconductor && $(call lint)
#migrate: ## run migrations
# unset DATABASE_URL && cargo build
@ -36,7 +46,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
@ -45,7 +55,8 @@ run: ## Run app in debug mode
# --all-features
test: ## Run all available tests
cargo test --no-fail-fast --workspace
$(call test)
cd env/dummy_conductor && $(call test)
xml-test-coverage: ## Generate code coverage report in XML format
cargo tarpaulin -t 1200 --out Xml

View file

@ -1,10 +1,9 @@
debug = true
source_code = "https://git.batsense.net/librepages/conductor"
conductor = "dummy"
api_keys = [
# CHANGE THIS!!
{ username = "librepages_api", password="longrandomlygeneratedpassword"}
]
[creds]
token="longrandomlygeneratedpassword"
[server]
# Please set a unique value, your mCaptcha instance's security depends on this being
@ -14,7 +13,7 @@ api_keys = [
port = 7000
#IP address. Enter 0.0.0.0 to listen on all available addresses
#ip= "0.0.0.0"
ip= "192.168.0.104"
ip= "127.0.0.1"
# enter your hostname, eg: example.com
domain = "localhost"
# Set true if you have setup TLS with a reverse proxy like Nginx.

View file

@ -0,0 +1,24 @@
[Unit]
Description=LibrePages Conductor: Easiest way to deploy websites. Conductor component
[Service]
Type=simple
User=root
ExecStart=/usr/bin/conductor serve
Restart=on-failure
RestartSec=1
SuccessExitStatus=3 4
RestartForceExitStatus=3 4
SystemCallArchitectures=native
MemoryDenyWriteExecute=true
NoNewPrivileges=true
Environment="RUST_LOG=info"
[Unit]
Wants=network-online.target
Wants=network-online.target
Requires=postgresql.service
After=syslog.target
[Install]
WantedBy=multi-user.target

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

185
env/dummy_conductor/Cargo.lock generated vendored
View file

@ -3,16 +3,58 @@
version = 3
[[package]]
name = "async-trait"
version = "0.1.57"
name = "addr2line"
version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76464446b8bc32758d7e88ee1a804d9914cd9b1cb264c029899680b0be29826f"
checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678"
dependencies = [
"gimli",
]
[[package]]
name = "adler"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "async-trait"
version = "0.1.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.66",
]
[[package]]
name = "backtrace"
version = "0.3.72"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17c6a35df3749d2e8bb1b7b21a976d82b15548788d2735b9d82f329268f71a11"
dependencies = [
"addr2line",
"cc",
"cfg-if",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
]
[[package]]
name = "cc"
version = "1.0.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "dummy_conductor"
version = "0.1.0"
@ -21,6 +63,22 @@ dependencies = [
"libconductor",
"serde",
"serde_json",
"tokio",
]
[[package]]
name = "gimli"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd"
[[package]]
name = "hermit-abi"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
dependencies = [
"libc",
]
[[package]]
@ -29,33 +87,94 @@ version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754"
[[package]]
name = "libc"
version = "0.2.155"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
[[package]]
name = "libconductor"
version = "0.1.0"
dependencies = [
"async-trait",
"libconfig",
"serde",
"serde_json",
]
[[package]]
name = "proc-macro2"
version = "1.0.46"
name = "libconfig"
version = "0.1.0"
source = "git+https://git.batsense.net/librepages/libconfig#f54290c4bae26b51a4945e0bf812e2b99856963b"
dependencies = [
"serde",
]
[[package]]
name = "memchr"
version = "2.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94e2ef8dbfc347b10c094890f778ee2e36ca9bb4262e86dc99cd217e35f3470b"
checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
[[package]]
name = "miniz_oxide"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae"
dependencies = [
"adler",
]
[[package]]
name = "num_cpus"
version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5"
dependencies = [
"hermit-abi",
"libc",
]
[[package]]
name = "object"
version = "0.35.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8ec7ab813848ba4522158d5517a6093db1ded27575b070f4177b8d12b41db5e"
dependencies = [
"memchr",
]
[[package]]
name = "pin-project-lite"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
[[package]]
name = "proc-macro2"
version = "1.0.85"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.21"
version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rustc-demangle"
version = "0.1.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
[[package]]
name = "ryu"
version = "1.0.11"
@ -64,29 +183,29 @@ checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
[[package]]
name = "serde"
version = "1.0.145"
version = "1.0.203"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b"
checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.145"
version = "1.0.203"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c"
checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.66",
]
[[package]]
name = "serde_json"
version = "1.0.85"
version = "1.0.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44"
checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3"
dependencies = [
"itoa",
"ryu",
@ -104,6 +223,40 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "syn"
version = "2.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "tokio"
version = "1.38.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a"
dependencies = [
"backtrace",
"num_cpus",
"pin-project-lite",
"tokio-macros",
]
[[package]]
name = "tokio-macros"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.66",
]
[[package]]
name = "unicode-ident"
version = "1.0.4"

View file

@ -10,5 +10,8 @@ serde = { version = "1", features=["derive"]}
serde_json = { version ="1", features = ["raw_value"]}
async-trait = "0.1.57"
[dev-dependencies]
tokio = { version = "1.23.0", features = ["rt-multi-thread", "macros", "rt"] }
[dependencies.libconductor]
path = "../libconductor"

View file

@ -41,8 +41,8 @@ mod tests {
use super::*;
#[test]
fn all_good() {
#[tokio::test]
async fn all_good() {
let c = DummyConductor {};
assert_eq!(c.name(), DUMMY_CONDUCTOR_NAME);
assert!(c.health().await);

37
env/libconductor/Cargo.lock generated vendored
View file

@ -4,9 +4,9 @@ version = 3
[[package]]
name = "async-trait"
version = "0.1.57"
version = "0.1.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76464446b8bc32758d7e88ee1a804d9914cd9b1cb264c029899680b0be29826f"
checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca"
dependencies = [
"proc-macro2",
"quote",
@ -24,24 +24,33 @@ name = "libconductor"
version = "0.1.0"
dependencies = [
"async-trait",
"libconfig",
"serde",
"serde_json",
]
[[package]]
name = "libconfig"
version = "0.1.0"
source = "git+https://git.batsense.net/librepages/libconfig#f54290c4bae26b51a4945e0bf812e2b99856963b"
dependencies = [
"serde",
]
[[package]]
name = "proc-macro2"
version = "1.0.46"
version = "1.0.85"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94e2ef8dbfc347b10c094890f778ee2e36ca9bb4262e86dc99cd217e35f3470b"
checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.21"
version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
dependencies = [
"proc-macro2",
]
@ -54,18 +63,18 @@ checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
[[package]]
name = "serde"
version = "1.0.145"
version = "1.0.203"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b"
checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.145"
version = "1.0.203"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c"
checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba"
dependencies = [
"proc-macro2",
"quote",
@ -74,9 +83,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.85"
version = "1.0.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44"
checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3"
dependencies = [
"itoa",
"ryu",
@ -85,9 +94,9 @@ dependencies = [
[[package]]
name = "syn"
version = "1.0.101"
version = "2.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e90cde112c4b9690b8cbe810cba9ddd8bc1d7472e2cae317b69e9438c1cba7d2"
checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5"
dependencies = [
"proc-macro2",
"quote",

View file

@ -9,6 +9,7 @@ edition = "2021"
serde = { version = "1", features=["derive"]}
serde_json = { version ="1", features = ["raw_value"]}
async-trait = { version = "0.1.57", optional = true}
libconfig = { version = "0.1.0", git = "https://git.batsense.net/librepages/libconfig" }
[features]
default = [

View file

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

21
renovate.json Normal file
View file

@ -0,0 +1,21 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:recommended",
":dependencyDashboard"
],
"labels": [
"renovate-bot"
],
"prHourlyLimit": 0,
"timezone": "Asia/kolkata",
"prCreation": "immediate",
"vulnerabilityAlerts": {
"enabled": true,
"labels": [
"renovate-bot",
"renovate-security",
"security"
]
}
}

117
scripts/bin-publish.sh Executable file
View file

@ -0,0 +1,117 @@
#!/bin/bash
# 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/>.
# publish.sh: grab bin from docker container, pack, sign and upload
# $2: binary version
# $3: Docker img tag
# $4: dumbserve password
set -xEeuo pipefail
DUMBSERVE_USERNAME=librepages
DUMBSERVE_PASSWORD=$4
DUMBSERVE_HOST="https://$DUMBSERVE_USERNAME:$DUMBSERVE_PASSWORD@dl.librepages.org"
NAME=conductor
KEY=67880CA5F4BC99BF247330E2DA576B07BC323961
TMP_DIR=$(mktemp -d)
FILENAME="$NAME-$2-linux-amd64"
TARBALL=$FILENAME.tar.gz
TARGET_DIR="$TMP_DIR/$FILENAME/"
mkdir -p $TARGET_DIR
DOCKER_IMG="realaravinth/$NAME:$3"
get_bin(){
cp target/release/conductor $TARGET_DIR
cp -r config/ $TARGET_DIR
cp -r contrib/ $TARGET_DIR
}
copy() {
echo "[*] Copying dist assets"
cp README.md $TARGET_DIR
cp LICENSE.md $TARGET_DIR
mkdir $TARGET_DIR/docs
# cp docs/CONFIGURATION.md $TARGET_DIR/docs
# cp -r docs/installation/ $TARGET_DIR/docs
get_bin
}
pack() {
echo "[*] Creating dist tarball"
pushd $TMP_DIR
tar -cvzf $TARBALL $FILENAME
popd
}
checksum() {
echo "[*] Generating dist tarball checksum"
pushd $TMP_DIR
sha256sum $TARBALL > $TARBALL.sha256
popd
}
sign() {
echo "[*] Signing dist tarball checksum"
pushd $TMP_DIR
export GPG_TTY=$(tty)
gpg --verbose \
--pinentry-mode loopback \
--batch --yes \
--passphrase $GPG_PASSWORD \
--local-user $KEY \
--output $TARBALL.asc \
--sign --detach \
--armor $TARBALL
popd
}
delete_dir() {
curl --location --request DELETE "$DUMBSERVE_HOST/api/v1/files/delete" \
--header 'Content-Type: application/json' \
--data-raw "{
\"path\": \"$1\"
}"
}
upload_dist() {
upload_dist="conductor/$1"
delete_dir $upload_dist
pushd $TMP_DIR
for file in $TARBALL $TARBALL.asc $TARBALL.sha256
do
curl -v \
-F upload=@$file \
"$DUMBSERVE_HOST/api/v1/files/upload?path=$upload_dist/"
done
popd
}
publish() {
copy
pack
checksum
sign
upload_dist $2
}
$1 $@

View file

@ -17,7 +17,7 @@
use actix_web::dev::ServiceRequest;
use actix_web::web;
use actix_web::Error;
use actix_web_httpauth::extractors::basic::BasicAuth;
use actix_web_httpauth::extractors::bearer::BearerAuth;
use crate::errors::*;
use crate::AppCtx;
@ -26,14 +26,13 @@ use crate::SETTINGS;
pub mod meta;
pub mod webhook;
pub async fn httpauth(
pub async fn bearerauth(
req: ServiceRequest,
credentials: BasicAuth,
credentials: BearerAuth,
) -> Result<ServiceRequest, (Error, ServiceRequest)> {
let _ctx: &AppCtx = req.app_data().unwrap();
let username = credentials.user_id();
let password = credentials.password().unwrap();
if SETTINGS.authenticate(username, password) {
let token = credentials.token();
if SETTINGS.authenticate(token) {
Ok(req)
} else {
let e = Error::from(ServiceError::Unauthorized);

View file

@ -24,7 +24,7 @@ use crate::errors::*;
use crate::AppCtx;
use crate::*;
use super::httpauth;
use super::bearerauth;
pub mod routes {
use super::*;
@ -47,7 +47,7 @@ pub fn services(cfg: &mut web::ServiceConfig) {
#[actix_web_codegen_const_routes::post(
path = "API_V1_ROUTES.webhook.post_event",
wrap = "HttpAuthentication::basic(httpauth)"
wrap = "HttpAuthentication::bearer(bearerauth)"
)]
async fn post_event(ctx: AppCtx, payload: web::Json<EventType>) -> ServiceResult<impl Responder> {
ctx.conductor.process(payload.into_inner()).await;
@ -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,14 @@ pub mod tests {
)
.await;
let creds = settings.api_keys.get(0).unwrap().clone();
let auth = format!(
"Basic {}",
base64::encode(format!("{}:{}", creds.username.clone(), creds.password))
);
let creds = settings.creds.clone();
let auth = format!("Bearer {}", creds.token,);
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 +85,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 {

128
src/docs.rs Normal file
View file

@ -0,0 +1,128 @@
/*
* 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

@ -19,6 +19,7 @@ use std::env;
use actix_web::http::StatusCode;
use actix_web::web::JsonConfig;
use actix_web::{error::InternalError, middleware, App, HttpServer};
use actix_web_prom::PrometheusMetricsBuilder;
use clap::{Parser, Subcommand};
use log::info;
@ -26,7 +27,7 @@ use lazy_static::lazy_static;
mod api;
mod ctx;
//mod docs;
mod docs;
#[cfg(not(tarpaulin_include))]
mod errors;
//#[macro_use]
@ -112,6 +113,11 @@ async fn serve(settings: Settings, ctx: AppCtx) -> std::io::Result<()> {
let ip = settings.server.get_ip();
println!("Starting server on: http://{ip}");
let prometheus = PrometheusMetricsBuilder::new("api")
.endpoint("/metrics")
.build()
.unwrap();
HttpServer::new(move || {
App::new()
.wrap(middleware::Logger::default())
@ -124,6 +130,7 @@ async fn serve(settings: Settings, ctx: AppCtx) -> std::io::Result<()> {
middleware::TrailingSlash::Trim,
))
.app_data(get_json_err())
.wrap(prometheus.clone())
.configure(routes::services)
})
.bind(ip)?

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;
@ -54,57 +54,53 @@ pub enum ConductorType {
#[derive(Debug, Clone, Deserialize)]
pub struct Creds {
pub username: String,
pub password: String,
pub token: String,
}
#[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)
pub fn authenticate(&self, token: &str) -> bool {
self.creds.token == token
}
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";
const ETC: &str = "/etc/librepages/conductor/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 +110,41 @@ 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");
s = from_env(
s,
&format!("{PREFIX}{SEPARATOR}CREDS{SEPARATOR}TOKEN"),
"creds.token",
);
Url::parse(&url).expect("Please enter a URL for source_code in settings");
s
}
#[cfg(test)]
@ -153,16 +154,13 @@ mod tests {
#[test]
fn creds_works() {
let settings = Settings::new().unwrap();
let mut creds = settings.api_keys.get(0).unwrap().clone();
let creds = settings.creds.clone();
assert!(settings.authenticate(&creds.username, &creds.password));
assert!(settings.authenticate(&creds.token));
creds.username = "noexist".into();
assert!(!settings.authenticate(&creds.username, &creds.password));
let mut creds = settings.creds.clone();
let mut creds = settings.api_keys.get(0).unwrap().clone();
creds.password = "noexist".into();
assert!(!settings.authenticate(&creds.username, &creds.password));
creds.token = "noexist".into();
assert!(!settings.authenticate(&creds.token))
}
}