Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ec1eb83814 | ||
|
|
b24dc346bb |
@@ -1,66 +0,0 @@
|
|||||||
# -*- ispell-local-dictionary: "english" -*-
|
|
||||||
|
|
||||||
* Info
|
|
||||||
|
|
||||||
From: Carbone https://hub.docker.com/r/carbone/carbone-ee#running-carbone-community-edition-forever-free
|
|
||||||
|
|
||||||
Upstream documentation:
|
|
||||||
- On-premise configuration: https://carbone.io/documentation/developer/on-premise-installation/configuration.html
|
|
||||||
- Template management: https://carbone.io/documentation/developer/on-premise-installation/template-management.html
|
|
||||||
- Studio Web Component: https://carbone.io/documentation/developer/embedding/studio-web-component.html
|
|
||||||
|
|
||||||
* What this charm enables
|
|
||||||
|
|
||||||
By default Carbone starts in the *stateless* mode: it only renders the
|
|
||||||
template sent with each request, exposes no Studio and keeps no template
|
|
||||||
metadata. The =init= hook turns it into the *stateful* service, which is
|
|
||||||
what the charm is meant to provide:
|
|
||||||
|
|
||||||
- =CARBONE_STUDIO=true= serves the Studio web interface and the
|
|
||||||
=/carbone-studio.js= Web Component.
|
|
||||||
- =CARBONE_TEMPLATE_MANAGEMENT=true= enables stable 64-bit template IDs,
|
|
||||||
versioning, the =GET/POST/PATCH /template= endpoints and the
|
|
||||||
=embedded-versioning= Studio mode.
|
|
||||||
- =CARBONE_BIND=0.0.0.0= makes Carbone listen on all interfaces so the
|
|
||||||
=web-proxy= can reach it (the upstream default is =127.0.0.1=).
|
|
||||||
|
|
||||||
* Usage
|
|
||||||
|
|
||||||
ex :
|
|
||||||
|
|
||||||
#+begin_src yaml
|
|
||||||
carbone:
|
|
||||||
relations:
|
|
||||||
web-proxy:
|
|
||||||
frontend:
|
|
||||||
domain: carbone.dev1.elabore.coop
|
|
||||||
#+end_src
|
|
||||||
|
|
||||||
* Persistence
|
|
||||||
|
|
||||||
| Path | Resource type | Content |
|
|
||||||
|-----------------+------------------+------------------------------------------------------|
|
|
||||||
| =/app/template= | =data-resources= | Templates and the metadata store (metadata.db) |
|
|
||||||
| =/app/config= | =config-resources= | Optional license, authentication public key (key.pub) |
|
|
||||||
|
|
||||||
Templates and their versioning metadata both live under =/app/template=,
|
|
||||||
so they survive redeploys and are covered by the =backup= relation.
|
|
||||||
|
|
||||||
* Enterprise license
|
|
||||||
|
|
||||||
Carbone runs fine without a license (Community Edition, forever free).
|
|
||||||
Advanced features (dynamic images/colors, barcodes, charts, HTML
|
|
||||||
aggregation, PDF operations, ...) require an Enterprise license.
|
|
||||||
|
|
||||||
To enable it, drop your =*.carbone-license= file into the config store
|
|
||||||
(=/app/config= on the host side, i.e.
|
|
||||||
=$SERVICE_CONFIGSTORE/app/config=) and redeploy the service.
|
|
||||||
|
|
||||||
* API authentication
|
|
||||||
|
|
||||||
Authentication is *disabled by default*: the API is reachable as soon as
|
|
||||||
the service is up, which is the expected setup behind the =web-proxy=.
|
|
||||||
If you ever need an API key (JWT), enable =CARBONE_AUTHENTICATION=true=,
|
|
||||||
generate an EC keypair with =generate-keys=, expose the public key as
|
|
||||||
=/app/config/key.pub= and generate a token with =generate-token=. See
|
|
||||||
the upstream "Deploy with Docker" documentation for the exact commands.
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
## Init is run on host
|
|
||||||
## For now it is run every time the script is launched, but
|
|
||||||
## it should be launched only once after build.
|
|
||||||
|
|
||||||
## Accessible variables are:
|
|
||||||
## - SERVICE_NAME Name of current service
|
|
||||||
## - DOCKER_BASE_IMAGE Base image from which this service might be built if any
|
|
||||||
## - SERVICE_DATASTORE Location on host of the DATASTORE of this service
|
|
||||||
## - SERVICE_CONFIGSTORE Location on host of the CONFIGSTORE of this service
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
## The image runs as the unprivileged "carbone" user, while the datastore
|
|
||||||
## and configstore bind mounts are created by root. Both /app/template
|
|
||||||
## (templates + metadata.db) and /app/config (config.json, keys, license)
|
|
||||||
## must be writable by that user, otherwise the container crash-loops with
|
|
||||||
## "EACCES: permission denied, open '/app/config/config.json'".
|
|
||||||
uid_gid=($(docker_get_uid_gid "$SERVICE_NAME" "carbone" "carbone")) || exit 1
|
|
||||||
uid="${uid_gid[0]}"
|
|
||||||
gid="${uid_gid[1]}"
|
|
||||||
|
|
||||||
mkdir -p "$SERVICE_DATASTORE/app/template" "$SERVICE_CONFIGSTORE/app/config"
|
|
||||||
chown "$uid:$gid" "$SERVICE_DATASTORE/app/template" "$SERVICE_CONFIGSTORE/app/config"
|
|
||||||
|
|
||||||
## Carbone starts in stateless mode (document generation only) and binds
|
|
||||||
## to 127.0.0.1 by default (see `carbone webserver --help`, v5.15.1).
|
|
||||||
## Inside Docker it must:
|
|
||||||
## - listen on all interfaces so the web-proxy can reach it (CARBONE_BIND) ;
|
|
||||||
## - serve the Studio web interface and /carbone-studio.js (CARBONE_STUDIO) ;
|
|
||||||
## - enable stateful template management: stable 64-bit template IDs,
|
|
||||||
## versioning and the GET/POST/PATCH /template endpoints
|
|
||||||
## (CARBONE_TEMPLATE_MANAGEMENT).
|
|
||||||
##
|
|
||||||
## CARBONE_TEMPLATE_METADATA_FLUSH_CRON defaults to once a day; flushing
|
|
||||||
## every 5 minutes shrinks the window where a hard crash (SIGKILL, OOM)
|
|
||||||
## would lose template metadata. The graceful-shutdown flush remains the
|
|
||||||
## safety net for redeploys (see docker-compose.stop_grace_period).
|
|
||||||
init-config-add "
|
|
||||||
$SERVICE_NAME:
|
|
||||||
environment:
|
|
||||||
CARBONE_BIND: \"0.0.0.0\"
|
|
||||||
CARBONE_STUDIO: \"true\"
|
|
||||||
CARBONE_TEMPLATE_MANAGEMENT: \"true\"
|
|
||||||
CARBONE_TEMPLATE_METADATA_FLUSH_CRON: \"*/5 * * * *\"
|
|
||||||
"
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
## From carbone/carbone-ee:full-5.15.1
|
|
||||||
docker-image: docker.0k.io/carbone-ee:5.15.1
|
|
||||||
docker-compose:
|
|
||||||
## Carbone flushes its template metadata to disk on graceful shutdown.
|
|
||||||
## Docker's default 10s stop timeout is shorter than Carbone's shutdown
|
|
||||||
## sequence (exitQuietPeriod + flush), so a plain redeploy would SIGKILL
|
|
||||||
## it and lose the templates/versioning. Keep this above Carbone's
|
|
||||||
## exitDeadline (default 3 min).
|
|
||||||
stop_grace_period: 200s
|
|
||||||
data-resources:
|
|
||||||
## Templates and the template-management metadata store (metadata.db)
|
|
||||||
## both live here, so they survive redeploys and are covered by backup.
|
|
||||||
- /app/template
|
|
||||||
config-resources:
|
|
||||||
## Holds the optional Enterprise license (*.carbone-license) and the
|
|
||||||
## authentication public key (key.pub). See README.org.
|
|
||||||
- /app/config
|
|
||||||
|
|
||||||
uses:
|
|
||||||
web-proxy:
|
|
||||||
#constraint: required | recommended | optional
|
|
||||||
#auto: pair | summon | none ## default: pair
|
|
||||||
constraint: recommended
|
|
||||||
auto: pair
|
|
||||||
solves:
|
|
||||||
proxy: "Public access"
|
|
||||||
default-options:
|
|
||||||
target: !var-expand ${MASTER_BASE_SERVICE_NAME}:4000
|
|
||||||
|
|
||||||
backup:
|
|
||||||
constraint: recommended
|
|
||||||
auto: pair
|
|
||||||
solves:
|
|
||||||
backup: "Automatic regular backup"
|
|
||||||
default-options:
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
carbone-test:
|
|
||||||
charm: carbone
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
# -*- ispell-local-dictionary: "english" -*-
|
|
||||||
|
|
||||||
* Info
|
|
||||||
|
|
||||||
This charm is provided to work with plausible charm
|
|
||||||
|
|
||||||
* Usage
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
## Init is run on host
|
|
||||||
## For now it is run every time the script is launched, but
|
|
||||||
## it should be launched only once after build.
|
|
||||||
|
|
||||||
## Accessible variables are:
|
|
||||||
## - SERVICE_NAME Name of current service
|
|
||||||
## - DOCKER_BASE_IMAGE Base image from which this service might be built if any
|
|
||||||
## - SERVICE_DATASTORE Location on host of the DATASTORE of this service
|
|
||||||
## - SERVICE_CONFIGSTORE Location on host of the CONFIGSTORE of this service
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
init-config-add "
|
|
||||||
$SERVICE_NAME:
|
|
||||||
environment:
|
|
||||||
CLICKHOUSE_SKIP_USER_SETUP: 1
|
|
||||||
healthcheck:
|
|
||||||
test: [ \"CMD-SHELL\", \"wget --no-verbose --tries=1 -O - http://127.0.0.1:8123/ping || exit 1\" ]
|
|
||||||
"
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
docker-image: docker.0k.io/clickhouse:24.12-alpine
|
|
||||||
#docker-image: clickhouse/clickhouse-server:24.12-alpine
|
|
||||||
|
|
||||||
data-resources:
|
|
||||||
- /var/lib/clickhouse
|
|
||||||
- /var/log/clickhouse-server
|
|
||||||
|
|
||||||
charm-resources:
|
|
||||||
- /etc/clickhouse-server/config.d/logs.xml
|
|
||||||
- /etc/clickhouse-server/config.d/ipv4-only.xml
|
|
||||||
- /etc/clickhouse-server/config.d/low-resources.xml
|
|
||||||
|
|
||||||
provides:
|
|
||||||
event-db:
|
|
||||||
|
|
||||||
uses:
|
|
||||||
log-rotate:
|
|
||||||
constraint: recommended
|
|
||||||
auto: pair
|
|
||||||
solves:
|
|
||||||
disk-leak: "/var/log/clickhouse-server"
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
<clickhouse>
|
|
||||||
<listen_host>0.0.0.0</listen_host>
|
|
||||||
</clickhouse>
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
<clickhouse>
|
|
||||||
<logger>
|
|
||||||
<level>warning</level>
|
|
||||||
<console>true</console>
|
|
||||||
</logger>
|
|
||||||
|
|
||||||
<query_log replace="1">
|
|
||||||
<database>system</database>
|
|
||||||
<table>query_log</table>
|
|
||||||
<flush_interval_milliseconds>7500</flush_interval_milliseconds>
|
|
||||||
<engine>
|
|
||||||
ENGINE = MergeTree
|
|
||||||
PARTITION BY event_date
|
|
||||||
ORDER BY (event_time)
|
|
||||||
TTL event_date + interval 30 day
|
|
||||||
SETTINGS ttl_only_drop_parts=1
|
|
||||||
</engine>
|
|
||||||
</query_log>
|
|
||||||
|
|
||||||
<!-- Stops unnecessary logging -->
|
|
||||||
<metric_log remove="remove" />
|
|
||||||
<asynchronous_metric_log remove="remove" />
|
|
||||||
<query_thread_log remove="remove" />
|
|
||||||
<text_log remove="remove" />
|
|
||||||
<trace_log remove="remove" />
|
|
||||||
<session_log remove="remove" />
|
|
||||||
<part_log remove="remove" />
|
|
||||||
</clickhouse>
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
<!-- https://clickhouse.com/docs/en/operations/tips#using-less-than-16gb-of-ram -->
|
|
||||||
<clickhouse>
|
|
||||||
<!--
|
|
||||||
https://clickhouse.com/docs/en/operations/server-configuration-parameters/settings#mark_cache_size -->
|
|
||||||
<mark_cache_size>524288000</mark_cache_size>
|
|
||||||
|
|
||||||
<profile>
|
|
||||||
<default>
|
|
||||||
<!-- https://clickhouse.com/docs/en/operations/settings/settings#max_threads -->
|
|
||||||
<max_threads>1</max_threads>
|
|
||||||
<!-- https://clickhouse.com/docs/en/operations/settings/settings#max_block_size -->
|
|
||||||
<max_block_size>8192</max_block_size>
|
|
||||||
<!-- https://clickhouse.com/docs/en/operations/settings/settings#max_download_threads -->
|
|
||||||
<max_download_threads>1</max_download_threads>
|
|
||||||
<!--
|
|
||||||
https://clickhouse.com/docs/en/operations/settings/settings#input_format_parallel_parsing -->
|
|
||||||
<input_format_parallel_parsing>0</input_format_parallel_parsing>
|
|
||||||
<!--
|
|
||||||
https://clickhouse.com/docs/en/operations/settings/settings#output_format_parallel_formatting -->
|
|
||||||
<output_format_parallel_formatting>0</output_format_parallel_formatting>
|
|
||||||
</default>
|
|
||||||
</profile>
|
|
||||||
</clickhouse>
|
|
||||||
32
keycloak-elabore/README.rst
Normal file
32
keycloak-elabore/README.rst
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
Description
|
||||||
|
===========
|
||||||
|
|
||||||
|
Using ``keycloak`` version 24.0
|
||||||
|
DEV info : https://www.keycloak.org/server/containers
|
||||||
|
|
||||||
|
Usage
|
||||||
|
=====
|
||||||
|
|
||||||
|
To start with ``keycloak``, just put this service in your
|
||||||
|
``compose.yml``::
|
||||||
|
|
||||||
|
keycloak:
|
||||||
|
options:
|
||||||
|
admin-password: CHANGEME
|
||||||
|
relations:
|
||||||
|
web-proxy:
|
||||||
|
frontend:
|
||||||
|
domain: id.mydomain.fr
|
||||||
|
|
||||||
|
Customize theme
|
||||||
|
===============
|
||||||
|
|
||||||
|
You can customize theme by putting your theme in
|
||||||
|
``/srv/datastore/data/keycloak/opt/keycloak/themes``
|
||||||
|
|
||||||
|
For example copy the material folder from
|
||||||
|
https://github.com/MAXIMUS-DeltaWare/material-keycloak-theme and
|
||||||
|
restart ``keycloak``.
|
||||||
|
|
||||||
|
Then go to your admin console, log in and go to the realm/themes part
|
||||||
|
to choose you new theme
|
||||||
15
keycloak-elabore/build/Dockerfile
Normal file
15
keycloak-elabore/build/Dockerfile
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
#FROM keycloak/keycloak:24.0.4 as builder
|
||||||
|
#
|
||||||
|
#ENV KC_METRICS_ENABLED=true
|
||||||
|
#ENV KC_FEATURES=token-exchange
|
||||||
|
#ENV KC_DB=postgres
|
||||||
|
#
|
||||||
|
#WORKDIR /opt/keycloak
|
||||||
|
## for demonstration purposes only, please make sure to use proper certificates in production instead
|
||||||
|
#RUN /opt/keycloak/bin/kc.sh build
|
||||||
|
|
||||||
|
FROM keycloak/keycloak:24.0.4
|
||||||
|
#COPY --from=builder /opt/keycloak/ /opt/keycloak/
|
||||||
|
WORKDIR /opt/keycloak
|
||||||
|
ENV KC_LOG_LEVEL=INFO
|
||||||
|
ENTRYPOINT ["/opt/keycloak/bin/kc.sh", "start", "--optimized"]
|
||||||
16
keycloak-elabore/build/Dockerfiledebug
Normal file
16
keycloak-elabore/build/Dockerfiledebug
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
FROM docker.0k.io/keycloak:17.0.1 as builder
|
||||||
|
|
||||||
|
ENV KC_METRICS_ENABLED=true
|
||||||
|
ENV KC_FEATURES=token-exchange
|
||||||
|
ENV KC_DB=postgres
|
||||||
|
RUN /opt/keycloak/bin/kc.sh build
|
||||||
|
|
||||||
|
FROM builder as inspector
|
||||||
|
ENTRYPOINT ["ls", "-l", "/opt/keycloak/lib/"]
|
||||||
|
|
||||||
|
|
||||||
|
#FROM docker.0k.io/keycloak:17.0.0
|
||||||
|
#COPY --from=builder /opt/keycloak/lib/quarkus/ /opt/keycloak/lib/quarkus/
|
||||||
|
#WORKDIR /opt/keycloak
|
||||||
|
#ENV KC_LOG_LEVEL=INFO
|
||||||
|
#ENTRYPOINT ["/opt/keycloak/bin/kc.sh", "start"]
|
||||||
12
keycloak-elabore/hooks/init
Executable file
12
keycloak-elabore/hooks/init
Executable file
@@ -0,0 +1,12 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
admin_password=$(options-get admin-password) || exit 1
|
||||||
|
|
||||||
|
init-config-add "\
|
||||||
|
$MASTER_BASE_SERVICE_NAME:
|
||||||
|
environment:
|
||||||
|
KEYCLOAK_ADMIN: \"admin\"
|
||||||
|
KEYCLOAK_ADMIN_PASSWORD: \"$admin_password\"
|
||||||
|
"
|
||||||
@@ -6,12 +6,12 @@ PASSWORD="$(relation-get password)"
|
|||||||
USER="$(relation-get user)"
|
USER="$(relation-get user)"
|
||||||
DBNAME="$(relation-get dbname)"
|
DBNAME="$(relation-get dbname)"
|
||||||
|
|
||||||
|
|
||||||
config-add "\
|
config-add "\
|
||||||
services:
|
services:
|
||||||
$MASTER_BASE_SERVICE_NAME:
|
$MASTER_BASE_SERVICE_NAME:
|
||||||
environment:
|
environment:
|
||||||
DATABASE_URL: postgres://$USER:$PASSWORD@$TARGET_SERVICE_NAME:5432/$DBNAME
|
KC_DB_URL: \"jdbc:postgresql://$MASTER_TARGET_SERVICE_NAME:5432/$DBNAME\"
|
||||||
|
KC_DB_USERNAME: \"$USER\"
|
||||||
|
KC_DB_PASSWORD: \"$PASSWORD\"
|
||||||
|
KC_DB: \"postgres\"
|
||||||
"
|
"
|
||||||
|
|
||||||
info "Configured $SERVICE_NAME code for $TARGET_SERVICE_NAME access."
|
|
||||||
21
keycloak-elabore/hooks/web_proxy-relation-joined
Executable file
21
keycloak-elabore/hooks/web_proxy-relation-joined
Executable file
@@ -0,0 +1,21 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
. lib/common
|
||||||
|
|
||||||
|
DOMAIN=$(relation-get domain) || exit 1
|
||||||
|
#IP_HOST=$(hostname -I | awk '{print $1}')
|
||||||
|
|
||||||
|
set -e
|
||||||
|
keycloak:generate-key-if-not-exist "$DOMAIN"
|
||||||
|
|
||||||
|
|
||||||
|
config-add "\
|
||||||
|
services:
|
||||||
|
$MASTER_BASE_SERVICE_NAME:
|
||||||
|
environment:
|
||||||
|
KC_HOSTNAME: \"$DOMAIN\"
|
||||||
|
KC_PROXY: edge
|
||||||
|
KC_HTTP_ENABLED: \"true\"
|
||||||
|
KC_HOSTNAME_STRICT: \"false\"
|
||||||
|
"
|
||||||
|
|
||||||
46
keycloak-elabore/lib/common
Normal file
46
keycloak-elabore/lib/common
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
# -*- mode: bash -*-
|
||||||
|
|
||||||
|
KEYCLOAK_DIR=/opt/keycloak
|
||||||
|
DATASTORE_KEYCLOAK_DIR="$SERVICE_DATASTORE$KEYCLOAK_DIR"
|
||||||
|
HOST_DATASTORE_KEYCLOAK_DIR="$HOST_DATASTORE/$SERVICE_NAME$KEYCLOAK_DIR"
|
||||||
|
|
||||||
|
keycloak:generate-key-if-not-exist() {
|
||||||
|
local domain="$1" ip_host
|
||||||
|
|
||||||
|
[ -d "$DATASTORE_KEYCLOAK_DIR" ] && return 0
|
||||||
|
|
||||||
|
ip_host=$(set -o pipefail; getent ahostsv4 "$domain" | head -n 1 | cut -f 1 -d " ") || {
|
||||||
|
err "Couldn't resolve to ipv4 domain name '$domain'."
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
info "Resolved successfully '$domain' to ip '$ip_host'."
|
||||||
|
debug "DOCKER_BASE_IMAGE: $DOCKER_BASE_IMAGE"
|
||||||
|
debug "HOST_DATASTORE_KEYCLOAK_DIR:: $HOST_DATASTORE_KEYCLOAK_DIR"
|
||||||
|
mkdir -p "$DATASTORE_KEYCLOAK_DIR/conf" || return 0
|
||||||
|
docker_image_export_dir "$DOCKER_BASE_IMAGE" "/opt/keycloak" "$SERVICE_DATASTORE/opt" || return 1
|
||||||
|
uid=$(docker_get_uid "$SERVICE_NAME" "keycloak") || return 1
|
||||||
|
chown "$uid" "$DATASTORE_KEYCLOAK_DIR" -R
|
||||||
|
debug "DATASTORE_KEYCLOAK_DIR_LS:: $(ls $DATASTORE_KEYCLOAK_DIR)"
|
||||||
|
docker run -w /opt/keycloak \
|
||||||
|
-v "$HOST_DATASTORE_KEYCLOAK_DIR":"/opt/keycloak" \
|
||||||
|
--entrypoint bash \
|
||||||
|
"$DOCKER_BASE_IMAGE" -c "
|
||||||
|
export KC_METRICS_ENABLED=true
|
||||||
|
export KC_FEATURES=token-exchange
|
||||||
|
export KC_DB=postgres
|
||||||
|
keytool -genkeypair -storepass password \
|
||||||
|
-storetype PKCS12 -keyalg RSA \
|
||||||
|
-keysize 2048 -dname 'CN=$domain' \
|
||||||
|
-alias server -ext 'SAN:c=DNS:$domain,IP:$ip_host' \
|
||||||
|
-keystore conf/server.keystore || exit 1
|
||||||
|
echo 'Generated key'
|
||||||
|
/opt/keycloak/bin/kc.sh build
|
||||||
|
|
||||||
|
" || {
|
||||||
|
|
||||||
|
rmdir "$DATASTORE_KEYCLOAK_DIR/conf" 2>/dev/null
|
||||||
|
rmdir "$DATASTORE_KEYCLOAK_DIR" 2>/dev/null
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
24
keycloak-elabore/metadata.yml
Normal file
24
keycloak-elabore/metadata.yml
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
|
||||||
|
data-resources:
|
||||||
|
- /opt/keycloak
|
||||||
|
|
||||||
|
default-options:
|
||||||
|
|
||||||
|
uses:
|
||||||
|
web-proxy:
|
||||||
|
#constraint: required | recommended | optional
|
||||||
|
#auto: pair | summon | none ## default: pair
|
||||||
|
constraint: required
|
||||||
|
auto: pair
|
||||||
|
solves:
|
||||||
|
proxy: "Public access"
|
||||||
|
default-options:
|
||||||
|
target: !var-expand ${MASTER_BASE_SERVICE_NAME}:8080
|
||||||
|
postgres-database:
|
||||||
|
#constraint: required | recommended | optional
|
||||||
|
#auto: pair | summon | none ## default: pair
|
||||||
|
constraint: required
|
||||||
|
auto: summon
|
||||||
|
solves:
|
||||||
|
database: "main storage"
|
||||||
|
default-options:
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
DOMAIN=$(relation-get domain) || {
|
|
||||||
echo "Failed to get domain"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
config-add "\
|
|
||||||
services:
|
|
||||||
$MASTER_BASE_SERVICE_NAME:
|
|
||||||
environment:
|
|
||||||
N8N_HOST: \"${DOMAIN}\"
|
|
||||||
WEBHOOK_URL: \"https:\/\/${DOMAIN}\"
|
|
||||||
"
|
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
docker-image: docker.0k.io/n8n:1.45.1
|
docker-image: docker.n8n.io/n8nio/n8n:1.23.0
|
||||||
|
|
||||||
uses:
|
uses:
|
||||||
postgres-database:
|
postgres-database:
|
||||||
@@ -22,15 +22,6 @@ uses:
|
|||||||
proxy: "Public access"
|
proxy: "Public access"
|
||||||
default-options:
|
default-options:
|
||||||
target: !var-expand ${MASTER_BASE_SERVICE_NAME}:5678
|
target: !var-expand ${MASTER_BASE_SERVICE_NAME}:5678
|
||||||
apache-custom-rules:
|
|
||||||
- !var-expand |
|
|
||||||
|
|
||||||
## Use RewriteEngine to handle WebSocket connection upgrades
|
|
||||||
RewriteEngine On
|
|
||||||
RewriteCond %{HTTP:Upgrade} ^websocket$ [NC]
|
|
||||||
RewriteCond %{HTTP:Connection} Upgrade [NC]
|
|
||||||
RewriteRule /(.*)\$ ws://${MASTER_BASE_SERVICE_NAME}:5678/\$1 [P,L]
|
|
||||||
|
|
||||||
backup:
|
backup:
|
||||||
constraint: recommended
|
constraint: recommended
|
||||||
auto: pair
|
auto: pair
|
||||||
|
|||||||
@@ -12,9 +12,8 @@ Config info: https://github.com/outline/outline/blob/main/.env.sample
|
|||||||
Odoo config: if you configure odoo OIDC connector, the callback url
|
Odoo config: if you configure odoo OIDC connector, the callback url
|
||||||
should be like this : https://<YOUR_OUTLINE>:443/auth/oidc.callback
|
should be like this : https://<YOUR_OUTLINE>:443/auth/oidc.callback
|
||||||
|
|
||||||
|
Requires a =smtp-server= provider to be functional, you can use
|
||||||
#Requires a =smtp-server= provider to be functional, you can use
|
=smtp-stub= charm to provide information to externally managed =SMTP=.
|
||||||
#=smtp-stub= charm to provide information to externally managed =SMTP=.
|
|
||||||
|
|
||||||
#+begin_src yaml
|
#+begin_src yaml
|
||||||
outline:
|
outline:
|
||||||
@@ -27,58 +26,16 @@ outline:
|
|||||||
oidc-user-info-uri: #the user info uri of your OIDC provider
|
oidc-user-info-uri: #the user info uri of your OIDC provider
|
||||||
oidc-logout-uri: #the login uri of your OIDC provider
|
oidc-logout-uri: #the login uri of your OIDC provider
|
||||||
|
|
||||||
#smtp-stub:
|
smtp-stub:
|
||||||
# options:
|
options:
|
||||||
# host: smtp.myhost.com
|
host: smtp.myhost.com
|
||||||
# port: 465
|
port: 465
|
||||||
# connection-security: "ssl/tls"
|
connection-security: "ssl/tls"
|
||||||
# auth-method: password #IMPORTANT: if not present login password doesn’t work
|
auth-method: password #IMPORTANT: if not present login password doesn’t work
|
||||||
# login: myuser
|
login: myuser
|
||||||
# password: myp4ssw0rd
|
password: myp4ssw0rd
|
||||||
|
|
||||||
#+end_src
|
#+end_src
|
||||||
|
|
||||||
** Odoo 14
|
|
||||||
|
|
||||||
We monkey-patch odoo in order to make it work, be sure to use latest version in 14.0 of galicea openIDConnection module
|
|
||||||
|
|
||||||
* Database ownership alignment
|
|
||||||
|
|
||||||
The =pre_deploy= hook ensures that every object of the database
|
|
||||||
(tables, sequences, views, materialized views, standalone types,
|
|
||||||
functions, procedures) is owned by the application role before the
|
|
||||||
container starts and runs its migrations.
|
|
||||||
|
|
||||||
Historical provisioning or restores executed as the =postgres=
|
|
||||||
superuser leave objects owned by =postgres=, which makes any later
|
|
||||||
=ALTER= on these objects fail with "must be owner of ..." and puts
|
|
||||||
outline in a crash-loop at migration time. This was seen on
|
|
||||||
2026-09-11 when upgrading elabore.coop from 1.6.1 to 1.10.0:
|
|
||||||
migration =20260714000000-add-mcp-to-search-queries-source.js=
|
|
||||||
failed on =enum_search_queries_source=. The same drift was found
|
|
||||||
on every managed server (lokavaluto.fr, lagemme.org, moneko.org).
|
|
||||||
|
|
||||||
Extensions are excluded from the realignment (they are managed by
|
|
||||||
the =postgres= charm). The hook is idempotent and silent when
|
|
||||||
there is no drift, and blocks the deployment (=exit 1=) if the
|
|
||||||
realignment fails, so the problem is visible at deploy time instead
|
|
||||||
of as a cryptic crash-loop.
|
|
||||||
|
|
||||||
* Datastore ownership alignment
|
|
||||||
|
|
||||||
The =init= hook aligns the ownership of the service datastore with
|
|
||||||
the user the Outline container runs as. Since 1.10.0 the image runs
|
|
||||||
as the unprivileged =nodejs= user (older images, up to 1.6.1, ran as
|
|
||||||
=root=), while the datastore is provisioned by =root=. Without
|
|
||||||
realignment the application cannot write its =uploads=, =public= and
|
|
||||||
=avatars= buckets and every attachment upload fails with "Permission
|
|
||||||
denied writing to ... Check the host machine file system
|
|
||||||
permissions". This was seen on 2026-09-11 on elabore.coop after the
|
|
||||||
1.6.1 to 1.10.0 upgrade, on every existing datastore.
|
|
||||||
|
|
||||||
The hook reads the user from the image's =Config.User=, so it stays
|
|
||||||
version-agnostic: images running as =root= are left untouched, and
|
|
||||||
re-running the hook on an already aligned datastore is a no-op.
|
|
||||||
|
|
||||||
* Building a new image
|
* Building a new image
|
||||||
|
|
||||||
@@ -88,9 +45,33 @@ We use the official image with an added patch due to 2 bugs:
|
|||||||
|
|
||||||
Note that a PR was pushed with a fix on the first bug. But this was not yet tested.
|
Note that a PR was pushed with a fix on the first bug. But this was not yet tested.
|
||||||
|
|
||||||
The fix are on 1.6.1
|
These fix are on 0.76.0
|
||||||
|
|
||||||
** Fix
|
** First fix
|
||||||
|
|
||||||
|
We need to add "url.port = '';" in ~build/server/middlewares/passport.js~ to remove the port. Note that this is a bad fix but works for our setup.
|
||||||
|
|
||||||
|
#+begin_src bash
|
||||||
|
IMAGE=docker.0k.io/outline:0.76.0-elabore
|
||||||
|
|
||||||
|
echo 'apk add patch bash' | dupd -u "$IMAGE" -- -u 1
|
||||||
|
cat <<'EOF1' | dupd -u "$IMAGE" -- -u 0
|
||||||
|
patch -p 1 <<'EOF2'
|
||||||
|
--- a/build/server/middlewares/passport.js
|
||||||
|
+++ b/build/server/middlewares/passport.js
|
||||||
|
@@ -40,6 +40,7 @@
|
||||||
|
const requestHost = ctx.get("host");
|
||||||
|
const url = new URL("".concat(reqProtocol, "://").concat(requestHost).concat(redirectUrl));
|
||||||
|
url.host = host;
|
||||||
|
+ url.port = '';
|
||||||
|
return ctx.redirect("".concat(url.toString()).concat(hasQueryString ? "&" : "?", "notice=").concat(notice));
|
||||||
|
}
|
||||||
|
if (_env.default.isDevelopment) {
|
||||||
|
EOF2
|
||||||
|
EOF1
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
** Second fix
|
||||||
|
|
||||||
Upon calling "/oidc" url, outline will return "Set-Cookie" header
|
Upon calling "/oidc" url, outline will return "Set-Cookie" header
|
||||||
with a "domain:" value that is incorrect (still the inner docker
|
with a "domain:" value that is incorrect (still the inner docker
|
||||||
@@ -103,14 +84,13 @@ The patches will change the "build/" files, so this is a very temporary and brit
|
|||||||
|
|
||||||
|
|
||||||
#+begin_src bash
|
#+begin_src bash
|
||||||
IMAGE=docker.0k.io/outline:1.6.1-elabore
|
IMAGE=docker.0k.io/outline:0.76.0-elabore
|
||||||
|
|
||||||
echo 'apt update && apt install patch' | dupd -u "$IMAGE" -- -u 0
|
|
||||||
cat <<'EOF1' | dupd -u "$IMAGE" -- -u 0
|
cat <<'EOF1' | dupd -u "$IMAGE" -- -u 0
|
||||||
patch -p 1 <<'EOF2'
|
patch -p 1 <<'EOF2'
|
||||||
--- a/build/server/utils/passport.js.orig
|
--- a/build/server/utils/passport.js.orig
|
||||||
+++ b/build/server/utils/passport.js
|
+++ b/build/server/utils/passport.js
|
||||||
@@ -56,7 +56,7 @@
|
@@ -37,7 +37,7 @@
|
||||||
const state = buildState(host, token, client);
|
const state = buildState(host, token, client);
|
||||||
ctx.cookies.set(this.key, state, {
|
ctx.cookies.set(this.key, state, {
|
||||||
expires: (0, _dateFns.addMinutes)(new Date(), 10),
|
expires: (0, _dateFns.addMinutes)(new Date(), 10),
|
||||||
@@ -119,7 +99,7 @@ patch -p 1 <<'EOF2'
|
|||||||
});
|
});
|
||||||
callback(null, token);
|
callback(null, token);
|
||||||
});
|
});
|
||||||
@@ -73,7 +73,7 @@
|
@@ -53,7 +53,7 @@
|
||||||
// Destroy the one-time pad token and ensure it matches
|
// Destroy the one-time pad token and ensure it matches
|
||||||
ctx.cookies.set(this.key, "", {
|
ctx.cookies.set(this.key, "", {
|
||||||
expires: (0, _dateFns.subMinutes)(new Date(), 1),
|
expires: (0, _dateFns.subMinutes)(new Date(), 1),
|
||||||
|
|||||||
@@ -47,7 +47,6 @@ oidc_logout_uri=$(options-get oidc-logout-uri) || exit 1
|
|||||||
|
|
||||||
init-config-add "
|
init-config-add "
|
||||||
$SERVICE_NAME:
|
$SERVICE_NAME:
|
||||||
restart: unless-stopped
|
|
||||||
volumes:
|
volumes:
|
||||||
- $SERVICE_DATASTORE:/var/lib/outline/data
|
- $SERVICE_DATASTORE:/var/lib/outline/data
|
||||||
environment:
|
environment:
|
||||||
@@ -63,50 +62,12 @@ $SERVICE_NAME:
|
|||||||
OIDC_LOGOUT_URI: \"$oidc_logout_uri\"
|
OIDC_LOGOUT_URI: \"$oidc_logout_uri\"
|
||||||
OIDC_SCOPES: \"openid\"
|
OIDC_SCOPES: \"openid\"
|
||||||
OIDC_USERNAME_CLAIM: \"preferred_username\"
|
OIDC_USERNAME_CLAIM: \"preferred_username\"
|
||||||
|
OIDC_DISPLAY_NAME: \"OpenID Connect\"
|
||||||
NODE_ENV: \"production\"
|
NODE_ENV: \"production\"
|
||||||
LOG_LEVEL: \"debug\"
|
LOG_LEVEL: \"debug\"
|
||||||
FORCE_HTTPS: \"false\"
|
FORCE_HTTPS: \"false\"
|
||||||
FILE_STORAGE: \"local\"
|
|
||||||
#DEVELOPMENT_UNSAFE_INLINE_CSP: \"true\"
|
#DEVELOPMENT_UNSAFE_INLINE_CSP: \"true\"
|
||||||
#DEBUG: \"http\"
|
DEBUG: \"http\"
|
||||||
"
|
"
|
||||||
|
|
||||||
## The datastore is bind-mounted into the container. Outline runs as
|
|
||||||
## an unprivileged user (image Config.User: root up to 1.6.1, "nodejs"
|
|
||||||
## since 1.10.0) and must write its uploads, public and avatars
|
|
||||||
## buckets. Provisioned by root, the datastore is not writable by
|
|
||||||
## that user and every upload fails with "Permission denied writing
|
|
||||||
## to ... Check the host machine file system permissions". Align the
|
|
||||||
## datastore ownership with the image user; skip images running as
|
|
||||||
## root. See README.org, "Datastore ownership alignment".
|
|
||||||
app_user=
|
|
||||||
if [ -n "$DOCKER_BASE_IMAGE" ]; then
|
|
||||||
app_user=$(docker image inspect "$DOCKER_BASE_IMAGE" \
|
|
||||||
--format '{{.Config.User}}') || exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
case "$app_user" in
|
|
||||||
""|0|0:0|root)
|
|
||||||
## image runs as root: nothing to align
|
|
||||||
;;
|
|
||||||
*:*)
|
|
||||||
uid="${app_user%%:*}"
|
|
||||||
gid="${app_user#*:}"
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
uid_gid=($(docker_get_uid_gid "$SERVICE_NAME" "$app_user" "$app_user")) || exit 1
|
|
||||||
uid="${uid_gid[0]}"
|
|
||||||
gid="${uid_gid[1]}"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
if [ -n "${uid:-}" ]; then
|
|
||||||
mkdir -p "$SERVICE_DATASTORE"
|
|
||||||
chown -R "$uid:$gid" "$SERVICE_DATASTORE" || {
|
|
||||||
err "Failed to align datastore ownership on '$uid:$gid'."
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
info "Datastore ownership aligned on '$uid:$gid'."
|
|
||||||
fi
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,150 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
## Should be executable N time in a row with same result.
|
|
||||||
##
|
|
||||||
## Ensure the outline application role owns every object of its
|
|
||||||
## database before the container boots and runs its migrations.
|
|
||||||
##
|
|
||||||
## Historical provisioning or restores executed as the "postgres"
|
|
||||||
## superuser leave objects owned by "postgres", which makes any
|
|
||||||
## later ALTER on these objects fail with "must be owner of ..."
|
|
||||||
## and puts outline in a crash-loop at migration time. See
|
|
||||||
## README.org, section "Database ownership alignment".
|
|
||||||
|
|
||||||
. lib/common
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
relation="postgres-database"
|
|
||||||
|
|
||||||
db_role=$(outline:named-relation-get "$relation" user) || {
|
|
||||||
err "Couldn't get ${WHITE}user${NORMAL} value" \
|
|
||||||
"in ${DARKCYAN}$relation${NORMAL} relation's data."
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
## List every object of schema "public" not owned by the application
|
|
||||||
## role: tables, sequences, views, materialized views, standalone
|
|
||||||
## types and functions. Extensions are excluded (managed by the
|
|
||||||
## postgres charm).
|
|
||||||
audit_query="SET app.role = '$db_role';
|
|
||||||
SELECT obj FROM (
|
|
||||||
SELECT CASE c.relkind
|
|
||||||
WHEN 'S' THEN 'sequence '
|
|
||||||
WHEN 'v' THEN 'view '
|
|
||||||
WHEN 'm' THEN 'matview '
|
|
||||||
ELSE 'table '
|
|
||||||
END || c.relname AS obj
|
|
||||||
FROM pg_class c
|
|
||||||
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
||||||
WHERE n.nspname = 'public'
|
|
||||||
AND c.relkind IN ('r','p','S','v','m')
|
|
||||||
AND pg_get_userbyid(c.relowner) <> current_setting('app.role')
|
|
||||||
UNION ALL
|
|
||||||
SELECT 'type ' || t.typname AS obj
|
|
||||||
FROM pg_type t
|
|
||||||
JOIN pg_namespace n ON n.oid = t.typnamespace
|
|
||||||
WHERE n.nspname = 'public'
|
|
||||||
AND t.typtype IN ('e','d','c')
|
|
||||||
AND NOT EXISTS (SELECT 1 FROM pg_class c WHERE c.reltype = t.oid)
|
|
||||||
AND pg_get_userbyid(t.typowner) <> current_setting('app.role')
|
|
||||||
UNION ALL
|
|
||||||
SELECT CASE p.prokind
|
|
||||||
WHEN 'p' THEN 'procedure '
|
|
||||||
ELSE 'function '
|
|
||||||
END || p.proname AS obj
|
|
||||||
FROM pg_proc p
|
|
||||||
JOIN pg_namespace n ON n.oid = p.pronamespace
|
|
||||||
WHERE n.nspname = 'public'
|
|
||||||
AND p.prokind IN ('f','p')
|
|
||||||
AND pg_get_userbyid(p.proowner) <> current_setting('app.role')
|
|
||||||
) drift
|
|
||||||
ORDER BY obj;"
|
|
||||||
|
|
||||||
drift=$(sql < <(e "$audit_query")) || {
|
|
||||||
err "Failed to audit database ownership for ${WHITE}$db_role${NORMAL}."
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
if [ -z "$drift" ]; then
|
|
||||||
## fast-path: nothing to do
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
info "Found database objects not owned by '${db_role}', reassigning ownership:"
|
|
||||||
e "$drift" | prefix " ${GRAY}|${NORMAL} " >&2
|
|
||||||
|
|
||||||
dbname=$(outline:named-relation-get "$relation" dbname) || {
|
|
||||||
err "Couldn't get ${WHITE}dbname${NORMAL} value" \
|
|
||||||
"in ${DARKCYAN}$relation${NORMAL} relation's data."
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
## Reassign ownership of every drifted object, then the database
|
|
||||||
## itself. Role name is passed through a session GUC and quoted
|
|
||||||
## with format('%I') in every generated statement.
|
|
||||||
repair_query="SET app.role = '$db_role';
|
|
||||||
DO \$\$
|
|
||||||
DECLARE
|
|
||||||
r record;
|
|
||||||
app_role text := current_setting('app.role');
|
|
||||||
BEGIN
|
|
||||||
FOR r IN
|
|
||||||
SELECT CASE c.relkind
|
|
||||||
WHEN 'S' THEN format('ALTER SEQUENCE public.%I OWNER TO %I', c.relname, app_role)
|
|
||||||
WHEN 'v' THEN format('ALTER VIEW public.%I OWNER TO %I', c.relname, app_role)
|
|
||||||
WHEN 'm' THEN format('ALTER MATERIALIZED VIEW public.%I OWNER TO %I', c.relname, app_role)
|
|
||||||
ELSE format('ALTER TABLE public.%I OWNER TO %I', c.relname, app_role)
|
|
||||||
END AS stmt
|
|
||||||
FROM pg_class c
|
|
||||||
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
||||||
WHERE n.nspname = 'public'
|
|
||||||
AND c.relkind IN ('r','p','S','v','m')
|
|
||||||
AND pg_get_userbyid(c.relowner) <> app_role
|
|
||||||
UNION ALL
|
|
||||||
SELECT format('ALTER TYPE public.%I OWNER TO %I', t.typname, app_role)
|
|
||||||
FROM pg_type t
|
|
||||||
JOIN pg_namespace n ON n.oid = t.typnamespace
|
|
||||||
WHERE n.nspname = 'public'
|
|
||||||
AND t.typtype IN ('e','d','c')
|
|
||||||
AND NOT EXISTS (SELECT 1 FROM pg_class c WHERE c.reltype = t.oid)
|
|
||||||
AND pg_get_userbyid(t.typowner) <> app_role
|
|
||||||
UNION ALL
|
|
||||||
SELECT CASE p.prokind
|
|
||||||
WHEN 'p' THEN format('ALTER PROCEDURE public.%I(%s) OWNER TO %I',
|
|
||||||
p.proname, pg_get_function_identity_arguments(p.oid), app_role)
|
|
||||||
ELSE format('ALTER FUNCTION public.%I(%s) OWNER TO %I',
|
|
||||||
p.proname, pg_get_function_identity_arguments(p.oid), app_role)
|
|
||||||
END AS stmt
|
|
||||||
FROM pg_proc p
|
|
||||||
JOIN pg_namespace n ON n.oid = p.pronamespace
|
|
||||||
WHERE n.nspname = 'public'
|
|
||||||
AND p.prokind IN ('f','p')
|
|
||||||
AND pg_get_userbyid(p.proowner) <> app_role
|
|
||||||
LOOP
|
|
||||||
EXECUTE r.stmt;
|
|
||||||
END LOOP;
|
|
||||||
END \$\$;
|
|
||||||
ALTER DATABASE \"$dbname\" OWNER TO \"$db_role\";"
|
|
||||||
|
|
||||||
sql < <(e "$repair_query") || {
|
|
||||||
err "Failed to reassign ownership to '${db_role}'."
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
## Fail-hard: verify the realignment actually worked.
|
|
||||||
remaining_drift=$(sql < <(e "$audit_query")) || {
|
|
||||||
err "Failed to re-audit database ownership for ${WHITE}$db_role${NORMAL}."
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
if [ -n "$remaining_drift" ]; then
|
|
||||||
err "Some database objects are still not owned by '${db_role}':"
|
|
||||||
e "$remaining_drift" | prefix " ${GRAY}|${NORMAL} " >&2
|
|
||||||
err "Deployment halted. Fix the ownership manually and retry, e.g.:"
|
|
||||||
err " docker exec <postgres-container> psql -U postgres -d \"$dbname\" \\" >&2
|
|
||||||
err " -c 'ALTER TYPE public.<name> OWNER TO \"$db_role\"'" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
info "Database ownership aligned on '${db_role}'."
|
|
||||||
@@ -13,9 +13,9 @@ services:
|
|||||||
$MASTER_BASE_SERVICE_NAME:
|
$MASTER_BASE_SERVICE_NAME:
|
||||||
environment:
|
environment:
|
||||||
SMTP_USERNAME: \"$user\"
|
SMTP_USERNAME: \"$user\"
|
||||||
SMTP_PASSWORD: \"${password//\$/\$\$}\"
|
SMTP_PASS: \"${password//\$/\$\$}\"
|
||||||
SMTP_HOST: \"$host\"
|
SMTP_HOST: \"$host\"
|
||||||
SMTP_PORT: \"$port\"
|
SMTP_PORT: \"$port\"
|
||||||
SMTP_FROM_EMAIL: \"$user\"
|
#SMTP_SECURE: \"false\"
|
||||||
"
|
"
|
||||||
|
|
||||||
|
|||||||
@@ -1,65 +0,0 @@
|
|||||||
# -*- mode: shell-script -*-
|
|
||||||
|
|
||||||
##
|
|
||||||
## Database access helpers (from cyclos/immich pattern in 0k-charms)
|
|
||||||
##
|
|
||||||
|
|
||||||
## Get target service name for a named relation
|
|
||||||
outline:relation-get-target-service() {
|
|
||||||
local relation="$1" ts
|
|
||||||
if ! read-0 ts _ _ < <(get_service_relation "$SERVICE_NAME" "$relation"); then
|
|
||||||
err "Couldn't find relation ${DARKCYAN}$relation${NORMAL}."
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
e "$ts"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
## Get the raw data of a named relation
|
|
||||||
outline:relation-get-config() {
|
|
||||||
local relation="$1" ts relation_dir
|
|
||||||
ts=$(outline:relation-get-target-service "$relation") || return 1
|
|
||||||
relation_dir=$(get_relation_data_dir "$SERVICE_NAME" "$ts" "$relation") || return 1
|
|
||||||
cat "${relation_dir}/data"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
## Get a key from the relation data
|
|
||||||
outline:named-relation-get() {
|
|
||||||
local relation="$1" key="$2" config
|
|
||||||
config=$(outline:relation-get-config "$relation") || return 1
|
|
||||||
e "$config" | shyaml get-value "$key" || {
|
|
||||||
err "Couldn't get ${WHITE}$key${NORMAL} value" \
|
|
||||||
"in ${DARKCYAN}$relation${NORMAL} relation's data."
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
## Run SQL as the postgres superuser on the database related to this
|
|
||||||
## service through the "postgres-database" relation.
|
|
||||||
## Usage: sql < <(echo "SELECT ...")
|
|
||||||
## echo "SELECT ..." | sql
|
|
||||||
sql() {
|
|
||||||
(
|
|
||||||
local dbname ts target_charm target_charm_path
|
|
||||||
dbname="$(outline:named-relation-get "postgres-database" dbname)" || exit 1
|
|
||||||
ts=$(outline:relation-get-target-service "postgres-database") || exit 1
|
|
||||||
|
|
||||||
export SERVICE_NAME="$ts"
|
|
||||||
export SERVICE_DATASTORE="$DATASTORE/$SERVICE_NAME"
|
|
||||||
DOCKER_BASE_IMAGE=$(service_ensure_image_ready "$SERVICE_NAME") || exit 1
|
|
||||||
export DOCKER_BASE_IMAGE
|
|
||||||
|
|
||||||
target_charm=$(get_service_charm "$ts") || exit 1
|
|
||||||
target_charm_path=$(charm.get_dir "$target_charm") || exit 1
|
|
||||||
|
|
||||||
set +e
|
|
||||||
. "$target_charm_path/lib/common"
|
|
||||||
set -e
|
|
||||||
|
|
||||||
ensure_db_docker_running
|
|
||||||
|
|
||||||
ddb -d "$dbname" -v ON_ERROR_STOP=1
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
docker-image: docker.0k.io/outline:0.83.0-elabore
|
docker-image: docker.0k.io/outline:0.76.0-elabore
|
||||||
|
|
||||||
uses:
|
uses:
|
||||||
postgres-database:
|
postgres-database:
|
||||||
@@ -11,8 +11,6 @@ uses:
|
|||||||
default-options:
|
default-options:
|
||||||
extensions:
|
extensions:
|
||||||
- uuid-ossp
|
- uuid-ossp
|
||||||
- unaccent
|
|
||||||
- pg_trgm
|
|
||||||
redis-database:
|
redis-database:
|
||||||
constraint: required
|
constraint: required
|
||||||
auto: summon
|
auto: summon
|
||||||
@@ -32,15 +30,6 @@ uses:
|
|||||||
proxy: "Public access"
|
proxy: "Public access"
|
||||||
default-options:
|
default-options:
|
||||||
target: !var-expand ${MASTER_BASE_SERVICE_NAME}:3000
|
target: !var-expand ${MASTER_BASE_SERVICE_NAME}:3000
|
||||||
apache-custom-rules:
|
|
||||||
- !var-expand |
|
|
||||||
|
|
||||||
## Use RewriteEngine to handle WebSocket connection upgrades
|
|
||||||
RewriteEngine On
|
|
||||||
RewriteCond %{HTTP:Connection} Upgrade [NC]
|
|
||||||
RewriteCond %{HTTP:Upgrade} websocket [NC]
|
|
||||||
RewriteRule /(.*)\$ ws://${MASTER_BASE_SERVICE_NAME}:3000/\$1 [P,L]
|
|
||||||
|
|
||||||
backup:
|
backup:
|
||||||
constraint: recommended
|
constraint: recommended
|
||||||
auto: pair
|
auto: pair
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
outline:
|
|
||||||
options:
|
|
||||||
sender-email: outline@example.com
|
|
||||||
oidc-client-id: test-client
|
|
||||||
oidc-client-secret: test-secret
|
|
||||||
oidc-auth-uri: https://example.com/auth
|
|
||||||
oidc-token-uri: https://example.com/token
|
|
||||||
oidc-user-info-uri: https://example.com/userinfo
|
|
||||||
oidc-logout-uri: https://example.com/logout
|
|
||||||
|
|
||||||
smtp-stub:
|
|
||||||
options:
|
|
||||||
host: smtp.example.com
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
# -*- ispell-local-dictionary: "english" -*-
|
|
||||||
|
|
||||||
* Info
|
|
||||||
|
|
||||||
From: https://github.com/plausible/community-edition/
|
|
||||||
|
|
||||||
* Usage
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
config-add "\
|
|
||||||
services:
|
|
||||||
$MASTER_BASE_SERVICE_NAME:
|
|
||||||
environment:
|
|
||||||
CLICKHOUSE_DATABASE_URL: http://$TARGET_SERVICE_NAME:8123/$TARGET_SERVICE_NAME
|
|
||||||
"
|
|
||||||
|
|
||||||
info "Configured $SERVICE_NAME code for $TARGET_SERVICE_NAME access."
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
SECRET_KEY_BASE="$SERVICE_DATASTORE"/secret-key
|
|
||||||
|
|
||||||
SHARE_DIR="$SERVICE_DATASTORE"/var/lib/plausible
|
|
||||||
mkdir -p $SHARE_DIR
|
|
||||||
|
|
||||||
uid=$(docker_get_uid "$SERVICE_NAME" "plausible")
|
|
||||||
|
|
||||||
if ! [ -f "$SECRET_KEY_BASE" ]; then
|
|
||||||
info "Generating secret key"
|
|
||||||
mkdir -p "${SECRET_KEY_BASE%/*}"
|
|
||||||
umask 077
|
|
||||||
openssl rand -base64 64 > "$SECRET_KEY_BASE"
|
|
||||||
else
|
|
||||||
info "Using existing secret key"
|
|
||||||
fi
|
|
||||||
|
|
||||||
secret_key_base=$(cat "$SECRET_KEY_BASE")
|
|
||||||
|
|
||||||
init-config-add "
|
|
||||||
$SERVICE_NAME:
|
|
||||||
environment:
|
|
||||||
SECRET_KEY_BASE: \"$secret_key_base\"
|
|
||||||
"
|
|
||||||
|
|
||||||
chown -v "$uid" "$SHARE_DIR"
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
host=$(relation-get host) || exit 1
|
|
||||||
port=$(relation-get port) || exit 1
|
|
||||||
user=$(relation-get login) || exit 1
|
|
||||||
password="$(relation-get password)" || exit 1
|
|
||||||
|
|
||||||
|
|
||||||
config-add "\
|
|
||||||
services:
|
|
||||||
$MASTER_BASE_SERVICE_NAME:
|
|
||||||
environment:
|
|
||||||
SMTP_USER_NAME: \"$user\"
|
|
||||||
SMTP_USER_PWD: \"${password//\$/\$\$}\"
|
|
||||||
SMTP_HOST_ADDR: \"$host\"
|
|
||||||
SMTP_HOST_PORT: \"$port\"
|
|
||||||
SMTP_HOST_SSL_ENABLE: \"true\"
|
|
||||||
MAILER_EMAIL: \"$user\"
|
|
||||||
"
|
|
||||||
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
DOMAIN=$(relation-get domain) || {
|
|
||||||
echo "Failed to get domain"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
config-add "\
|
|
||||||
services:
|
|
||||||
$MASTER_BASE_SERVICE_NAME:
|
|
||||||
environment:
|
|
||||||
BASE_URL: \"https:\/\/${DOMAIN}\"
|
|
||||||
"
|
|
||||||
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
docker-image: docker.0k.io/plausible:3.0.1
|
|
||||||
#docker-image: ghcr.io/plausible/community-edition:v3.0.1
|
|
||||||
|
|
||||||
data-resources:
|
|
||||||
- /var/lib/plausible
|
|
||||||
|
|
||||||
docker-compose:
|
|
||||||
entrypoint: sh -c "/entrypoint.sh db createdb && /entrypoint.sh db migrate && /entrypoint.sh run"
|
|
||||||
#entrypoint: sh -c "/entrypoint.sh run"
|
|
||||||
uses:
|
|
||||||
event-db:
|
|
||||||
#constraint: required | recommended | optional
|
|
||||||
#auto: pair | summon | none ## default: pair
|
|
||||||
constraint: required
|
|
||||||
auto: summon
|
|
||||||
solves:
|
|
||||||
database: "event db"
|
|
||||||
postgres-database:
|
|
||||||
#constraint: required | recommended | optional
|
|
||||||
#auto: pair | summon | none ## default: pair
|
|
||||||
constraint: required
|
|
||||||
auto: summon
|
|
||||||
solves:
|
|
||||||
database: "main storage"
|
|
||||||
default-options:
|
|
||||||
extensions:
|
|
||||||
- citext
|
|
||||||
smtp-server:
|
|
||||||
constraint: required
|
|
||||||
auto: summon
|
|
||||||
solves:
|
|
||||||
proxy: "Public access"
|
|
||||||
web-proxy:
|
|
||||||
#constraint: required | recommended | optional
|
|
||||||
#auto: pair | summon | none ## default: pair
|
|
||||||
constraint: recommended
|
|
||||||
auto: pair
|
|
||||||
solves:
|
|
||||||
proxy: "Public access"
|
|
||||||
default-options:
|
|
||||||
target: !var-expand ${MASTER_BASE_SERVICE_NAME}:8000
|
|
||||||
apache-custom-rules:
|
|
||||||
- !var-expand |
|
|
||||||
ProxyPreserveHost On
|
|
||||||
|
|
||||||
#Set web sockets
|
|
||||||
RewriteEngine On
|
|
||||||
RewriteCond %{HTTP:Upgrade} =websocket [NC]
|
|
||||||
RewriteCond %{HTTP:Connection} upgrade [NC]
|
|
||||||
RewriteRule ^/(live/websocket)$ ws://${MASTER_BASE_SERVICE_NAME}:8000/\$1 [P,L]
|
|
||||||
|
|
||||||
|
|
||||||
backup:
|
|
||||||
constraint: recommended
|
|
||||||
auto: pair
|
|
||||||
solves:
|
|
||||||
backup: "Automatic regular backup"
|
|
||||||
default-options:
|
|
||||||
Reference in New Issue
Block a user