karakeep nobuild

This commit is contained in:
Alexandre
2026-01-12 13:42:35 +01:00
committed by GitHub
parent 4489587326
commit 0f8a850b4a
57 changed files with 1041 additions and 507 deletions

View File

@@ -1,15 +0,0 @@
#!/command/with-contenv bashio
# shellcheck shell=bash
set -e
bashio::log.info "Creating folders"
mkdir -p \
/data/cache \
/data/chrome \
/config/meili \
/usr/src/chrome/extensions
if id chrome &>/dev/null; then
chown -R chrome:chrome /data/cache /data/chrome /usr/src/chrome/extensions
fi

View File

@@ -1,50 +0,0 @@
#!/usr/bin/with-contenv bashio
# shellcheck shell=bash
set -e
generate_secret() {
# Avoid SIGPIPE from `tr` when `head` terminates early under pipefail.
(
set +o pipefail 2>/dev/null || true
tr -dc 'A-Za-z0-9' < /dev/urandom | head -c 64
)
}
set_option() {
local key="$1"
local value="$2"
# Store permanently in Home Assistant add-on options
bashio::addon.option "${key}" "${value}"
# Export into current process
export "${key}=${value}"
# Export into s6 so all services inherit it
if [ -d /var/run/s6/container_environment ]; then
printf "%s" "${value}" > "/var/run/s6/container_environment/${key}"
fi
}
load_option() {
local key="$1"
local value
value="$(bashio::config "${key}")"
export "${key}=${value}"
if [ -d /var/run/s6/container_environment ]; then
printf "%s" "${value}" > "/var/run/s6/container_environment/${key}"
fi
}
for key in MEILI_MASTER_KEY NEXTAUTH_SECRET; do
if bashio::config.has_value "${key}"; then
bashio::log.info "Using existing ${key}"
load_option "${key}"
else
bashio::log.warning "${key} not set, generating persistent secret"
value="$(generate_secret)"
set_option "${key}" "${value}"
fi
done

View File

@@ -1,46 +0,0 @@
#!/command/with-contenv bashio
# shellcheck shell=bash
set -e
EXTENSIONS_DIR="${CHROME_EXTENSIONS_DIR:-/usr/src/chrome/extensions}"
bashio::log.info "Refreshing Chromium extensions in ${EXTENSIONS_DIR}"
mkdir -p "${EXTENSIONS_DIR}"
download_extension() {
local name="$1"
local extension_id="$2"
local crx_path rc
crx_path="$(mktemp)"
if ! curl -fsSL \
"https://clients2.google.com/service/update2/crx?response=redirect&prodversion=120.0&acceptformat=crx2,crx3&x=id%3D${extension_id}%26installsource%3Dondemand%26uc" \
-o "${crx_path}"; then
rm -f "${crx_path}"
bashio::log.warning "Failed to download extension ${name}. Continuing without refresh."
return 0
fi
rm -rf "${EXTENSIONS_DIR:?}/${name}"
mkdir -p "${EXTENSIONS_DIR}/${name}"
rc=0
unzip -q "${crx_path}" -d "${EXTENSIONS_DIR}/${name}" || rc=$?
rm -f "${crx_path}"
# unzip may return 1 even though files extracted (common with CRX zip metadata)
if [ "${rc}" -ne 0 ] && [ "${rc}" -ne 1 ]; then
bashio::log.warning "Failed to unzip extension ${name} (rc=${rc}). Continuing."
return 0
fi
return 0
}
download_extension "i-dont-care-about-cookies" "fllaojicojecljbmefodhfapmkghcbnh"
download_extension "ublock-origin" "cjpalhdlnbpafiamejdnhcphjbkeiagm"
if id chrome &>/dev/null; then
chown -R chrome:chrome "${EXTENSIONS_DIR}"
fi

View File

@@ -0,0 +1,31 @@
<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig>
<alias>
<family>sans-serif</family>
<prefer>
<family>Main sans-serif font name goes here</family>
<family>Noto Color Emoji</family>
<family>Noto Emoji</family>
</prefer>
</alias>
<alias>
<family>serif</family>
<prefer>
<family>Main serif font name goes here</family>
<family>Noto Color Emoji</family>
<family>Noto Emoji</family>
</prefer>
</alias>
<alias>
<family>monospace</family>
<prefer>
<family>Main monospace font name goes here</family>
<family>Noto Color Emoji</family>
<family>Noto Emoji</family>
</prefer>
</alias>
</fontconfig>

View File

@@ -0,0 +1,96 @@
types {
text/html html htm shtml;
text/css css;
text/xml xml;
image/gif gif;
image/jpeg jpeg jpg;
application/javascript js;
application/atom+xml atom;
application/rss+xml rss;
text/mathml mml;
text/plain txt;
text/vnd.sun.j2me.app-descriptor jad;
text/vnd.wap.wml wml;
text/x-component htc;
image/png png;
image/svg+xml svg svgz;
image/tiff tif tiff;
image/vnd.wap.wbmp wbmp;
image/webp webp;
image/x-icon ico;
image/x-jng jng;
image/x-ms-bmp bmp;
font/woff woff;
font/woff2 woff2;
application/java-archive jar war ear;
application/json json;
application/mac-binhex40 hqx;
application/msword doc;
application/pdf pdf;
application/postscript ps eps ai;
application/rtf rtf;
application/vnd.apple.mpegurl m3u8;
application/vnd.google-earth.kml+xml kml;
application/vnd.google-earth.kmz kmz;
application/vnd.ms-excel xls;
application/vnd.ms-fontobject eot;
application/vnd.ms-powerpoint ppt;
application/vnd.oasis.opendocument.graphics odg;
application/vnd.oasis.opendocument.presentation odp;
application/vnd.oasis.opendocument.spreadsheet ods;
application/vnd.oasis.opendocument.text odt;
application/vnd.openxmlformats-officedocument.presentationml.presentation
pptx;
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
xlsx;
application/vnd.openxmlformats-officedocument.wordprocessingml.document
docx;
application/vnd.wap.wmlc wmlc;
application/x-7z-compressed 7z;
application/x-cocoa cco;
application/x-java-archive-diff jardiff;
application/x-java-jnlp-file jnlp;
application/x-makeself run;
application/x-perl pl pm;
application/x-pilot prc pdb;
application/x-rar-compressed rar;
application/x-redhat-package-manager rpm;
application/x-sea sea;
application/x-shockwave-flash swf;
application/x-stuffit sit;
application/x-tcl tcl tk;
application/x-x509-ca-cert der pem crt;
application/x-xpinstall xpi;
application/xhtml+xml xhtml;
application/xspf+xml xspf;
application/zip zip;
application/octet-stream bin exe dll;
application/octet-stream deb;
application/octet-stream dmg;
application/octet-stream iso img;
application/octet-stream msi msp msm;
audio/midi mid midi kar;
audio/mpeg mp3;
audio/ogg ogg;
audio/x-m4a m4a;
audio/x-realaudio ra;
video/3gpp 3gpp 3gp;
video/mp2t ts;
video/mp4 mp4;
video/mpeg mpeg mpg;
video/quicktime mov;
video/webm webm;
video/x-flv flv;
video/x-m4v m4v;
video/x-mng mng;
video/x-ms-asf asx asf;
video/x-ms-wmv wmv;
video/x-msvideo avi;
}

View File

@@ -0,0 +1,15 @@
proxy_http_version 1.1;
proxy_ignore_client_abort off;
proxy_read_timeout 86400s;
proxy_redirect off;
proxy_send_timeout 86400s;
proxy_max_temp_file_size 0;
proxy_set_header Accept-Encoding "";
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $http_host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-NginX-Proxy true;
proxy_set_header X-Real-IP $remote_addr;

View File

@@ -0,0 +1 @@
resolver 127.0.0.11 ipv6=off;

View File

@@ -0,0 +1,6 @@
root /dev/null;
server_name $hostname;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
add_header X-Robots-Tag none;

View File

@@ -0,0 +1,8 @@
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_session_timeout 10m;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
#ssl_stapling on;
#ssl_stapling_verify on;

View File

@@ -0,0 +1,52 @@
# Run nginx in foreground.
daemon off;
# This is run inside Docker.
user root;
# Pid storage location.
pid /var/run/nginx.pid;
# Set number of worker processes.
worker_processes 1;
# Enables the use of JIT for regular expressions to speed-up their processing.
pcre_jit on;
# Write error log to the add-on log.
error_log /proc/1/fd/1 error;
# Load allowed environment vars
env HASSIO_TOKEN;
# Load dynamic modules.
include /etc/nginx/modules/*.conf;
# Max num of simultaneous connections by a worker process.
events {
worker_connections 512;
}
http {
include /etc/nginx/includes/mime.types;
access_log off;
client_max_body_size 4G;
default_type application/octet-stream;
gzip on;
keepalive_timeout 65;
sendfile on;
server_tokens off;
tcp_nodelay on;
tcp_nopush on;
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
include /etc/nginx/includes/resolver.conf;
include /etc/nginx/includes/upstream.conf;
include /etc/nginx/servers/*.conf;
}

View File

@@ -0,0 +1,31 @@
server {
{{ if not .ssl }}
listen {{ .port }} default_server;
{{ else }}
listen {{ .port }} default_server ssl;
{{ end }}
include /etc/nginx/includes/server_params.conf;
include /etc/nginx/includes/proxy_params.conf;
{{ if .ssl }}
include /etc/nginx/includes/ssl_params.conf;
ssl_certificate {{ .certfile }};
ssl_certificate_key {{ .keyfile }};
{{ end }}
location / {
{{ if .ingress_user }}
set $ingress_user "";
if ($remote_addr = 172.30.32.2) {
set $ingress_user {{ .ingress_user }};
}
proxy_set_header X-WebAuth-User $ingress_user;
{{ end }}
proxy_pass {{ .protocol }}://backend;
}
}

View File

@@ -0,0 +1,3 @@
upstream backend {
server 127.0.0.1:{{ .port }};
}

View File

@@ -0,0 +1,59 @@
#!/command/with-contenv bash
# shellcheck shell=bash
set -e
echo " Loading env variables:"
OPTIONS_SOURCE=/data/options.json
while read -r -d $'\0' key && read -r value; do
line="$key=$value"
# log redacted config
case "$key" in
*PASS*|*SECRET*|*KEY*|*TOKEN*)
echo " $key=******"
;;
*)
echo " $line"
;;
esac
export $key=$value
# set .env
echo "$line" >> /.env || true
# set /etc/environment
echo "$line" >> /etc/environment
# For s6
if [ -d /var/run/s6/container_environment ]; then
printf "%s" "$value" > /var/run/s6/container_environment/"$key"
fi
echo "export $line" >> ~/.bashrc
done < <(
jq -r '
reduce to_entries[] as $item (
{};
if $item.value | type == "object" then
. + $item.value
else
. + { ($item.key): $item.value }
end
)
| to_entries[]
| "\(.key)\u0000\(.value|tostring)"
' "$OPTIONS_SOURCE"
)
if [ -n "$TZ" ] && [ -f /etc/localtime ]; then
if [ -f /usr/share/zoneinfo/"$TZ" ]; then
echo "Timezone set to $TZ"
ln -snf /usr/share/zoneinfo/"$TZ" /etc/localtime
echo "$TZ" >/etc/timezone
fi
fi

View File

@@ -0,0 +1 @@
oneshot

View File

@@ -0,0 +1 @@
/etc/s6-overlay/s6-rc.d/init-addon-config/run

View File

@@ -0,0 +1,8 @@
#!/command/with-contenv bash
# shellcheck shell=bash
set -e
if [ ! -d "$DATA_DIR" ]; then
mkdir -p "$DATA_DIR"
fi

View File

@@ -0,0 +1 @@
oneshot

View File

@@ -0,0 +1 @@
/etc/s6-overlay/s6-rc.d/init-folders/run

View File

@@ -0,0 +1,49 @@
#!/command/with-contenv bashio
# shellcheck shell=bash
set -e
OPTIONS_SOURCE=/data/options.json
service_port=${SERVICE_PORT:-80}
service_protocol=${SERVICE_PROTOCOL:-http}
nginx_port=${NGINX_PORT:-8080}
certfile="/ssl/$(jq -r '.certfile' $OPTIONS_SOURCE)"
keyfile="/ssl/$(jq -r '.keyfile' $OPTIONS_SOURCE)"
ingress_user=$(jq -r '.ingress_user' $OPTIONS_SOURCE)
# Generate upstream configuration
printf '{ "port": "%s" }' "$service_port" \
| tempio \
-template /etc/nginx/templates/upstream.gtpl \
-out /etc/nginx/includes/upstream.conf
if jq -e '.ssl == true' $OPTIONS_SOURCE > /dev/null; then
# Require certfile and keyfile
#bashio::config.require 'certfile'
#bashio::config.require 'keyfile'
# If certfile or keyfile does not exist, generate self-signed cert
if [ ! -f "$certfile" ] || [ ! -f "$keyfile" ]; then
bashio::log.warning "SSL is enabled, but either certfile, keyfile or both are missing. Falling back to a self-signed certificate..."
certfile="/data/ssl/fullchain.pem"
keyfile="/data/ssl/privkey.pem"
/usr/local/bin/ssl-check-generate.sh "$certfile" "$keyfile" true
else
/usr/local/bin/ssl-check-generate.sh "$certfile" "$keyfile" false
fi
fi
printf '{
"certfile": "%s",
"keyfile": "%s",
"port": "%s",
"protocol": "%s",
"ssl": %s,
"ingress_user": "%s"
}' "$certfile" "$keyfile" "$nginx_port" "$service_protocol" "$(jq -r '.ssl' $OPTIONS_SOURCE)" "$ingress_user" \
| tempio \
-template /etc/nginx/templates/direct.gtpl \
-out /etc/nginx/servers/direct.conf

View File

@@ -0,0 +1 @@
oneshot

View File

@@ -0,0 +1 @@
/etc/s6-overlay/s6-rc.d/init-nginx/run

View File

@@ -1,21 +1,148 @@
#!/command/with-contenv bashio
#!/bin/bash
# shellcheck shell=bash
set -e
set -euo pipefail
EXTENSIONS_DIR="${CHROME_EXTENSIONS_DIR:-/usr/src/chrome/extensions}"
############################
# Configurable directories #
############################
extensions=()
for extension in "${EXTENSIONS_DIR}/i-dont-care-about-cookies" "${EXTENSIONS_DIR}/ublock-origin"; do
if [ -d "$extension" ]; then
extensions+=("$extension")
CHROME_CACHE_DIR="/data/cache"
CHROME_PROFILE_DIR="/data/chrome-profile"
EXT_BASE_DIR="/data/extensions"
# Extension IDs (Chrome Web Store)
# uBlock Origin (MV2): cjpalhdlnbpafiamejdnhcphjbkeiagm
# I don't care about cookies: fihnjjcciajhdojfnbdddfaoknhalnja
UBLOCK_ID="cjpalhdlnbpafiamejdnhcphjbkeiagm"
IDCAC_ID="fihnjjcciajhdojfnbdddfaoknhalnja"
UBLOCK_DIR="${EXT_BASE_DIR}/ublock"
IDCAC_DIR="${EXT_BASE_DIR}/idontcareaboutcookies"
#################################
# Ensure persistent writable FS #
#################################
mkdir -p "$CHROME_CACHE_DIR" "$CHROME_PROFILE_DIR" "$EXT_BASE_DIR"
chown -R chrome:chrome "$CHROME_CACHE_DIR" "$CHROME_PROFILE_DIR" "$EXT_BASE_DIR"
########################
# Helper: download CRX #
########################
need_bin() {
local b="$1"
if ! command -v "$b" >/dev/null 2>&1; then
echo "Missing required binary: $b"
exit 1
fi
done
}
extension_flag=""
if [ ${#extensions[@]} -gt 0 ]; then
extension_flag="--load-extension=$(IFS=,; echo "${extensions[*]}")"
fi
crx_to_zip() {
# CRX2/CRX3 contains a header before the ZIP payload.
# We locate the first ZIP local-file header "PK\003\004" and write from there.
local crx="$1"
local zip="$2"
python3 - "$crx" "$zip" <<'PY'
import sys
crx_path, zip_path = sys.argv[1], sys.argv[2]
with open(crx_path, "rb") as f:
data = f.read()
sig = b"PK\x03\x04"
i = data.find(sig)
if i == -1:
raise SystemExit("Could not find ZIP signature in CRX (PK\\x03\\x04).")
with open(zip_path, "wb") as f:
f.write(data[i:])
PY
}
download_and_unpack_extension() {
local ext_id="$1"
local out_dir="$2"
local name="$3"
# Skip if already unpacked
if [ -f "${out_dir}/manifest.json" ]; then
return 0
fi
need_bin python3
need_bin curl
need_bin unzip
mkdir -p "$out_dir"
chown -R chrome:chrome "$out_dir"
local tmpdir
tmpdir="$(mktemp -d)"
# shellcheck disable=SC2064
trap "rm -rf '$tmpdir'" EXIT
local crx="${tmpdir}/${name}.crx"
local zip="${tmpdir}/${name}.zip"
# CWS update endpoint (works for most public extensions)
# Note: prodversion is just a hint; keep it reasonably recent.
local url
url="https://clients2.google.com/service/update2/crx?response=redirect&prodversion=120.0.0.0&acceptformat=crx2,crx3&x=id%3D${ext_id}%26installsource%3Dondemand%26uc"
echo "Downloading ${name} (${ext_id})..."
curl -fsSL "$url" -o "$crx"
echo "Unpacking ${name}..."
crx_to_zip "$crx" "$zip"
# Unzip into a clean directory
rm -rf "${out_dir:?}/"*
unzip -q "$zip" -d "$out_dir"
chown -R chrome:chrome "$out_dir"
# Basic validation
if [ ! -f "${out_dir}/manifest.json" ]; then
echo "Failed to unpack ${name}: manifest.json not found in ${out_dir}"
exit 1
fi
rm -rf "$tmpdir"
trap - EXIT
}
#########################
# Download extensions #
#########################
download_and_unpack_extension "$UBLOCK_ID" "$UBLOCK_DIR" "ublock-origin"
download_and_unpack_extension "$IDCAC_ID" "$IDCAC_DIR" "i-dont-care-about-cookies"
EXTENSIONS="${UBLOCK_DIR},${IDCAC_DIR}"
#########################
# Start Chromium #
#########################
cd /usr/src/chrome
exec su chrome -c "chromium-browser --headless=new --no-sandbox --disable-gpu --disable-dev-shm-usage --remote-debugging-address=0.0.0.0 --remote-debugging-port=9222 --hide-scrollbars --disable-crash-reporter --no-crash-upload --user-data-dir=/data/chrome ${extension_flag}"
# Use exec so s6 can manage the process; avoid backgrounding and /dev/null-ing
exec su chrome -c "
chromium-browser \
--headless=new \
--no-sandbox \
--disable-gpu \
--disable-dev-shm-usage \
--disable-crash-reporter \
--no-crash-upload \
--hide-scrollbars \
--remote-debugging-address=0.0.0.0 \
--remote-debugging-port=9222 \
--user-data-dir='${CHROME_PROFILE_DIR}' \
--disk-cache-dir='${CHROME_CACHE_DIR}' \
--disable-extensions-except='${EXTENSIONS}' \
--load-extension='${EXTENSIONS}' \
about:blank
"

View File

@@ -1 +1 @@
longrun
longrun

View File

@@ -0,0 +1 @@
/etc/s6-overlay/s6-rc.d/svc-chrome/run

View File

@@ -1,7 +1,12 @@
#!/command/with-contenv bashio
#!/command/with-contenv bash
# shellcheck shell=bash
set -e
mkdir -p "${MEILI_DIR}"
if [ ! -d "$MEILI_DIR" ]; then
mkdir -p "$MEILI_DIR"
fi
exec /bin/meilisearch --db-path "${MEILI_DIR}" --no-analytics --experimental-dumpless-upgrade
cd "$MEILI_DIR"
exec /bin/meilisearch --no-analytics --experimental-dumpless-upgrade

View File

@@ -1 +1 @@
longrun
longrun

View File

@@ -0,0 +1 @@
/etc/s6-overlay/s6-rc.d/svc-meilisearch/run

View File

@@ -0,0 +1,9 @@
#!/command/with-contenv bashio
# shellcheck shell=bash
set -e
bashio::net.wait_for "$SERVICE_PORT" localhost 900
bashio::log.info "Starting NGinx..."
exec nginx

View File

@@ -0,0 +1 @@
longrun

View File

@@ -0,0 +1 @@
/etc/s6-overlay/s6-rc.d/svc-nginx/run

View File

@@ -0,0 +1,47 @@
#!/command/with-contenv bashio
# shellcheck shell=bash
set -e
# Check for required arguments
if [ $# -ne 3 ]; then
bashio::log.error "[ssl-check-generate.sh] missing: <certfile> <keyfile> <selfsigned>"
exit 1
fi
bashio::log.debug "SSL Certificate check"
renew_days=90
certfile="$1"
keyfile="$2"
selfsigned=${3:-true}
if [ ! -f "$certfile" ] || [ ! -f "$keyfile" ]; then
if [ "$selfsigned" = "true" ]; then
/usr/local/bin/ssl-keygen.sh "$certfile" "$keyfile"
exit 0
else
bashio::log.error "[ssl-check-generate.sh] either certfile, keyfile, or both are missing"
exit 1
fi
fi
enddate=$(openssl x509 -enddate -noout -in "$certfile" 2>/dev/null || true)
if [ -n "$enddate" ]; then
expiry_date=$(echo "$enddate" | cut -d= -f2 | sed 's/ GMT$//')
expiry_ts=$(date -d "$expiry_date" +%s)
now_ts=$(date +%s)
days_left=$(( (expiry_ts - now_ts) / 86400 ))
if [ "$days_left" -le "$renew_days" ]; then
bashio::log.info "Self-signed cert expiring in $days_left days, regenerating..."
/usr/local/bin/ssl-keygen.sh "$certfile" "$keyfile"
fi
else
bashio::log.error "Unable to determine ssl certificate expiry date"
fi
if pgrep -x nginx >/dev/null 2>&1; then
nginx -s reload
fi

View File

@@ -0,0 +1,57 @@
#!/command/with-contenv bashio
# shellcheck shell=bash
set -e
# Check for required arguments
if [ $# -ne 2 ]; then
bashio::log.error "[ssl-keygen.sh] missing: <certfile> <keyfile>"
exit 1
fi
certfile="$1"
keyfile="$2"
[ -f "$certfile" ] && rm -f "$certfile"
[ -f "$keyfile" ] && rm -f "$keyfile"
mkdir -p "$(dirname "$certfile")" && mkdir -p "$(dirname "$keyfile")"
if ! hostname="$(bashio::info.hostname 2> /dev/null)" || [ -z "$hostname" ]; then
hostname="homeassistant"
fi
tmp_openssl_cfg=$(mktemp)
trap 'rm -f "$tmp_openssl_cfg"' EXIT
cat > "$tmp_openssl_cfg" <<EOF
[req]
default_bits = 4096
prompt = no
default_md = sha256
req_extensions = req_ext
distinguished_name = dn
[dn]
CN = ${hostname}.local
[req_ext]
subjectAltName = @alt_names
[alt_names]
DNS.1 = localhost
DNS.2 = ${hostname}.local
EOF
if ! openssl req -x509 -nodes -days 3650 \
-newkey rsa:4096 \
-keyout "$keyfile" \
-out "$certfile" \
-config "$tmp_openssl_cfg" \
-extensions req_ext; then
# Certificate gen failed
bashio::log.error "OpenSSL certificate generation failed"
exit 1
fi
bashio::log.info "New self-signed certificate generated"

View File