Compare commits
63 commits
Author | SHA1 | Date | |
---|---|---|---|
8f766ae883 | |||
a49b6eda71 | |||
61e1a04a74 | |||
|
2951e6108b | ||
|
5be1af71f1 | ||
|
496a1015bd | ||
97714408bc | |||
845d7b623b | |||
44bb60b819 | |||
6fd98159f8 | |||
|
e9f1c04040 | ||
|
86e6b37baa | ||
|
449f946c1a | ||
|
12326f8b22 | ||
dcf93427a7 | |||
8770086354 | |||
43827923d8 | |||
372fcd6688 | |||
867375673e | |||
|
374cec6eb5 | ||
|
200df429b8 | ||
|
8c75edaec6 | ||
|
e6fafdd9f1 | ||
|
00d1b90538 | ||
43b0226746 | |||
859c947db3 | |||
f580c49a97 | |||
b1d3c3e4a1 | |||
|
9748936a72 | ||
|
3f05a42fa6 | ||
|
09756b6301 | ||
|
7c038bae36 | ||
6aa3c4ffba | |||
|
6377333b84 | ||
a63d251f59 | |||
|
4ac2b04efd | ||
5932a3e96d | |||
fdc7b10a6e | |||
d25a4e9808 | |||
|
15b58dab9f | ||
b0d94f91dc | |||
d40e8642de | |||
5851b686b4 | |||
db9115b90b | |||
b15c72ef30 | |||
cd0589fb2e | |||
58eef6b3fa | |||
158ec03aab | |||
0e388d4e1e | |||
ef0175eca0 | |||
838cb9387a | |||
15a17a184d | |||
96c1b807a7 | |||
4db76a0705 | |||
ccb9f0f046 | |||
3a2e6355da | |||
a38411abaa | |||
b8246f0fd8 | |||
1fa28ef9b7 | |||
a4f4903120 | |||
1b40c44854 | |||
20aa88ca51 | |||
8d9bc95bf6 |
41 changed files with 1804 additions and 463 deletions
|
@ -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
1201
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
12
Cargo.toml
12
Cargo.toml
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
19
Makefile
19
Makefile
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
24
contrib/librepages-conductor.service
Normal file
24
contrib/librepages-conductor.service
Normal 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
|
BIN
docs/openapi/favicon-16x16.png
Normal file
BIN
docs/openapi/favicon-16x16.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 665 B |
BIN
docs/openapi/favicon-32x32.png
Normal file
BIN
docs/openapi/favicon-32x32.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 628 B |
16
docs/openapi/index.css
Normal file
16
docs/openapi/index.css
Normal 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
19
docs/openapi/index.html
Normal 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>
|
79
docs/openapi/oauth2-redirect.html
Normal file
79
docs/openapi/oauth2-redirect.html
Normal 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
178
docs/openapi/openapi.yml
Normal 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
|
20
docs/openapi/swagger-initializer.js
Normal file
20
docs/openapi/swagger-initializer.js
Normal 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>
|
||||
};
|
3
docs/openapi/swagger-ui-bundle.js
Normal file
3
docs/openapi/swagger-ui-bundle.js
Normal file
File diff suppressed because one or more lines are too long
1
docs/openapi/swagger-ui-bundle.js.map
Normal file
1
docs/openapi/swagger-ui-bundle.js.map
Normal file
File diff suppressed because one or more lines are too long
3
docs/openapi/swagger-ui-es-bundle-core.js
Normal file
3
docs/openapi/swagger-ui-es-bundle-core.js
Normal file
File diff suppressed because one or more lines are too long
1
docs/openapi/swagger-ui-es-bundle-core.js.map
Normal file
1
docs/openapi/swagger-ui-es-bundle-core.js.map
Normal file
File diff suppressed because one or more lines are too long
3
docs/openapi/swagger-ui-es-bundle.js
Normal file
3
docs/openapi/swagger-ui-es-bundle.js
Normal file
File diff suppressed because one or more lines are too long
1
docs/openapi/swagger-ui-es-bundle.js.map
Normal file
1
docs/openapi/swagger-ui-es-bundle.js.map
Normal file
File diff suppressed because one or more lines are too long
3
docs/openapi/swagger-ui-standalone-preset.js
Normal file
3
docs/openapi/swagger-ui-standalone-preset.js
Normal file
File diff suppressed because one or more lines are too long
1
docs/openapi/swagger-ui-standalone-preset.js.map
Normal file
1
docs/openapi/swagger-ui-standalone-preset.js.map
Normal file
File diff suppressed because one or more lines are too long
3
docs/openapi/swagger-ui.css
Normal file
3
docs/openapi/swagger-ui.css
Normal file
File diff suppressed because one or more lines are too long
1
docs/openapi/swagger-ui.css.map
Normal file
1
docs/openapi/swagger-ui.css.map
Normal file
File diff suppressed because one or more lines are too long
2
docs/openapi/swagger-ui.js
Normal file
2
docs/openapi/swagger-ui.js
Normal file
File diff suppressed because one or more lines are too long
1
docs/openapi/swagger-ui.js.map
Normal file
1
docs/openapi/swagger-ui.js.map
Normal file
File diff suppressed because one or more lines are too long
185
env/dummy_conductor/Cargo.lock
generated
vendored
185
env/dummy_conductor/Cargo.lock
generated
vendored
|
@ -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"
|
||||
|
|
3
env/dummy_conductor/Cargo.toml
vendored
3
env/dummy_conductor/Cargo.toml
vendored
|
@ -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"
|
||||
|
|
4
env/dummy_conductor/src/lib.rs
vendored
4
env/dummy_conductor/src/lib.rs
vendored
|
@ -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
37
env/libconductor/Cargo.lock
generated
vendored
|
@ -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",
|
||||
|
|
1
env/libconductor/Cargo.toml
vendored
1
env/libconductor/Cargo.toml
vendored
|
@ -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 = [
|
||||
|
|
14
env/libconductor/src/event_types.rs
vendored
14
env/libconductor/src/event_types.rs
vendored
|
@ -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
21
renovate.json
Normal 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
117
scripts/bin-publish.sh
Executable 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 $@
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
10
src/ctx.rs
10
src/ctx.rs
|
@ -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
128
src/docs.rs
Normal 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);
|
||||
}
|
||||
}
|
|
@ -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)?
|
||||
|
|
|
@ -17,5 +17,6 @@
|
|||
use actix_web::web;
|
||||
|
||||
pub fn services(cfg: &mut web::ServiceConfig) {
|
||||
crate::docs::services(cfg);
|
||||
crate::api::v1::services(cfg);
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue