inital commit
8
.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
/configs/code-server/.local/*
|
||||||
|
!/configs/code-server/.local/.gitkeep
|
||||||
|
|
||||||
|
/configs/code-server/.config/*
|
||||||
|
!/configs/code-server/.config/.gitkeep
|
||||||
|
|
||||||
|
.env
|
||||||
|
.env*
|
84
Dockerfile.code-server
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
FROM codercom/code-server:latest
|
||||||
|
|
||||||
|
USER root
|
||||||
|
|
||||||
|
# Install Python and dependencies
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
python3 \
|
||||||
|
python3-pip \
|
||||||
|
python3-venv \
|
||||||
|
python3-full \
|
||||||
|
pipx \
|
||||||
|
# Dependencies for CairoSVG and Pillow (PIL)
|
||||||
|
libcairo2-dev \
|
||||||
|
libfreetype6-dev \
|
||||||
|
libffi-dev \
|
||||||
|
libjpeg-dev \
|
||||||
|
libpng-dev \
|
||||||
|
libz-dev \
|
||||||
|
python3-dev \
|
||||||
|
pkg-config \
|
||||||
|
# Additional dependencies for advanced image processing
|
||||||
|
libwebp-dev \
|
||||||
|
libtiff5-dev \
|
||||||
|
libopenjp2-7-dev \
|
||||||
|
liblcms2-dev \
|
||||||
|
libxml2-dev \
|
||||||
|
libxslt1-dev \
|
||||||
|
# PDF generation dependencies
|
||||||
|
weasyprint \
|
||||||
|
fonts-roboto \
|
||||||
|
# Git for git-based plugins
|
||||||
|
git \
|
||||||
|
# For lxml
|
||||||
|
zlib1g-dev \
|
||||||
|
# Required for some plugins
|
||||||
|
build-essential \
|
||||||
|
&& apt-get clean \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Switch to non-root user (coder)
|
||||||
|
USER coder
|
||||||
|
|
||||||
|
# Set up a virtual environment for mkdocs
|
||||||
|
RUN mkdir -p /home/coder/.venv
|
||||||
|
RUN python3 -m venv /home/coder/.venv/mkdocs
|
||||||
|
|
||||||
|
# Install mkdocs-material in the virtual environment with all extras
|
||||||
|
RUN /home/coder/.venv/mkdocs/bin/pip install "mkdocs-material[imaging,recommended,git]"
|
||||||
|
|
||||||
|
# Install additional useful MkDocs plugins
|
||||||
|
RUN /home/coder/.venv/mkdocs/bin/pip install \
|
||||||
|
mkdocs-minify-plugin \
|
||||||
|
mkdocs-git-revision-date-localized-plugin \
|
||||||
|
mkdocs-glightbox \
|
||||||
|
mkdocs-redirects \
|
||||||
|
mkdocs-awesome-pages-plugin \
|
||||||
|
mkdocs-blog-plugin \
|
||||||
|
mkdocs-rss-plugin \
|
||||||
|
mkdocs-meta-descriptions-plugin \
|
||||||
|
mkdocs-swagger-ui-tag \
|
||||||
|
mkdocs-macros-plugin \
|
||||||
|
mkdocs-material-extensions \
|
||||||
|
mkdocs-section-index \
|
||||||
|
mkdocs-table-reader-plugin \
|
||||||
|
mkdocs-pdf-export-plugin \
|
||||||
|
mkdocs-mermaid2-plugin \
|
||||||
|
pymdown-extensions \
|
||||||
|
pygments \
|
||||||
|
pillow \
|
||||||
|
cairosvg
|
||||||
|
|
||||||
|
# Add the virtual environment bin to PATH
|
||||||
|
ENV PATH="/home/coder/.venv/mkdocs/bin:${PATH}"
|
||||||
|
|
||||||
|
# Add shell configuration to activate the virtual environment in .bashrc
|
||||||
|
RUN echo 'export PATH="/home/coder/.venv/mkdocs/bin:$PATH"' >> ~/.bashrc
|
||||||
|
RUN echo 'export PATH="/home/coder/.local/bin:$PATH"' >> ~/.bashrc
|
||||||
|
|
||||||
|
# Create a convenience script to simplify running mkdocs commands
|
||||||
|
RUN mkdir -p /home/coder/.local/bin \
|
||||||
|
&& echo '#!/bin/bash\ncd /home/coder/mkdocs\nmkdocs "$@"' > /home/coder/.local/bin/run-mkdocs \
|
||||||
|
&& chmod +x /home/coder/.local/bin/run-mkdocs
|
||||||
|
|
||||||
|
WORKDIR /home/coder
|
51
README.md
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
# Changemaker Lite
|
||||||
|
|
||||||
|
Changemaker Lite is a streamlined documentation and development platform featuring essential self-hosted services for creating, managing, and automating content workflows.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- **Homepage**: Modern dashboard for accessing all services
|
||||||
|
- **Code Server**: VS Code in your browser for remote development
|
||||||
|
- **MkDocs Material**: Beautiful documentation with live preview
|
||||||
|
- **Static Site Server**: High-performance hosting for built sites
|
||||||
|
- **Listmonk**: Self-hosted newsletter and email campaign management
|
||||||
|
- **PostgreSQL**: Reliable database backend
|
||||||
|
- **n8n**: Workflow automation and service integration
|
||||||
|
- **NocoDB**: No-code database platform and smart spreadsheet interface
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Clone the repository
|
||||||
|
git clone https://git.albertademocracytaskforce.org/admin/ab.dem.tf.changemaker.git
|
||||||
|
cd changemaker.lite
|
||||||
|
|
||||||
|
# Configure environment (creates .env file)
|
||||||
|
./config.sh
|
||||||
|
|
||||||
|
# Start all services
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
## Service Access
|
||||||
|
|
||||||
|
After starting, access services at:
|
||||||
|
|
||||||
|
- **Homepage Dashboard**: http://localhost:3010
|
||||||
|
- **Documentation (Dev)**: http://localhost:4000
|
||||||
|
- **Documentation (Built)**: http://localhost:4001
|
||||||
|
- **Code Server**: http://localhost:8888
|
||||||
|
- **Listmonk**: http://localhost:9000
|
||||||
|
- **n8n**: http://localhost:5678
|
||||||
|
- **NocoDB**: http://localhost:8090
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
Complete documentation is available in the MkDocs site, including:
|
||||||
|
|
||||||
|
- Service configuration guides
|
||||||
|
- Integration examples
|
||||||
|
- Workflow automation tutorials
|
||||||
|
- Troubleshooting guides
|
||||||
|
|
||||||
|
Visit http://localhost:4000 after starting services to access the full documentation.
|
290
add-cname-records.sh
Executable file
@ -0,0 +1,290 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
echo "#############################################################"
|
||||||
|
echo "# "
|
||||||
|
echo "# DNS Setup for Changemaker.lite Services "
|
||||||
|
echo "# "
|
||||||
|
echo "# This script will ADD DNS records for your services. "
|
||||||
|
echo "# Existing DNS records will NOT be deleted. "
|
||||||
|
echo "# "
|
||||||
|
echo "#############################################################"
|
||||||
|
echo ""
|
||||||
|
echo "-------------------------------------------------------------"
|
||||||
|
echo "Cloudflare Credentials Required"
|
||||||
|
echo "Please ensure your .env file contains the following variables:"
|
||||||
|
echo " CF_API_TOKEN=your_cloudflare_api_token"
|
||||||
|
echo " CF_ZONE_ID=your_cloudflare_zone_id"
|
||||||
|
echo " CF_TUNNEL_ID=your_cloudflared_tunnel_id"
|
||||||
|
echo " CF_DOMAIN=yourdomain.com"
|
||||||
|
echo ""
|
||||||
|
echo "You can find these values in your Cloudflare dashboard:"
|
||||||
|
echo " - API Token: https://dash.cloudflare.com/profile/api-tokens (Create a token with Zone:DNS:Edit and Access:Apps:Edit permissions for your domain)"
|
||||||
|
echo " - Zone ID: On your domain's overview page"
|
||||||
|
echo " - Tunnel ID: In the Zero Trust dashboard under Access > Tunnels"
|
||||||
|
echo " - Domain: The domain you want to use for your services"
|
||||||
|
echo ""
|
||||||
|
echo "-------------------------------------------------------------"
|
||||||
|
echo ""
|
||||||
|
read -p "Type 'y' to continue or any other key to abort: " consent
|
||||||
|
if [[ "$consent" != "y" && "$consent" != "Y" ]]; then
|
||||||
|
echo "Aborted by user."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Source environment variables from the .env file in the same directory
|
||||||
|
ENV_FILE="$(dirname "$0")/.env"
|
||||||
|
if [ -f "$ENV_FILE" ]; then
|
||||||
|
export $(grep -v '^#' "$ENV_FILE" | xargs)
|
||||||
|
else
|
||||||
|
echo "Error: .env file not found at $ENV_FILE"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if required Cloudflare variables are set
|
||||||
|
if [ -z "$CF_API_TOKEN" ] || [ -z "$CF_ZONE_ID" ] || [ -z "$CF_TUNNEL_ID" ] || [ -z "$CF_DOMAIN" ]; then
|
||||||
|
echo "Error: One or more required Cloudflare environment variables (CF_API_TOKEN, CF_ZONE_ID, CF_TUNNEL_ID, CF_DOMAIN) are not set in $ENV_FILE."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if jq is installed
|
||||||
|
if ! command -v jq &> /dev/null; then
|
||||||
|
echo "Error: jq is required but not installed. Please install jq to continue."
|
||||||
|
echo "On Debian/Ubuntu: sudo apt-get install jq"
|
||||||
|
echo "On RHEL/CentOS: sudo yum install jq"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Array of subdomains that need DNS records - updated to match our active services
|
||||||
|
SUBDOMAINS=(
|
||||||
|
"dashboard"
|
||||||
|
"code"
|
||||||
|
"listmonk"
|
||||||
|
"docs"
|
||||||
|
"n8n"
|
||||||
|
"db"
|
||||||
|
"git"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Function to check if DNS record already exists
|
||||||
|
record_exists() {
|
||||||
|
local subdomain=$1
|
||||||
|
local records=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/dns_records?name=$subdomain.$CF_DOMAIN" \
|
||||||
|
-H "Authorization: Bearer $CF_API_TOKEN" \
|
||||||
|
-H "Content-Type: application/json")
|
||||||
|
|
||||||
|
local count=$(echo $records | jq -r '.result | length')
|
||||||
|
[ "$count" -gt 0 ]
|
||||||
|
}
|
||||||
|
|
||||||
|
# Add CNAME records for each subdomain (only if they don't exist)
|
||||||
|
echo "Adding DNS records for services..."
|
||||||
|
|
||||||
|
for subdomain in "${SUBDOMAINS[@]}"; do
|
||||||
|
if record_exists "$subdomain"; then
|
||||||
|
echo "DNS record for $subdomain.$CF_DOMAIN already exists, skipping..."
|
||||||
|
else
|
||||||
|
echo "Adding CNAME record for $subdomain.$CF_DOMAIN..."
|
||||||
|
|
||||||
|
response=$(curl -s -X POST "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/dns_records" \
|
||||||
|
-H "Authorization: Bearer $CF_API_TOKEN" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
--data "{
|
||||||
|
\"type\": \"CNAME\",
|
||||||
|
\"name\": \"$subdomain\",
|
||||||
|
\"content\": \"$CF_TUNNEL_ID.cfargotunnel.com\",
|
||||||
|
\"ttl\": 1,
|
||||||
|
\"proxied\": true
|
||||||
|
}")
|
||||||
|
|
||||||
|
success=$(echo $response | jq -r '.success')
|
||||||
|
if [ "$success" == "true" ]; then
|
||||||
|
echo "✓ Successfully added CNAME record for $subdomain.$CF_DOMAIN"
|
||||||
|
else
|
||||||
|
echo "✗ Failed to add CNAME record for $subdomain.$CF_DOMAIN"
|
||||||
|
echo "Error: $(echo $response | jq -r '.errors[0].message')"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Add root domain record if it doesn't exist
|
||||||
|
if record_exists "@"; then
|
||||||
|
echo "Root domain DNS record already exists, skipping..."
|
||||||
|
else
|
||||||
|
echo "Adding root domain CNAME record..."
|
||||||
|
response=$(curl -s -X POST "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/dns_records" \
|
||||||
|
-H "Authorization: Bearer $CF_API_TOKEN" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
--data "{
|
||||||
|
\"type\": \"CNAME\",
|
||||||
|
\"name\": \"@\",
|
||||||
|
\"content\": \"$CF_TUNNEL_ID.cfargotunnel.com\",
|
||||||
|
\"ttl\": 1,
|
||||||
|
\"proxied\": true
|
||||||
|
}")
|
||||||
|
|
||||||
|
success=$(echo $response | jq -r '.success')
|
||||||
|
if [ "$success" == "true" ]; then
|
||||||
|
echo "✓ Successfully added root domain CNAME record"
|
||||||
|
else
|
||||||
|
echo "✗ Failed to add root domain CNAME record"
|
||||||
|
echo "Error: $(echo $response | jq -r '.errors[0].message')"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "DNS records setup complete!"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Prompt for admin email for secured services
|
||||||
|
echo "-------------------------------------------------------------"
|
||||||
|
echo "Setting up Cloudflare Access Protection"
|
||||||
|
echo "-------------------------------------------------------------"
|
||||||
|
echo ""
|
||||||
|
echo "The following services will be protected with authentication:"
|
||||||
|
echo " - dashboard.$CF_DOMAIN"
|
||||||
|
echo " - code.$CF_DOMAIN"
|
||||||
|
echo ""
|
||||||
|
echo "Please enter the admin email address that should have access:"
|
||||||
|
read ADMIN_EMAIL
|
||||||
|
|
||||||
|
# Validate email format
|
||||||
|
if [[ ! "$ADMIN_EMAIL" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then
|
||||||
|
echo "Error: Invalid email format. Please provide a valid email address."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Services that require authentication - updated for our use case
|
||||||
|
PROTECTED_SERVICES=("dashboard" "code")
|
||||||
|
|
||||||
|
# Services that should have bypass policies (public access) - updated for our use case
|
||||||
|
BYPASS_SERVICES=("listmonk" "docs" "n8n" "db" "git")
|
||||||
|
|
||||||
|
# Function to create access application with email authentication
|
||||||
|
create_protected_app() {
|
||||||
|
local service=$1
|
||||||
|
echo "Setting up authentication for $service.$CF_DOMAIN..."
|
||||||
|
|
||||||
|
app_response=$(curl -s -X POST "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/access/apps" \
|
||||||
|
-H "Authorization: Bearer $CF_API_TOKEN" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
--data "{
|
||||||
|
\"name\": \"$service $CF_DOMAIN\",
|
||||||
|
\"domain\": \"$service.$CF_DOMAIN\",
|
||||||
|
\"type\": \"self_hosted\",
|
||||||
|
\"session_duration\": \"24h\",
|
||||||
|
\"app_launcher_visible\": true,
|
||||||
|
\"skip_interstitial\": true
|
||||||
|
}")
|
||||||
|
|
||||||
|
app_id=$(echo $app_response | jq -r '.result.id')
|
||||||
|
|
||||||
|
if [ -z "$app_id" ] || [ "$app_id" == "null" ]; then
|
||||||
|
echo "✗ Error creating access application for $service"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "✓ Created access application for $service (ID: $app_id)"
|
||||||
|
|
||||||
|
# Create authentication policy
|
||||||
|
policy_response=$(curl -s -X POST "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/access/apps/$app_id/policies" \
|
||||||
|
-H "Authorization: Bearer $CF_API_TOKEN" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
--data "{
|
||||||
|
\"name\": \"Admin Access\",
|
||||||
|
\"decision\": \"allow\",
|
||||||
|
\"include\": [{
|
||||||
|
\"email\": {
|
||||||
|
\"email\": \"$ADMIN_EMAIL\"
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
\"require\": [],
|
||||||
|
\"exclude\": []
|
||||||
|
}")
|
||||||
|
|
||||||
|
policy_success=$(echo $policy_response | jq -r '.success')
|
||||||
|
|
||||||
|
if [ "$policy_success" == "true" ]; then
|
||||||
|
echo "✓ Created authentication policy for $service"
|
||||||
|
else
|
||||||
|
echo "✗ Failed to create authentication policy for $service"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to create bypass application (public access)
|
||||||
|
create_bypass_app() {
|
||||||
|
local service=$1
|
||||||
|
echo "Setting up public access for $service.$CF_DOMAIN..."
|
||||||
|
|
||||||
|
app_response=$(curl -s -X POST "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/access/apps" \
|
||||||
|
-H "Authorization: Bearer $CF_API_TOKEN" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
--data "{
|
||||||
|
\"name\": \"$service $CF_DOMAIN\",
|
||||||
|
\"domain\": \"$service.$CF_DOMAIN\",
|
||||||
|
\"type\": \"self_hosted\",
|
||||||
|
\"session_duration\": \"24h\",
|
||||||
|
\"app_launcher_visible\": false,
|
||||||
|
\"skip_interstitial\": true
|
||||||
|
}")
|
||||||
|
|
||||||
|
app_id=$(echo $app_response | jq -r '.result.id')
|
||||||
|
|
||||||
|
if [ -z "$app_id" ] || [ "$app_id" == "null" ]; then
|
||||||
|
echo "✗ Error creating access application for $service"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "✓ Created access application for $service (ID: $app_id)"
|
||||||
|
|
||||||
|
# Create bypass policy
|
||||||
|
policy_response=$(curl -s -X POST "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/access/apps/$app_id/policies" \
|
||||||
|
-H "Authorization: Bearer $CF_API_TOKEN" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
--data "{
|
||||||
|
\"name\": \"Public Access\",
|
||||||
|
\"decision\": \"bypass\",
|
||||||
|
\"include\": [{
|
||||||
|
\"everyone\": {}
|
||||||
|
}],
|
||||||
|
\"require\": [],
|
||||||
|
\"exclude\": []
|
||||||
|
}")
|
||||||
|
|
||||||
|
policy_success=$(echo $policy_response | jq -r '.success')
|
||||||
|
|
||||||
|
if [ "$policy_success" == "true" ]; then
|
||||||
|
echo "✓ Created public access policy for $service"
|
||||||
|
else
|
||||||
|
echo "✗ Failed to create public access policy for $service"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "Creating Cloudflare Access applications..."
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Create protected applications
|
||||||
|
for service in "${PROTECTED_SERVICES[@]}"; do
|
||||||
|
create_protected_app "$service"
|
||||||
|
echo ""
|
||||||
|
done
|
||||||
|
|
||||||
|
# Create bypass applications for public services
|
||||||
|
for service in "${BYPASS_SERVICES[@]}"; do
|
||||||
|
create_bypass_app "$service"
|
||||||
|
echo ""
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "-------------------------------------------------------------"
|
||||||
|
echo "Setup Complete!"
|
||||||
|
echo "-------------------------------------------------------------"
|
||||||
|
echo ""
|
||||||
|
echo "Protected services (require authentication with $ADMIN_EMAIL):"
|
||||||
|
for service in "${PROTECTED_SERVICES[@]}"; do
|
||||||
|
echo " - https://$service.$CF_DOMAIN"
|
||||||
|
done
|
||||||
|
echo ""
|
||||||
|
echo "Public services (no authentication required):"
|
||||||
|
for service in "${BYPASS_SERVICES[@]}"; do
|
||||||
|
echo " - https://$service.$CF_DOMAIN"
|
||||||
|
done
|
||||||
|
echo ""
|
||||||
|
echo "All services should be accessible through your Cloudflare tunnel."
|
724
config.sh
Executable file
@ -0,0 +1,724 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
cat << "EOF"
|
||||||
|
██████╗██╗ ██╗ █████╗ ███╗ ██╗ ██████╗ ███████╗
|
||||||
|
██╔════╝██║ ██║██╔══██╗████╗ ██║██╔════╝ ██╔════╝
|
||||||
|
██║ ███████║███████║██╔██╗ ██║██║ ███╗█████╗
|
||||||
|
██║ ██╔══██║██╔══██║██║╚██╗██║██║ ██║██╔══╝
|
||||||
|
╚██████╗██║ ██║██║ ██║██║ ╚████║╚██████╔╝███████╗
|
||||||
|
╚═════╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═══╝ ╚═════╝ ╚══════╝
|
||||||
|
|
||||||
|
███╗ ███╗ █████╗ ██╗ ██╗███████╗██████╗
|
||||||
|
████╗ ████║██╔══██╗██║ ██╔╝██╔════╝██╔══██╗
|
||||||
|
██╔████╔██║███████║█████╔╝ █████╗ ██████╔╝
|
||||||
|
██║╚██╔╝██║██╔══██║██╔═██╗ ██╔══╝ ██╔══██╗
|
||||||
|
██║ ╚═╝ ██║██║ ██║██║ ██╗███████╗██║ ██║
|
||||||
|
╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝
|
||||||
|
Configuration Wizard
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Get the absolute path of the script directory
|
||||||
|
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||||
|
ENV_FILE="$SCRIPT_DIR/.env"
|
||||||
|
MKDOCS_YML="$SCRIPT_DIR/mkdocs/docs/mkdocs.yml"
|
||||||
|
TUNNEL_CONFIG_DIR="$SCRIPT_DIR/configs/cloudflare"
|
||||||
|
TUNNEL_CONFIG_FILE="$TUNNEL_CONFIG_DIR/tunnel-config.yml"
|
||||||
|
SERVICES_YAML="$SCRIPT_DIR/configs/homepage/services.yaml"
|
||||||
|
MAIN_HTML="$SCRIPT_DIR/mkdocs/docs/overrides/main.html"
|
||||||
|
|
||||||
|
echo "Looking for .env file at: $ENV_FILE"
|
||||||
|
|
||||||
|
# Function to generate a random secure password
|
||||||
|
generate_password() {
|
||||||
|
local length=${1:-16}
|
||||||
|
openssl rand -base64 48 | tr -dc 'a-zA-Z0-9!@#$%^&*()-_=+' | head -c "$length"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to safely update environment variables in .env file
|
||||||
|
update_env_var() {
|
||||||
|
local key=$1
|
||||||
|
local value=$2
|
||||||
|
|
||||||
|
# More robust method to handle special characters in passwords
|
||||||
|
if grep -q "^$key=" "$ENV_FILE"; then
|
||||||
|
# Create a temporary file
|
||||||
|
local tmpfile=$(mktemp)
|
||||||
|
|
||||||
|
# Process the .env file line by line
|
||||||
|
while IFS= read -r line; do
|
||||||
|
if [[ "$line" =~ ^$key= ]]; then
|
||||||
|
echo "$key=$value" >> "$tmpfile"
|
||||||
|
else
|
||||||
|
echo "$line" >> "$tmpfile"
|
||||||
|
fi
|
||||||
|
done < "$ENV_FILE"
|
||||||
|
|
||||||
|
# Replace the original file with the temporary file
|
||||||
|
mv "$tmpfile" "$ENV_FILE"
|
||||||
|
echo "Updated $key in .env file"
|
||||||
|
else
|
||||||
|
# Add new key-value pair if it doesn't exist
|
||||||
|
echo "$key=$value" >> "$ENV_FILE"
|
||||||
|
echo "Added $key to .env file"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to create a timestamped backup of the .env file
|
||||||
|
backup_env_file() {
|
||||||
|
if [ -f "$ENV_FILE" ]; then
|
||||||
|
local timestamp=$(date +"%Y%m%d_%H%M%S")
|
||||||
|
local backup_file="$ENV_FILE.backup_$timestamp"
|
||||||
|
|
||||||
|
echo "Creating backup of current .env file to: $backup_file"
|
||||||
|
if cp "$ENV_FILE" "$backup_file"; then
|
||||||
|
echo "Backup created successfully!"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
echo "Failed to create backup file. Proceeding with caution..."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to initialize the .env file with default values
|
||||||
|
initialize_env_file() {
|
||||||
|
echo "Initializing new .env file with default values..."
|
||||||
|
|
||||||
|
cat > "$ENV_FILE" << EOL
|
||||||
|
# Never share this file publicly. It contains sensitive information.
|
||||||
|
# This file is used to configure various applications and services.
|
||||||
|
# Generated by Changemaker Config Wizard on $(date)
|
||||||
|
|
||||||
|
# User and Group Configuration
|
||||||
|
USER_NAME=coder
|
||||||
|
USER_ID=1000
|
||||||
|
GROUP_ID=1000
|
||||||
|
|
||||||
|
# Port Configuration
|
||||||
|
CODE_SERVER_PORT=8888
|
||||||
|
LISTMONK_PORT=9000
|
||||||
|
LISTMONK_DB_PORT=5432
|
||||||
|
MKDOCS_PORT=4000
|
||||||
|
MKDOCS_SITE_SERVER_PORT=4001
|
||||||
|
N8N_PORT=5678
|
||||||
|
NOCODB_PORT=8090
|
||||||
|
HOMEPAGE_PORT=3010
|
||||||
|
GITEA_WEB_PORT=3030
|
||||||
|
GITEA_SSH_PORT=2222
|
||||||
|
|
||||||
|
# Domain Configuration
|
||||||
|
BASE_DOMAIN=https://changeme.org
|
||||||
|
DOMAIN=changeme.org
|
||||||
|
LISTMONK_HOSTNAME=listmonk.changeme.org
|
||||||
|
N8N_HOST=n8n.changeme.org
|
||||||
|
GITEA_DOMAIN=git.changeme.org
|
||||||
|
GITEA_ROOT_URL=https://git.changeme.org
|
||||||
|
|
||||||
|
# Cloudflare Configuration
|
||||||
|
CF_API_TOKEN=your_cloudflare_api_token
|
||||||
|
CF_ZONE_ID=your_cloudflare_zone_id
|
||||||
|
CF_TUNNEL_ID=your_cloudflared_tunnel_id
|
||||||
|
CF_DOMAIN=changeme.org
|
||||||
|
|
||||||
|
# Database Configuration (PostgreSQL for Listmonk)
|
||||||
|
POSTGRES_USER=listmonk
|
||||||
|
POSTGRES_PASSWORD=changeMe
|
||||||
|
POSTGRES_DB=listmonk
|
||||||
|
|
||||||
|
# Listmonk Database Performance Settings
|
||||||
|
LISTMONK_DB_MAX_OPEN=25
|
||||||
|
LISTMONK_DB_MAX_IDLE=25
|
||||||
|
LISTMONK_DB_MAX_LIFETIME=300s
|
||||||
|
|
||||||
|
# Listmonk Admin Configuration
|
||||||
|
LISTMONK_ADMIN_USER=admin
|
||||||
|
LISTMONK_ADMIN_PASSWORD=changeMe
|
||||||
|
|
||||||
|
# N8N Configuration
|
||||||
|
N8N_USER_EMAIL=admin@example.com
|
||||||
|
N8N_USER_PASSWORD=changeMe
|
||||||
|
N8N_ENCRYPTION_KEY=changeMe
|
||||||
|
GENERIC_TIMEZONE=UTC
|
||||||
|
|
||||||
|
# Nocodb Configuration
|
||||||
|
NOCODB_JWT_SECRET=changeMe
|
||||||
|
NOCODB_DB_NAME=nocodb
|
||||||
|
NOCODB_DB_USER=noco
|
||||||
|
NOCODB_DB_PASSWORD=changeMe
|
||||||
|
HOMEPAGE_VAR_BASE_URL=https://changeme.org
|
||||||
|
EOL
|
||||||
|
|
||||||
|
echo "New .env file created with default values."
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to update the site_url in mkdocs.yml
|
||||||
|
update_mkdocs_yml() {
|
||||||
|
local new_domain=$1
|
||||||
|
|
||||||
|
if [ ! -f "$MKDOCS_YML" ]; then
|
||||||
|
echo "Warning: mkdocs.yml not found at $MKDOCS_YML"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Updating site_url in mkdocs.yml..."
|
||||||
|
|
||||||
|
# Create a backup of the mkdocs.yml file
|
||||||
|
local timestamp=$(date +"%Y%m%d_%H%M%S")
|
||||||
|
local backup_file="${MKDOCS_YML}.backup_${timestamp}"
|
||||||
|
cp "$MKDOCS_YML" "$backup_file"
|
||||||
|
echo "Created backup of mkdocs.yml at $backup_file"
|
||||||
|
|
||||||
|
# Update the site_url value
|
||||||
|
sed -i "s|^site_url:.*|site_url: https://$new_domain|" "$MKDOCS_YML"
|
||||||
|
|
||||||
|
if grep -q "site_url: https://$new_domain" "$MKDOCS_YML"; then
|
||||||
|
echo "Updated site_url in mkdocs.yml to: https://$new_domain"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
echo "Warning: Failed to update site_url in mkdocs.yml"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to update service URLs in services.yaml
|
||||||
|
update_services_yaml() {
|
||||||
|
local new_domain=$1
|
||||||
|
|
||||||
|
if [ ! -f "$SERVICES_YAML" ]; then
|
||||||
|
echo "Warning: services.yaml not found at $SERVICES_YAML"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Updating service URLs in services.yaml..."
|
||||||
|
|
||||||
|
# Create a backup of the services.yaml file
|
||||||
|
local timestamp=$(date +"%Y%m%d_%H%M%S")
|
||||||
|
local backup_file="${SERVICES_YAML}.backup_${timestamp}"
|
||||||
|
cp "$SERVICES_YAML" "$backup_file"
|
||||||
|
echo "Created backup of services.yaml at $backup_file"
|
||||||
|
|
||||||
|
# Update the commented URLs to use the new domain
|
||||||
|
sed -i "s|# href: \"https://code\.changeme\.org\"|# href: \"https://code.$new_domain\"|g" "$SERVICES_YAML"
|
||||||
|
sed -i "s|# href: \"https://listmonk\.changeme\.org\"|# href: \"https://listmonk.$new_domain\"|g" "$SERVICES_YAML"
|
||||||
|
sed -i "s|# href: \"https://db\.changeme\.org\"|# href: \"https://db.$new_domain\"|g" "$SERVICES_YAML"
|
||||||
|
sed -i "s|# href: \"https://docs\.changeme\.org\"|# href: \"https://docs.$new_domain\"|g" "$SERVICES_YAML"
|
||||||
|
sed -i "s|# href: \"https://n8n\.changeme\.org\"|# href: \"https://n8n.$new_domain\"|g" "$SERVICES_YAML"
|
||||||
|
sed -i "s|# href: \"https://test\.com\"|# href: \"https://$new_domain\"|g" "$SERVICES_YAML"
|
||||||
|
|
||||||
|
# Also update any remaining changeme.org references
|
||||||
|
sed -i "s|changeme\.org|$new_domain|g" "$SERVICES_YAML"
|
||||||
|
|
||||||
|
echo "Updated service URLs in services.yaml to use domain: $new_domain"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to update the login URL in main.html
|
||||||
|
update_main_html() {
|
||||||
|
local new_domain=$1
|
||||||
|
|
||||||
|
if [ ! -f "$MAIN_HTML" ]; then
|
||||||
|
echo "Warning: main.html not found at $MAIN_HTML"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Updating login URL in main.html..."
|
||||||
|
|
||||||
|
# Create a backup of the main.html file
|
||||||
|
local timestamp=$(date +"%Y%m%d_%H%M%S")
|
||||||
|
local backup_file="${MAIN_HTML}.backup_${timestamp}"
|
||||||
|
cp "$MAIN_HTML" "$backup_file"
|
||||||
|
echo "Created backup of main.html at $backup_file"
|
||||||
|
|
||||||
|
# Update the login button href to use the new domain
|
||||||
|
sed -i "s|href=\"https://homepage\.test\.com\"|href=\"https://homepage.$new_domain\"|g" "$MAIN_HTML"
|
||||||
|
|
||||||
|
# Also update any other test.com references
|
||||||
|
sed -i "s|homepage\.test\.com|homepage.$new_domain|g" "$MAIN_HTML"
|
||||||
|
|
||||||
|
echo "Updated login URL in main.html to: https://homepage.$new_domain"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to check if a port is in use
|
||||||
|
check_port() {
|
||||||
|
local port=$1
|
||||||
|
if command -v ss >/dev/null 2>&1; then
|
||||||
|
ss -tuln | grep -q ":$port "
|
||||||
|
elif command -v netstat >/dev/null 2>&1; then
|
||||||
|
netstat -tuln | grep -q ":$port "
|
||||||
|
else
|
||||||
|
# Fallback to lsof if available
|
||||||
|
if command -v lsof >/dev/null 2>&1; then
|
||||||
|
lsof -i ":$port" >/dev/null 2>&1
|
||||||
|
else
|
||||||
|
echo "Warning: Cannot check port availability. Please ensure ports are free manually."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to check all service ports for conflicts
|
||||||
|
check_port_conflicts() {
|
||||||
|
echo "Checking for port conflicts..."
|
||||||
|
|
||||||
|
local ports_to_check=(
|
||||||
|
"${CODE_SERVER_PORT:-8888}:Code Server"
|
||||||
|
"${LISTMONK_PORT:-9000}:Listmonk"
|
||||||
|
"${LISTMONK_DB_PORT:-5432}:Listmonk Database"
|
||||||
|
"${MKDOCS_PORT:-4000}:MkDocs"
|
||||||
|
"${MKDOCS_SITE_SERVER_PORT:-4001}:MkDocs Site Server"
|
||||||
|
"${N8N_PORT:-5678}:N8N"
|
||||||
|
"${NOCODB_PORT:-8090}:NocoDB"
|
||||||
|
"${HOMEPAGE_PORT:-3010}:Homepage"
|
||||||
|
"${GITEA_WEB_PORT:-3030}:Gitea Web"
|
||||||
|
"${GITEA_SSH_PORT:-2222}:Gitea SSH"
|
||||||
|
)
|
||||||
|
|
||||||
|
local conflicts_found=false
|
||||||
|
|
||||||
|
for port_info in "${ports_to_check[@]}"; do
|
||||||
|
local port=$(echo "$port_info" | cut -d: -f1)
|
||||||
|
local service=$(echo "$port_info" | cut -d: -f2)
|
||||||
|
|
||||||
|
if check_port "$port"; then
|
||||||
|
echo "⚠️ Port conflict detected: $port is already in use (assigned to $service)"
|
||||||
|
conflicts_found=true
|
||||||
|
else
|
||||||
|
echo "✅ Port $port is available for $service"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "$conflicts_found" = true ]; then
|
||||||
|
echo ""
|
||||||
|
echo "Port conflicts detected! Please choose alternative ports or stop conflicting services."
|
||||||
|
read -p "Do you want to configure alternative ports? [Y/n]: " configure_ports
|
||||||
|
|
||||||
|
if [[ "$configure_ports" =~ ^[Nn]$ ]]; then
|
||||||
|
echo "Configuration cancelled. Please resolve port conflicts and try again."
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
configure_alternative_ports
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "✅ All ports are available!"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to configure alternative ports
|
||||||
|
configure_alternative_ports() {
|
||||||
|
echo ""
|
||||||
|
echo "---- Port Configuration ----"
|
||||||
|
|
||||||
|
# Code Server
|
||||||
|
if check_port "${CODE_SERVER_PORT:-8888}"; then
|
||||||
|
read -p "Enter alternative port for Code Server [current: ${CODE_SERVER_PORT:-8888}]: " new_code_port
|
||||||
|
if [ ! -z "$new_code_port" ]; then
|
||||||
|
update_env_var "CODE_SERVER_PORT" "$new_code_port"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Listmonk
|
||||||
|
if check_port "${LISTMONK_PORT:-9000}"; then
|
||||||
|
read -p "Enter alternative port for Listmonk [current: ${LISTMONK_PORT:-9000}]: " new_listmonk_port
|
||||||
|
if [ ! -z "$new_listmonk_port" ]; then
|
||||||
|
update_env_var "LISTMONK_PORT" "$new_listmonk_port"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Listmonk DB
|
||||||
|
if check_port "${LISTMONK_DB_PORT:-5432}"; then
|
||||||
|
read -p "Enter alternative port for Listmonk Database [current: ${LISTMONK_DB_PORT:-5432}]: " new_db_port
|
||||||
|
if [ ! -z "$new_db_port" ]; then
|
||||||
|
update_env_var "LISTMONK_DB_PORT" "$new_db_port"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# MkDocs
|
||||||
|
if check_port "${MKDOCS_PORT:-4000}"; then
|
||||||
|
read -p "Enter alternative port for MkDocs [current: ${MKDOCS_PORT:-4000}]: " new_mkdocs_port
|
||||||
|
if [ ! -z "$new_mkdocs_port" ]; then
|
||||||
|
update_env_var "MKDOCS_PORT" "$new_mkdocs_port"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# MkDocs Site Server
|
||||||
|
if check_port "${MKDOCS_SITE_SERVER_PORT:-4001}"; then
|
||||||
|
read -p "Enter alternative port for MkDocs Site Server [current: ${MKDOCS_SITE_SERVER_PORT:-4001}]: " new_site_port
|
||||||
|
if [ ! -z "$new_site_port" ]; then
|
||||||
|
update_env_var "MKDOCS_SITE_SERVER_PORT" "$new_site_port"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# N8N
|
||||||
|
if check_port "${N8N_PORT:-5678}"; then
|
||||||
|
read -p "Enter alternative port for N8N [current: ${N8N_PORT:-5678}]: " new_n8n_port
|
||||||
|
if [ ! -z "$new_n8n_port" ]; then
|
||||||
|
update_env_var "N8N_PORT" "$new_n8n_port"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# NocoDB
|
||||||
|
if check_port "${NOCODB_PORT:-8090}"; then
|
||||||
|
read -p "Enter alternative port for NocoDB [current: ${NOCODB_PORT:-8090}]: " new_nocodb_port
|
||||||
|
if [ ! -z "$new_nocodb_port" ]; then
|
||||||
|
update_env_var "NOCODB_PORT" "$new_nocodb_port"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Homepage
|
||||||
|
if check_port "${HOMEPAGE_PORT:-3010}"; then
|
||||||
|
read -p "Enter alternative port for Homepage [current: ${HOMEPAGE_PORT:-3010}]: " new_homepage_port
|
||||||
|
if [ ! -z "$new_homepage_port" ]; then
|
||||||
|
update_env_var "HOMEPAGE_PORT" "$new_homepage_port"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Gitea Web
|
||||||
|
if check_port "${GITEA_WEB_PORT:-3030}"; then
|
||||||
|
read -p "Enter alternative port for Gitea Web [current: ${GITEA_WEB_PORT:-3030}]: " new_gitea_web_port
|
||||||
|
if [ ! -z "$new_gitea_web_port" ]; then
|
||||||
|
update_env_var "GITEA_WEB_PORT" "$new_gitea_web_port"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Gitea SSH
|
||||||
|
if check_port "${GITEA_SSH_PORT:-2222}"; then
|
||||||
|
read -p "Enter alternative port for Gitea SSH [current: ${GITEA_SSH_PORT:-2222}]: " new_gitea_ssh_port
|
||||||
|
if [ ! -z "$new_gitea_ssh_port" ]; then
|
||||||
|
update_env_var "GITEA_SSH_PORT" "$new_gitea_ssh_port"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Port configuration completed."
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to create Cloudflare tunnel configuration file
|
||||||
|
create_tunnel_config() {
|
||||||
|
local domain=$1
|
||||||
|
local tunnel_id=$2
|
||||||
|
|
||||||
|
# Ensure the tunnel config directory exists
|
||||||
|
if [ ! -d "$TUNNEL_CONFIG_DIR" ]; then
|
||||||
|
echo "Creating Cloudflare tunnel config directory at $TUNNEL_CONFIG_DIR"
|
||||||
|
mkdir -p "$TUNNEL_CONFIG_DIR"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Creating Cloudflare tunnel configuration file..."
|
||||||
|
|
||||||
|
# Generate the tunnel configuration file with simpler format
|
||||||
|
cat > "$TUNNEL_CONFIG_FILE" << EOL
|
||||||
|
# filepath: /home/bunker-admin/changemaker.lite/configs/cloudflare/tunnel-config.yml
|
||||||
|
# Cloudflare Tunnel Configuration
|
||||||
|
# Auto-generated by Changemaker Configuration Wizard
|
||||||
|
|
||||||
|
tunnel: $tunnel_id # e.g. 1234567890abcdef
|
||||||
|
credentials-file: /home/coder/.cloudflared/$tunnel_id.json # e.g. /home/coder/.cloudflared/[insert tunnel number].json
|
||||||
|
ingress:
|
||||||
|
|
||||||
|
- hostname: homepage.$domain
|
||||||
|
service: http://localhost:${HOMEPAGE_PORT:-3010}
|
||||||
|
|
||||||
|
- hostname: code.$domain
|
||||||
|
service: http://localhost:${CODE_SERVER_PORT:-8888}
|
||||||
|
|
||||||
|
- hostname: listmonk.$domain
|
||||||
|
service: http://localhost:${LISTMONK_PORT:-9000}
|
||||||
|
|
||||||
|
- hostname: docs.$domain
|
||||||
|
service: http://localhost:${MKDOCS_PORT:-4000}
|
||||||
|
|
||||||
|
- hostname: $domain
|
||||||
|
service: http://localhost:${MKDOCS_SITE_SERVER_PORT:-4001}
|
||||||
|
|
||||||
|
- hostname: n8n.$domain
|
||||||
|
service: http://localhost:${N8N_PORT:-5678}
|
||||||
|
|
||||||
|
- hostname: db.$domain
|
||||||
|
service: http://localhost:${NOCODB_PORT:-8090}
|
||||||
|
|
||||||
|
- hostname: git.$domain
|
||||||
|
service: http://localhost:${GITEA_WEB_PORT:-3030}
|
||||||
|
|
||||||
|
# Catch-all rule (required)
|
||||||
|
- service: http_status:404
|
||||||
|
EOL
|
||||||
|
|
||||||
|
echo "✅ Tunnel configuration file created at: $TUNNEL_CONFIG_FILE"
|
||||||
|
|
||||||
|
# If the tunnel ID is a placeholder, provide instructions
|
||||||
|
if [[ "$tunnel_id" == "\${CF_TUNNEL_ID}" ]]; then
|
||||||
|
echo "NOTE: You need to replace ${CF_TUNNEL_ID} with your actual Cloudflare Tunnel ID"
|
||||||
|
echo " once you create a tunnel in the Cloudflare Zero Trust dashboard."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to show tunnel setup instructions
|
||||||
|
show_tunnel_instructions() {
|
||||||
|
local domain=$1
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== Cloudflare Tunnel Setup Instructions ==="
|
||||||
|
echo ""
|
||||||
|
echo "To complete the tunnel setup:"
|
||||||
|
echo ""
|
||||||
|
echo "1. Install cloudflared on your server:"
|
||||||
|
echo " https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/installation"
|
||||||
|
echo ""
|
||||||
|
echo "2. Create a tunnel using the Cloudflare dashboard or run:"
|
||||||
|
echo " cloudflared tunnel create changemaker-tunnel"
|
||||||
|
echo ""
|
||||||
|
echo "3. Copy the credentials file to the correct location:"
|
||||||
|
echo " mkdir -p /home/coder/.cloudflared"
|
||||||
|
echo " cp ~/.cloudflared/[TUNNEL-ID].json /home/coder/.cloudflared/"
|
||||||
|
echo ""
|
||||||
|
echo "4. Update your .env with the correct CF_TUNNEL_ID if not already done"
|
||||||
|
echo ""
|
||||||
|
echo "5. Start the tunnel with your configuration file:"
|
||||||
|
echo " cloudflared tunnel --config $TUNNEL_CONFIG_FILE run"
|
||||||
|
echo ""
|
||||||
|
echo "6. After verifying it works, you can create a systemd service for automatic startup:"
|
||||||
|
echo " sudo cloudflared service install"
|
||||||
|
echo ""
|
||||||
|
echo "7. Your services will be available at the following URLs:"
|
||||||
|
echo " - Documentation: https://$domain"
|
||||||
|
echo " - Homepage: https://homepage.$domain"
|
||||||
|
echo " - Code Server: https://code.$domain"
|
||||||
|
echo " - Listmonk: https://listmonk.$domain"
|
||||||
|
echo " - N8N: https://n8n.$domain"
|
||||||
|
echo " - NocoDB: https://db.$domain"
|
||||||
|
echo " - MkDocs Dev: https://docs.$domain"
|
||||||
|
echo " - Gitea: https://git.$domain"
|
||||||
|
echo ""
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to load environment variables from .env file
|
||||||
|
load_env_vars() {
|
||||||
|
if [ -f "$ENV_FILE" ]; then
|
||||||
|
# Load variables from .env file, ignoring comments and empty lines
|
||||||
|
while IFS= read -r line; do
|
||||||
|
if [[ "$line" =~ ^[A-Za-z_][A-Za-z0-9_]*= ]]; then
|
||||||
|
export "$line"
|
||||||
|
fi
|
||||||
|
done < <(grep -E '^[A-Za-z_][A-Za-z0-9_]*=' "$ENV_FILE")
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Initialize a new .env file if it doesn't exist
|
||||||
|
if [ ! -f "$ENV_FILE" ]; then
|
||||||
|
echo "No .env file found. Creating a new one from scratch."
|
||||||
|
touch "$ENV_FILE"
|
||||||
|
initialize_env_file
|
||||||
|
else
|
||||||
|
echo "Found existing .env file. Will update values."
|
||||||
|
backup_env_file
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Load existing environment variables
|
||||||
|
load_env_vars
|
||||||
|
|
||||||
|
echo -e "\n\nWelcome to Changemaker Config!\n"
|
||||||
|
echo "This script will help you configure your .env file for Changemaker."
|
||||||
|
echo "Please provide the following information:"
|
||||||
|
|
||||||
|
# Domain configuration
|
||||||
|
read -p "Enter your domain name (without protocol, e.g., example.com): " domain_name
|
||||||
|
|
||||||
|
if [ -z "$domain_name" ]; then
|
||||||
|
echo "Domain name cannot be empty. Using default: changeme.org"
|
||||||
|
domain_name="changeme.org"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "\nUpdating domain settings in .env file..."
|
||||||
|
|
||||||
|
# Update main domain settings
|
||||||
|
update_env_var "DOMAIN" "$domain_name"
|
||||||
|
update_env_var "BASE_DOMAIN" "https://$domain_name"
|
||||||
|
update_env_var "HOMEPAGE_VAR_BASE_URL" "https://$domain_name"
|
||||||
|
update_env_var "LISTMONK_HOSTNAME" "listmonk.$domain_name"
|
||||||
|
update_env_var "N8N_HOST" "n8n.$domain_name"
|
||||||
|
update_env_var "CF_DOMAIN" "$domain_name"
|
||||||
|
update_env_var "GITEA_DOMAIN" "git.$domain_name"
|
||||||
|
update_env_var "GITEA_ROOT_URL" "https://git.$domain_name"
|
||||||
|
|
||||||
|
echo "Domain settings updated successfully!"
|
||||||
|
|
||||||
|
# Cloudflare Configuration
|
||||||
|
echo -e "\n---- Cloudflare Configuration ----"
|
||||||
|
echo "To use the DNS setup script, you'll need Cloudflare credentials."
|
||||||
|
echo "You can find these values in your Cloudflare dashboard:"
|
||||||
|
echo " - API Token: https://dash.cloudflare.com/profile/api-tokens"
|
||||||
|
echo " (Create a token with Zone:DNS:Edit and Access:Apps:Edit permissions)"
|
||||||
|
echo " - Zone ID: On your domain's overview page"
|
||||||
|
echo " - Tunnel ID: In the Zero Trust dashboard under Access > Tunnels"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
read -p "Do you want to configure Cloudflare settings now? [Y/n]: " configure_cf
|
||||||
|
|
||||||
|
if [[ ! "$configure_cf" =~ ^[Nn]$ ]]; then
|
||||||
|
echo ""
|
||||||
|
echo "Please enter your Cloudflare credentials:"
|
||||||
|
|
||||||
|
# CF API Token
|
||||||
|
read -p "Enter your Cloudflare API Token: " cf_api_token
|
||||||
|
if [ ! -z "$cf_api_token" ]; then
|
||||||
|
# Basic validation for API token format
|
||||||
|
if [[ "$cf_api_token" =~ ^[A-Za-z0-9_-]{40}$ ]]; then
|
||||||
|
update_env_var "CF_API_TOKEN" "$cf_api_token"
|
||||||
|
echo "✅ Cloudflare API Token updated"
|
||||||
|
else
|
||||||
|
echo "⚠️ Warning: API Token format seems incorrect (should be 40 characters)"
|
||||||
|
update_env_var "CF_API_TOKEN" "$cf_api_token"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "⚠️ Cloudflare API Token left unchanged"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# CF Zone ID
|
||||||
|
read -p "Enter your Cloudflare Zone ID: " cf_zone_id
|
||||||
|
if [ ! -z "$cf_zone_id" ]; then
|
||||||
|
# Basic validation for Zone ID format
|
||||||
|
if [[ "$cf_zone_id" =~ ^[a-f0-9]{32}$ ]]; then
|
||||||
|
update_env_var "CF_ZONE_ID" "$cf_zone_id"
|
||||||
|
echo "✅ Cloudflare Zone ID updated"
|
||||||
|
else
|
||||||
|
echo "⚠️ Warning: Zone ID format seems incorrect (should be 32 hex characters)"
|
||||||
|
update_env_var "CF_ZONE_ID" "$cf_zone_id"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "⚠️ Cloudflare Zone ID left unchanged"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# CF Tunnel ID
|
||||||
|
read -p "Enter your Cloudflare Tunnel ID: " cf_tunnel_id
|
||||||
|
if [ ! -z "$cf_tunnel_id" ]; then
|
||||||
|
# Basic validation for Tunnel ID format (UUID)
|
||||||
|
if [[ "$cf_tunnel_id" =~ ^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$ ]]; then
|
||||||
|
update_env_var "CF_TUNNEL_ID" "$cf_tunnel_id"
|
||||||
|
echo "✅ Cloudflare Tunnel ID updated"
|
||||||
|
|
||||||
|
# Create tunnel configuration with the provided tunnel ID
|
||||||
|
create_tunnel_config "$domain_name" "$cf_tunnel_id"
|
||||||
|
else
|
||||||
|
echo "⚠️ Warning: Tunnel ID format seems incorrect (should be UUID format)"
|
||||||
|
update_env_var "CF_TUNNEL_ID" "$cf_tunnel_id"
|
||||||
|
|
||||||
|
# Still create the config file even with potentially incorrect format
|
||||||
|
create_tunnel_config "$domain_name" "$cf_tunnel_id"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "⚠️ Cloudflare Tunnel ID left unchanged"
|
||||||
|
# Create template config without tunnel ID
|
||||||
|
create_tunnel_config "$domain_name" "\${CF_TUNNEL_ID}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Cloudflare configuration completed!"
|
||||||
|
echo "You can now run './add-cname-records.sh' to set up DNS records."
|
||||||
|
|
||||||
|
# Show tunnel setup instructions
|
||||||
|
show_tunnel_instructions "$domain_name"
|
||||||
|
else
|
||||||
|
echo "Skipping Cloudflare configuration. You can run this script again later to configure it."
|
||||||
|
# Still create a template tunnel config
|
||||||
|
echo ""
|
||||||
|
read -p "Do you want to create a template tunnel configuration anyway? [Y/n]: " create_template
|
||||||
|
if [[ ! "$create_template" =~ ^[Nn]$ ]]; then
|
||||||
|
create_tunnel_config "$domain_name" "\${CF_TUNNEL_ID}"
|
||||||
|
echo "Template tunnel configuration created. Update CF_TUNNEL_ID in .env and regenerate if needed."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Update the site_url in mkdocs.yml
|
||||||
|
echo -e "\nUpdating site_url in mkdocs.yml..."
|
||||||
|
update_mkdocs_yml "$domain_name"
|
||||||
|
|
||||||
|
# Update service URLs in services.yaml
|
||||||
|
echo -e "\nUpdating service URLs in services.yaml..."
|
||||||
|
update_services_yaml "$domain_name"
|
||||||
|
|
||||||
|
# Update the login URL in main.html
|
||||||
|
echo -e "\nUpdating login URL in main.html..."
|
||||||
|
update_main_html "$domain_name"
|
||||||
|
|
||||||
|
# Listmonk Admin Credentials configuration
|
||||||
|
echo -e "\n---- Listmonk Admin Credentials ----"
|
||||||
|
read -p "Enter Listmonk admin email/username [default: admin@example.com]: " listmonk_user
|
||||||
|
read -sp "Enter Listmonk admin password [default: changeMe]: " listmonk_password
|
||||||
|
echo # Add new line after password input
|
||||||
|
|
||||||
|
if [ -z "$listmonk_user" ]; then
|
||||||
|
listmonk_user="admin@example.com"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$listmonk_password" ]; then
|
||||||
|
listmonk_password="changeMe"
|
||||||
|
fi
|
||||||
|
|
||||||
|
update_env_var "LISTMONK_ADMIN_USER" "$listmonk_user"
|
||||||
|
update_env_var "LISTMONK_ADMIN_PASSWORD" "$listmonk_password"
|
||||||
|
|
||||||
|
echo "Listmonk admin credentials updated."
|
||||||
|
|
||||||
|
# N8N User Credentials configuration
|
||||||
|
echo -e "\n---- N8N Admin Credentials ----"
|
||||||
|
read -p "Enter N8N admin email [default: admin@example.com]: " n8n_email
|
||||||
|
read -sp "Enter N8N admin password [default: changeMe]: " n8n_password
|
||||||
|
echo # Add new line after password input
|
||||||
|
|
||||||
|
if [ -z "$n8n_email" ]; then
|
||||||
|
n8n_email="admin@example.com"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$n8n_password" ]; then
|
||||||
|
n8n_password="changeMe"
|
||||||
|
fi
|
||||||
|
|
||||||
|
update_env_var "N8N_USER_EMAIL" "$n8n_email"
|
||||||
|
update_env_var "N8N_USER_PASSWORD" "$n8n_password"
|
||||||
|
|
||||||
|
echo "N8N admin credentials updated."
|
||||||
|
|
||||||
|
# Generate secure passwords for database and encryption
|
||||||
|
echo -e "\n---- Generating Secure Passwords ----"
|
||||||
|
echo "Generating secure passwords for database and encryption keys..."
|
||||||
|
|
||||||
|
# Generate and update database password
|
||||||
|
postgres_password=$(generate_password 20)
|
||||||
|
update_env_var "POSTGRES_PASSWORD" "$postgres_password"
|
||||||
|
|
||||||
|
# Generate and update N8N encryption key
|
||||||
|
n8n_encryption_key=$(generate_password 32)
|
||||||
|
update_env_var "N8N_ENCRYPTION_KEY" "$n8n_encryption_key"
|
||||||
|
|
||||||
|
# Generate and update NocoDB passwords
|
||||||
|
nocodb_jwt_secret=$(generate_password 32)
|
||||||
|
update_env_var "NOCODB_JWT_SECRET" "$nocodb_jwt_secret"
|
||||||
|
|
||||||
|
nocodb_db_password=$(generate_password 20)
|
||||||
|
update_env_var "NOCODB_DB_PASSWORD" "$nocodb_db_password"
|
||||||
|
|
||||||
|
# Generate and update Gitea passwords
|
||||||
|
gitea_db_password=$(generate_password 20)
|
||||||
|
update_env_var "GITEA_DB_PASSWD" "$gitea_db_password"
|
||||||
|
|
||||||
|
gitea_db_root_password=$(generate_password 20)
|
||||||
|
update_env_var "GITEA_DB_ROOT_PASSWORD" "$gitea_db_root_password"
|
||||||
|
|
||||||
|
echo "Secure passwords generated and updated."
|
||||||
|
|
||||||
|
echo -e "\n✅ Configuration completed successfully!"
|
||||||
|
echo "Your .env file has been configured with:"
|
||||||
|
echo "- Domain: $domain_name"
|
||||||
|
echo "- Listmonk Admin: $listmonk_user"
|
||||||
|
echo "- N8N Admin Email: $n8n_email"
|
||||||
|
echo "- Secure random passwords for database, encryption, and NocoDB"
|
||||||
|
if [[ ! "$configure_cf" =~ ^[Nn]$ ]]; then
|
||||||
|
echo "- Cloudflare credentials for DNS management"
|
||||||
|
fi
|
||||||
|
echo -e "\nYour .env file is located at: $ENV_FILE"
|
||||||
|
echo "A backup of your original .env file was created before modifications."
|
||||||
|
echo -e "\nNext steps:"
|
||||||
|
echo "1. Run 'docker-compose up -d' to start your services"
|
||||||
|
if [[ ! "$configure_cf" =~ ^[Nn]$ ]]; then
|
||||||
|
echo "2. Run './add-cname-records.sh' to set up DNS records and access policies"
|
||||||
|
fi
|
35
configs/cloudflare/tunnel-config.yml
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
# filepath: /home/bunker-admin/ab.dem.tf.changemaker/configs/cloudflare/tunnel-config.yml
|
||||||
|
# filepath: /home/bunker-admin/changemaker.lite/configs/cloudflare/tunnel-config.yml
|
||||||
|
# Cloudflare Tunnel Configuration
|
||||||
|
# Auto-generated by Changemaker Configuration Wizard
|
||||||
|
|
||||||
|
tunnel: dce04dd0-1165-4d35-ad0a-880322e36c83 # e.g. 1234567890abcdef
|
||||||
|
credentials-file: /home/bunker-admin/.cloudflared/dce04dd0-1165-4d35-ad0a-880322e36c83.json # e.g. /home/coder/.cloudflared/[insert tunnel number].json
|
||||||
|
ingress:
|
||||||
|
|
||||||
|
- hostname: homepage.albertademocracytaskforce.org
|
||||||
|
service: http://localhost:3021
|
||||||
|
|
||||||
|
- hostname: code.albertademocracytaskforce.org
|
||||||
|
service: http://localhost:8889
|
||||||
|
|
||||||
|
- hostname: listmonk.albertademocracytaskforce.org
|
||||||
|
service: http://localhost:9001
|
||||||
|
|
||||||
|
- hostname: docs.albertademocracytaskforce.org
|
||||||
|
service: http://localhost:4050
|
||||||
|
|
||||||
|
- hostname: albertademocracytaskforce.org
|
||||||
|
service: http://localhost:4051
|
||||||
|
|
||||||
|
- hostname: n8n.albertademocracytaskforce.org
|
||||||
|
service: http://localhost:5679
|
||||||
|
|
||||||
|
- hostname: db.albertademocracytaskforce.org
|
||||||
|
service: http://localhost:8091
|
||||||
|
|
||||||
|
- hostname: git.albertademocracytaskforce.org
|
||||||
|
service: http://localhost:3034
|
||||||
|
|
||||||
|
# Catch-all rule (required)
|
||||||
|
- service: http_status:404
|
0
configs/code-server/.config/.gitkeep
Normal file
0
configs/code-server/.local/.gitkeep
Normal file
53
configs/homepage/bookmarks.yaml
Executable file
@ -0,0 +1,53 @@
|
|||||||
|
---
|
||||||
|
# For configuration options and examples, please see:
|
||||||
|
# https://gethomepage.dev/configs/bookmarks
|
||||||
|
|
||||||
|
- Documentation:
|
||||||
|
- Code Server:
|
||||||
|
- abbr: CS
|
||||||
|
href: https://github.com/coder/code-server
|
||||||
|
- MkDocs Material:
|
||||||
|
- abbr: MD
|
||||||
|
href: https://squidfunk.github.io/mkdocs-material/
|
||||||
|
- Homepage:
|
||||||
|
- abbr: HP
|
||||||
|
href: https://gethomepage.dev/
|
||||||
|
- Gitea:
|
||||||
|
- abbr: GT
|
||||||
|
href: https://docs.gitea.io/
|
||||||
|
|
||||||
|
- Services:
|
||||||
|
- Listmonk:
|
||||||
|
- abbr: LM
|
||||||
|
href: https://listmonk.app/docs/
|
||||||
|
- NocoDB:
|
||||||
|
- abbr: NC
|
||||||
|
href: https://docs.nocodb.com/
|
||||||
|
- n8n:
|
||||||
|
- abbr: N8
|
||||||
|
href: https://docs.n8n.io/
|
||||||
|
- PostgreSQL:
|
||||||
|
- abbr: PG
|
||||||
|
href: https://www.postgresql.org/docs/
|
||||||
|
- Gitea:
|
||||||
|
- abbr: GT
|
||||||
|
href: https://gitea.io/
|
||||||
|
|
||||||
|
- Resources:
|
||||||
|
- Docker:
|
||||||
|
- abbr: DC
|
||||||
|
href: https://docs.docker.com/
|
||||||
|
- Docker Compose:
|
||||||
|
- abbr: DCC
|
||||||
|
href: https://docs.docker.com/compose/
|
||||||
|
- Nginx:
|
||||||
|
- abbr: NG
|
||||||
|
href: https://nginx.org/en/docs/
|
||||||
|
|
||||||
|
- Development:
|
||||||
|
- GitHub:
|
||||||
|
- abbr: GH
|
||||||
|
href: https://github.com/
|
||||||
|
- Stack Overflow:
|
||||||
|
- abbr: SO
|
||||||
|
href: https://stackoverflow.com/
|
0
configs/homepage/custom.css
Executable file
0
configs/homepage/custom.js
Executable file
6
configs/homepage/docker.yaml
Executable file
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
# For configuration options and examples, please see:
|
||||||
|
# https://gethomepage.dev/configs/docker/
|
||||||
|
|
||||||
|
my-docker:
|
||||||
|
socket: /var/run/docker.sock
|
2
configs/homepage/kubernetes.yaml
Executable file
@ -0,0 +1,2 @@
|
|||||||
|
---
|
||||||
|
# sample kubernetes config
|
1224
configs/homepage/logs/homepage.log
Normal file
80
configs/homepage/services.yaml
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
---
|
||||||
|
# For public access, replace "http://localhost" with your subdomain URLs
|
||||||
|
|
||||||
|
- Essential Tools:
|
||||||
|
- Code Server:
|
||||||
|
href: "https://code.albertademocracytaskforce.org" # Uncomment for public access
|
||||||
|
description: VS Code in the browser
|
||||||
|
icon: mdi-code-braces
|
||||||
|
widget:
|
||||||
|
type: docker
|
||||||
|
container: code-server-changemaker-lite
|
||||||
|
server: my-docker
|
||||||
|
- Listmonk:
|
||||||
|
href: "https://listmonk.albertademocracytaskforce.org" # Uncomment for public access
|
||||||
|
description: Newsletter & mailing list manager
|
||||||
|
icon: mdi-email-newsletter
|
||||||
|
widget:
|
||||||
|
type: docker
|
||||||
|
container: listmonk_app-lite
|
||||||
|
server: my-docker
|
||||||
|
- NocoDB:
|
||||||
|
href: "https://db.albertademocracytaskforce.org" # Uncomment for public access
|
||||||
|
description: No-code database platform
|
||||||
|
icon: mdi-database
|
||||||
|
widget:
|
||||||
|
type: docker
|
||||||
|
container: ab.dem.tf.changemaker-nocodb-1
|
||||||
|
server: my-docker
|
||||||
|
- Gitea:
|
||||||
|
href: "https://git.albertademocracytaskforce.org" # Uncomment for public access
|
||||||
|
description: Git repository hosting
|
||||||
|
icon: mdi-git
|
||||||
|
widget:
|
||||||
|
type: docker
|
||||||
|
container: gitea_changemaker-lite
|
||||||
|
server: my-docker
|
||||||
|
|
||||||
|
- Content & Documentation:
|
||||||
|
- MkDocs (Live):
|
||||||
|
href: "https://docs.albertademocracytaskforce.org" # Uncomment for public access
|
||||||
|
description: Live documentation server with hot reload
|
||||||
|
icon: mdi-book-open-page-variant
|
||||||
|
widget:
|
||||||
|
type: docker
|
||||||
|
container: mkdocs-changemaker-lite
|
||||||
|
server: my-docker
|
||||||
|
- Static Site:
|
||||||
|
href: "https://albertademocracytaskforce.org" # Uncomment for public access
|
||||||
|
description: Built documentation hosting
|
||||||
|
icon: mdi-web
|
||||||
|
widget:
|
||||||
|
type: docker
|
||||||
|
container: mkdocs-site-server-changemaker-lite
|
||||||
|
server: my-docker
|
||||||
|
|
||||||
|
- Automation & Infrastructure:
|
||||||
|
- n8n:
|
||||||
|
href: "https://n8n.albertademocracytaskforce.org" # Uncomment for public access
|
||||||
|
description: Workflow automation platform
|
||||||
|
icon: mdi-workflow
|
||||||
|
widget:
|
||||||
|
type: docker
|
||||||
|
container: n8n-changemaker-lite
|
||||||
|
server: my-docker
|
||||||
|
- PostgreSQL (Listmonk):
|
||||||
|
href: "#"
|
||||||
|
description: Database for Listmonk
|
||||||
|
icon: mdi-database-outline
|
||||||
|
widget:
|
||||||
|
type: docker
|
||||||
|
container: listmonk_db-lite
|
||||||
|
server: my-docker
|
||||||
|
- PostgreSQL (NocoDB):
|
||||||
|
href: "#"
|
||||||
|
description: Database for NocoDB
|
||||||
|
icon: mdi-database-outline
|
||||||
|
widget:
|
||||||
|
type: docker
|
||||||
|
container: ab.dem.tf.changemaker-root_db-1
|
||||||
|
server: my-docker
|
41
configs/homepage/settings.yaml
Executable file
@ -0,0 +1,41 @@
|
|||||||
|
---
|
||||||
|
# For configuration options and examples, please see:
|
||||||
|
# https://gethomepage.dev/configs/settings/
|
||||||
|
|
||||||
|
title: Changemaker Lite
|
||||||
|
description: Self-hosted platform for documentation, development, and automation
|
||||||
|
theme: dark # or light
|
||||||
|
color: purple
|
||||||
|
|
||||||
|
background:
|
||||||
|
image: /images/background.png
|
||||||
|
blur: xs # sm, "", md, xl... see https://tailwindcss.com/docs/backdrop-blur
|
||||||
|
saturate: 100 # 0, 50, 100... see https://tailwindcss.com/docs/backdrop-saturate
|
||||||
|
brightness: 75 # 0, 50, 75... see https://tailwindcss.com/docs/backdrop-brightness
|
||||||
|
opacity: 50 # 0-100
|
||||||
|
|
||||||
|
cardBlur: xl # xs, md,
|
||||||
|
headerStyle: boxed
|
||||||
|
|
||||||
|
layout:
|
||||||
|
style: columns
|
||||||
|
columns: 3
|
||||||
|
|
||||||
|
quicklaunch:
|
||||||
|
searchDescriptions: true
|
||||||
|
hideInternetSearch: true
|
||||||
|
showSearchSuggestions: true
|
||||||
|
hideVisitURL: true
|
||||||
|
provider: duckduckgo
|
||||||
|
|
||||||
|
showStats: true
|
||||||
|
|
||||||
|
bookmarks:
|
||||||
|
showCategories: true
|
||||||
|
showIcons: true
|
||||||
|
target: _blank
|
||||||
|
columns: 3
|
||||||
|
pinned:
|
||||||
|
- Essential Tools:Code Server
|
||||||
|
- Content & Documentation:MkDocs (Live)
|
||||||
|
- Automation & Infrastructure:n8n
|
24
configs/homepage/widgets.yaml
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
# For configuration options and examples, please see:
|
||||||
|
# https://gethomepage.dev/configs/info-widgets/
|
||||||
|
|
||||||
|
- resources:
|
||||||
|
cpu: true
|
||||||
|
memory: true
|
||||||
|
disk: /
|
||||||
|
|
||||||
|
- greeting:
|
||||||
|
text_size: xl
|
||||||
|
text: "Welcome to Alberta Democracy Taskforce Changemaker Lite"
|
||||||
|
|
||||||
|
- datetime:
|
||||||
|
text_size: xl
|
||||||
|
format:
|
||||||
|
dateStyle: short
|
||||||
|
timeStyle: short
|
||||||
|
hour12: true
|
||||||
|
|
||||||
|
- search:
|
||||||
|
provider: duckduckgo
|
||||||
|
target: _blank
|
||||||
|
|
243
docker-compose.yml
Normal file
@ -0,0 +1,243 @@
|
|||||||
|
x-db-credentials: &db-credentials
|
||||||
|
POSTGRES_USER: &db-user ${POSTGRES_USER}
|
||||||
|
POSTGRES_PASSWORD: &db-password ${POSTGRES_PASSWORD}
|
||||||
|
POSTGRES_DB: &db-name ${POSTGRES_DB}
|
||||||
|
|
||||||
|
services:
|
||||||
|
code-server:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile.code-server
|
||||||
|
container_name: code-server-changemaker-lite
|
||||||
|
environment:
|
||||||
|
- DOCKER_USER=${USER_NAME:-coder}
|
||||||
|
- DEFAULT_WORKSPACE=/home/coder/mkdocs/
|
||||||
|
user: "${USER_ID:-1000}:${GROUP_ID:-1000}"
|
||||||
|
volumes:
|
||||||
|
- ./configs/code-server/.config:/home/coder/.config
|
||||||
|
- ./configs/code-server/.local:/home/coder/.local
|
||||||
|
- ./mkdocs:/home/coder/mkdocs/
|
||||||
|
ports:
|
||||||
|
- "${CODE_SERVER_PORT:-8888}:8080"
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- changemaker-lite
|
||||||
|
|
||||||
|
listmonk-app:
|
||||||
|
image: listmonk/listmonk:latest
|
||||||
|
container_name: listmonk_app-lite
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "${LISTMONK_PORT:-9000}:9000"
|
||||||
|
networks:
|
||||||
|
- changemaker-lite
|
||||||
|
hostname: ${LISTMONK_HOSTNAME}
|
||||||
|
depends_on:
|
||||||
|
- listmonk-db
|
||||||
|
command: [sh, -c, "./listmonk --install --idempotent --yes --config '' && ./listmonk --upgrade --yes --config '' && ./listmonk --config ''"]
|
||||||
|
environment:
|
||||||
|
LISTMONK_app__address: 0.0.0.0:9000
|
||||||
|
LISTMONK_db__user: *db-user
|
||||||
|
LISTMONK_db__password: *db-password
|
||||||
|
LISTMONK_db__database: *db-name
|
||||||
|
LISTMONK_db__host: listmonk-db
|
||||||
|
LISTMONK_db__port: 5432
|
||||||
|
LISTMONK_db__ssl_mode: disable
|
||||||
|
LISTMONK_db__max_open: 25
|
||||||
|
LISTMONK_db__max_idle: 25
|
||||||
|
LISTMONK_db__max_lifetime: 300s
|
||||||
|
TZ: Etc/UTC
|
||||||
|
LISTMONK_ADMIN_USER: ${LISTMONK_ADMIN_USER:-}
|
||||||
|
LISTMONK_ADMIN_PASSWORD: ${LISTMONK_ADMIN_PASSWORD:-}
|
||||||
|
volumes:
|
||||||
|
- ./assets/uploads:/listmonk/uploads:rw
|
||||||
|
|
||||||
|
listmonk-db:
|
||||||
|
image: postgres:17-alpine
|
||||||
|
container_name: listmonk_db-lite
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "127.0.0.1:${LISTMONK_DB_PORT:-5432}:5432"
|
||||||
|
networks:
|
||||||
|
- changemaker-lite
|
||||||
|
environment:
|
||||||
|
<<: *db-credentials
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 6
|
||||||
|
volumes:
|
||||||
|
- type: volume
|
||||||
|
source: listmonk-data
|
||||||
|
target: /var/lib/postgresql/data
|
||||||
|
|
||||||
|
mkdocs:
|
||||||
|
image: squidfunk/mkdocs-material
|
||||||
|
container_name: mkdocs-changemaker-lite
|
||||||
|
volumes:
|
||||||
|
- ./mkdocs:/docs:rw
|
||||||
|
- ./assets/images:/docs/assets/images:rw
|
||||||
|
user: "${USER_ID:-1000}:${GROUP_ID:-1000}"
|
||||||
|
ports:
|
||||||
|
- "${MKDOCS_PORT:-4000}:8000"
|
||||||
|
environment:
|
||||||
|
- SITE_URL=${BASE_DOMAIN:-https://changeme.org}
|
||||||
|
command: serve --dev-addr=0.0.0.0:8000 --watch-theme --livereload
|
||||||
|
networks:
|
||||||
|
- changemaker-lite
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
mkdocs-site-server:
|
||||||
|
image: lscr.io/linuxserver/nginx:latest
|
||||||
|
container_name: mkdocs-site-server-changemaker-lite
|
||||||
|
environment:
|
||||||
|
- PUID=${USER_ID:-1000} # Uses USER_ID from your .env file, defaults to 1000
|
||||||
|
- PGID=${GROUP_ID:-1000} # Uses GROUP_ID from your .env file, defaults to 1000
|
||||||
|
- TZ=Etc/UTC
|
||||||
|
volumes:
|
||||||
|
- ./mkdocs/site:/config/www # Mounts your static site to Nginx's web root
|
||||||
|
ports:
|
||||||
|
- "${MKDOCS_SITE_SERVER_PORT:-4001}:80" # Exposes Nginx's port 80 to host port 4001
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- changemaker-lite
|
||||||
|
|
||||||
|
n8n:
|
||||||
|
image: docker.n8n.io/n8nio/n8n
|
||||||
|
container_name: n8n-changemaker-lite
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "${N8N_PORT:-5678}:5678"
|
||||||
|
environment:
|
||||||
|
- N8N_HOST=${N8N_HOST:-n8n.${DOMAIN}}
|
||||||
|
- N8N_PORT=5678
|
||||||
|
- N8N_PROTOCOL=https
|
||||||
|
- NODE_ENV=production
|
||||||
|
- WEBHOOK_URL=https://${N8N_HOST:-n8n.${DOMAIN}}/
|
||||||
|
- GENERIC_TIMEZONE=${GENERIC_TIMEZONE:-UTC}
|
||||||
|
- N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY:-changeMe}
|
||||||
|
- N8N_USER_MANAGEMENT_DISABLED=false
|
||||||
|
- N8N_DEFAULT_USER_EMAIL=${N8N_USER_EMAIL:-admin@example.com}
|
||||||
|
- N8N_DEFAULT_USER_PASSWORD=${N8N_USER_PASSWORD:-changeMe}
|
||||||
|
volumes:
|
||||||
|
- n8n_data:/home/node/.n8n
|
||||||
|
- ./local-files:/files
|
||||||
|
networks:
|
||||||
|
- changemaker-lite
|
||||||
|
|
||||||
|
nocodb:
|
||||||
|
depends_on:
|
||||||
|
root_db:
|
||||||
|
condition: service_healthy
|
||||||
|
environment:
|
||||||
|
NC_DB: "pg://root_db:5432?u=postgres&p=password&d=root_db"
|
||||||
|
image: "nocodb/nocodb:latest"
|
||||||
|
ports:
|
||||||
|
- "${NOCODB_PORT:-8090}:8080"
|
||||||
|
restart: always
|
||||||
|
volumes:
|
||||||
|
- "nc_data:/usr/app/data"
|
||||||
|
networks:
|
||||||
|
- changemaker-lite
|
||||||
|
root_db:
|
||||||
|
environment:
|
||||||
|
POSTGRES_DB: root_db
|
||||||
|
POSTGRES_PASSWORD: password
|
||||||
|
POSTGRES_USER: postgres
|
||||||
|
healthcheck:
|
||||||
|
interval: 10s
|
||||||
|
retries: 10
|
||||||
|
test: "pg_isready -U \"$$POSTGRES_USER\" -d \"$$POSTGRES_DB\""
|
||||||
|
timeout: 2s
|
||||||
|
image: postgres:16.6
|
||||||
|
restart: always
|
||||||
|
volumes:
|
||||||
|
- "db_data:/var/lib/postgresql/data"
|
||||||
|
networks:
|
||||||
|
- changemaker-lite
|
||||||
|
|
||||||
|
# Homepage App
|
||||||
|
homepage-changemaker:
|
||||||
|
image: ghcr.io/gethomepage/homepage:latest
|
||||||
|
container_name: homepage-changemaker-lite
|
||||||
|
ports:
|
||||||
|
- "${HOMEPAGE_PORT:-3010}:3000"
|
||||||
|
volumes:
|
||||||
|
- ./configs/homepage:/app/config
|
||||||
|
- ./assets/icons:/app/public/icons
|
||||||
|
- ./assets/images:/app/public/images
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
environment:
|
||||||
|
- PUID=${USER_ID:-1000}
|
||||||
|
- PGID=${DOCKER_GROUP_ID:-984}
|
||||||
|
- TZ=Etc/UTC
|
||||||
|
- HOMEPAGE_ALLOWED_HOSTS=*
|
||||||
|
- HOMEPAGE_VAR_BASE_URL=${HOMEPAGE_VAR_BASE_URL:-http://localhost}
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- changemaker-lite
|
||||||
|
|
||||||
|
# Gitea - Git service
|
||||||
|
gitea-app:
|
||||||
|
image: gitea/gitea:1.23.7
|
||||||
|
container_name: gitea_changemaker-lite
|
||||||
|
environment:
|
||||||
|
- USER_UID=${USER_ID:-1000}
|
||||||
|
- USER_GID=${GROUP_ID:-1000}
|
||||||
|
- GITEA__database__DB_TYPE=${GITEA_DB_TYPE:-mysql}
|
||||||
|
- GITEA__database__HOST=${GITEA_DB_HOST:-gitea-db:3306}
|
||||||
|
- GITEA__database__NAME=${GITEA_DB_NAME:-gitea}
|
||||||
|
- GITEA__database__USER=${GITEA_DB_USER:-gitea}
|
||||||
|
- GITEA__database__PASSWD=${GITEA_DB_PASSWD}
|
||||||
|
- GITEA__server__ROOT_URL=${GITEA_ROOT_URL}
|
||||||
|
- GITEA__server__HTTP_PORT=3000
|
||||||
|
- GITEA__server__PROTOCOL=http
|
||||||
|
- GITEA__server__DOMAIN=${GITEA_DOMAIN}
|
||||||
|
- GITEA__server__ENABLE_GZIP=true
|
||||||
|
- GITEA__server__PROXY_PROTOCOL=true
|
||||||
|
- GITEA__server__PROXY_PROXY_PROTOCOL_TLS=true
|
||||||
|
- GITEA__server__PROXY_ALLOW_SUBNET=0.0.0.0/0
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- changemaker-lite
|
||||||
|
volumes:
|
||||||
|
- gitea_data:/data
|
||||||
|
- /etc/timezone:/etc/timezone:ro
|
||||||
|
- /etc/localtime:/etc/localtime:ro
|
||||||
|
ports:
|
||||||
|
- "${GITEA_WEB_PORT:-3030}:3000"
|
||||||
|
- "${GITEA_SSH_PORT:-2222}:22"
|
||||||
|
depends_on:
|
||||||
|
- gitea-db
|
||||||
|
|
||||||
|
gitea-db:
|
||||||
|
image: mysql:8
|
||||||
|
container_name: gitea_mysql_changemaker-lite
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
- MYSQL_ROOT_PASSWORD=${GITEA_DB_ROOT_PASSWORD}
|
||||||
|
- MYSQL_USER=${GITEA_DB_USER:-gitea}
|
||||||
|
- MYSQL_PASSWORD=${GITEA_DB_PASSWD}
|
||||||
|
- MYSQL_DATABASE=${GITEA_DB_NAME:-gitea}
|
||||||
|
networks:
|
||||||
|
- changemaker-lite
|
||||||
|
volumes:
|
||||||
|
- mysql_data:/var/lib/mysql
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "${GITEA_DB_USER:-gitea}", "-p${GITEA_DB_PASSWD}"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
|
||||||
|
networks:
|
||||||
|
changemaker-lite:
|
||||||
|
driver: bridge
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
listmonk-data:
|
||||||
|
n8n_data:
|
||||||
|
nc_data:
|
||||||
|
db_data:
|
||||||
|
gitea_data:
|
||||||
|
mysql_data:
|
BIN
mkdocs/.cache/plugin/social/0abb4cb0b56b80f3a9ce2388a76b413f.png
Normal file
After Width: | Height: | Size: 40 KiB |
BIN
mkdocs/.cache/plugin/social/0b55e7897874ca8de1bfe30265aef42e.png
Normal file
After Width: | Height: | Size: 54 KiB |
BIN
mkdocs/.cache/plugin/social/0ee943db87dfa39f1d8b8882384425da.png
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
mkdocs/.cache/plugin/social/1345f63ac767939fb6429ccf170f1e5f.png
Normal file
After Width: | Height: | Size: 40 KiB |
BIN
mkdocs/.cache/plugin/social/13b7450908684a452205d94a249e3e4d.png
Normal file
After Width: | Height: | Size: 33 KiB |
BIN
mkdocs/.cache/plugin/social/1931a5be0487b18cdd3a1c0ddeb68e09.png
Normal file
After Width: | Height: | Size: 34 KiB |
BIN
mkdocs/.cache/plugin/social/1ba54b15aedf0426dba781e37512dce1.png
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
mkdocs/.cache/plugin/social/1f7db51025c379f324852dcda65e7887.png
Normal file
After Width: | Height: | Size: 36 KiB |
BIN
mkdocs/.cache/plugin/social/237553615f5a06f3712aaba6bb3a7ffb.png
Normal file
After Width: | Height: | Size: 35 KiB |
BIN
mkdocs/.cache/plugin/social/260a1c90a2b4cd55ee26d2479458825c.png
Normal file
After Width: | Height: | Size: 57 KiB |
BIN
mkdocs/.cache/plugin/social/28d05a95b492b49a865d6e7c037f4a99.png
Normal file
After Width: | Height: | Size: 26 KiB |
BIN
mkdocs/.cache/plugin/social/2b564a56ae420375695ce06cf8beede5.png
Normal file
After Width: | Height: | Size: 35 KiB |
BIN
mkdocs/.cache/plugin/social/2bdc81b931c7b76d4bfc172a28af3c32.png
Normal file
After Width: | Height: | Size: 34 KiB |
BIN
mkdocs/.cache/plugin/social/2d3cf2d42c43e6e706e01669ea7b47d7.png
Normal file
After Width: | Height: | Size: 40 KiB |
BIN
mkdocs/.cache/plugin/social/32ef6c8195741823ddec0fa6e9a9f3eb.png
Normal file
After Width: | Height: | Size: 34 KiB |
BIN
mkdocs/.cache/plugin/social/3361679ccc4e968c67cd73402e9aab81.png
Normal file
After Width: | Height: | Size: 29 KiB |
BIN
mkdocs/.cache/plugin/social/359a00add05c04970048e0a8be38f335.png
Normal file
After Width: | Height: | Size: 39 KiB |
BIN
mkdocs/.cache/plugin/social/380d7c2b0fe08b493118f7e3e813d619.png
Normal file
After Width: | Height: | Size: 31 KiB |
BIN
mkdocs/.cache/plugin/social/39d5d9e743d460c873cdf972697a0dd0.png
Normal file
After Width: | Height: | Size: 50 KiB |
BIN
mkdocs/.cache/plugin/social/3a2fd3f826d2a906ed92c3970b25cb7d.png
Normal file
After Width: | Height: | Size: 34 KiB |
BIN
mkdocs/.cache/plugin/social/3d6d2c6d670fab67a7f8b37a36b1f14c.png
Normal file
After Width: | Height: | Size: 55 KiB |
BIN
mkdocs/.cache/plugin/social/3ee478cafa95963d945be030289a0159.png
Normal file
After Width: | Height: | Size: 49 KiB |
BIN
mkdocs/.cache/plugin/social/3f18fd6addada8d9deb60bbad32514fa.png
Normal file
After Width: | Height: | Size: 33 KiB |
BIN
mkdocs/.cache/plugin/social/431ab4f4662987971222ad7fc4583df8.png
Normal file
After Width: | Height: | Size: 43 KiB |
BIN
mkdocs/.cache/plugin/social/44a679a39915b848994bc5eb7c771084.png
Normal file
After Width: | Height: | Size: 38 KiB |
BIN
mkdocs/.cache/plugin/social/4764c5a1bcc778ffcddc0a795f1139c7.png
Normal file
After Width: | Height: | Size: 38 KiB |
BIN
mkdocs/.cache/plugin/social/4b0aa135e76f6b9e18ebdd24b1552da6.png
Normal file
After Width: | Height: | Size: 36 KiB |
BIN
mkdocs/.cache/plugin/social/4dbd1b187dc8ffc02bbd3ee4ed2f63b8.png
Normal file
After Width: | Height: | Size: 32 KiB |
BIN
mkdocs/.cache/plugin/social/4e95f2e36cd0bb6a7ad665beb497ac83.png
Normal file
After Width: | Height: | Size: 41 KiB |
BIN
mkdocs/.cache/plugin/social/4eb95c3f3f9893da99dee3b68b137595.png
Normal file
After Width: | Height: | Size: 35 KiB |
BIN
mkdocs/.cache/plugin/social/502ad9b1f18fe233f6ee41cacd812429.png
Normal file
After Width: | Height: | Size: 50 KiB |
BIN
mkdocs/.cache/plugin/social/56f27d3fe6ed0435d9bfce3d8d1ab049.png
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
mkdocs/.cache/plugin/social/5b8437d363c52abd31123bd3d5bf82e2.png
Normal file
After Width: | Height: | Size: 37 KiB |
BIN
mkdocs/.cache/plugin/social/5c8282bc8e15c5e47e91f2c2266a5dbc.png
Normal file
After Width: | Height: | Size: 41 KiB |
BIN
mkdocs/.cache/plugin/social/5d4583bbd8388c720bff551fd35a1ac6.png
Normal file
After Width: | Height: | Size: 58 KiB |
BIN
mkdocs/.cache/plugin/social/609232be2b0ead3ecefbc6b21d45653f.png
Normal file
After Width: | Height: | Size: 54 KiB |
BIN
mkdocs/.cache/plugin/social/61ada07d673e75ab5d383772efa3e386.png
Normal file
After Width: | Height: | Size: 27 KiB |
BIN
mkdocs/.cache/plugin/social/665a7a652a8a7d093192df525fe984b9.png
Normal file
After Width: | Height: | Size: 38 KiB |
BIN
mkdocs/.cache/plugin/social/668c246629c618cf956e7cea78d4037d.png
Normal file
After Width: | Height: | Size: 26 KiB |
BIN
mkdocs/.cache/plugin/social/680dbad20ecfbdfa00d309b192263291.png
Normal file
After Width: | Height: | Size: 42 KiB |
BIN
mkdocs/.cache/plugin/social/687fb766b8846fd05f5c0bb69a9edf84.png
Normal file
After Width: | Height: | Size: 44 KiB |
BIN
mkdocs/.cache/plugin/social/690d82aedba9d262454729e29c356b3c.png
Normal file
After Width: | Height: | Size: 38 KiB |
BIN
mkdocs/.cache/plugin/social/70175cd9d7212ffcc59fbc9c7f0d4440.png
Normal file
After Width: | Height: | Size: 42 KiB |
BIN
mkdocs/.cache/plugin/social/704e77577bf1d6afce98437ea2866a83.png
Normal file
After Width: | Height: | Size: 34 KiB |
BIN
mkdocs/.cache/plugin/social/721bd151e1ec0d1e48006634b8ff2e38.png
Normal file
After Width: | Height: | Size: 29 KiB |
BIN
mkdocs/.cache/plugin/social/731847eb1db30740c72d6a0f1bc79a71.png
Normal file
After Width: | Height: | Size: 31 KiB |
BIN
mkdocs/.cache/plugin/social/740e1015f23d88c791e7cd6c726e1081.png
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
mkdocs/.cache/plugin/social/7a03c2a41a22d644330b1b7bc2b58d3d.png
Normal file
After Width: | Height: | Size: 46 KiB |
BIN
mkdocs/.cache/plugin/social/7db31f564bab29ce21afe0a7feae7012.png
Normal file
After Width: | Height: | Size: 33 KiB |
BIN
mkdocs/.cache/plugin/social/7ec96f86a1cb836cbe60f2d539a79d77.png
Normal file
After Width: | Height: | Size: 62 KiB |
BIN
mkdocs/.cache/plugin/social/83db91be2c8770ec6e0cad47805127ef.png
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
mkdocs/.cache/plugin/social/86bc017f81fe2395e0c53430488feb8e.png
Normal file
After Width: | Height: | Size: 39 KiB |
BIN
mkdocs/.cache/plugin/social/8bd313bb897682b9a393f2f76f55c593.png
Normal file
After Width: | Height: | Size: 36 KiB |
BIN
mkdocs/.cache/plugin/social/8dfb08c0f74e953a0fd4a26db905370a.png
Normal file
After Width: | Height: | Size: 57 KiB |
BIN
mkdocs/.cache/plugin/social/919b2121a7b47fe2cdad12febff6359a.png
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
mkdocs/.cache/plugin/social/91b8d7383fe2e54c00b38a0f26e3b770.png
Normal file
After Width: | Height: | Size: 41 KiB |
BIN
mkdocs/.cache/plugin/social/96a422da08dcb28205d2e584a7c620ca.png
Normal file
After Width: | Height: | Size: 31 KiB |
BIN
mkdocs/.cache/plugin/social/9b8aa203f6bb9d837c1bc398fb6cf8ec.png
Normal file
After Width: | Height: | Size: 34 KiB |
BIN
mkdocs/.cache/plugin/social/9dabdb58ebfab640f34444d6ab1dbf91.png
Normal file
After Width: | Height: | Size: 58 KiB |
BIN
mkdocs/.cache/plugin/social/9ef625b1724cd11a5b589b7482507f1b.png
Normal file
After Width: | Height: | Size: 55 KiB |
BIN
mkdocs/.cache/plugin/social/9f9f9e07a4f4ee67a035cea72d494e21.png
Normal file
After Width: | Height: | Size: 40 KiB |
BIN
mkdocs/.cache/plugin/social/9fdd03d6602a201cef87a201f27fc715.png
Normal file
After Width: | Height: | Size: 31 KiB |
BIN
mkdocs/.cache/plugin/social/a0bccd2b0770e49326ded4080b1052bd.png
Normal file
After Width: | Height: | Size: 67 KiB |
BIN
mkdocs/.cache/plugin/social/a1fd1179cb1dcbb6751321e1f87069e3.png
Normal file
After Width: | Height: | Size: 42 KiB |
BIN
mkdocs/.cache/plugin/social/a42db1f8972fbbd7ab92c9fa6e91d45e.png
Normal file
After Width: | Height: | Size: 64 KiB |
BIN
mkdocs/.cache/plugin/social/a5775bbacaf158405defcb68416ea8bd.png
Normal file
After Width: | Height: | Size: 27 KiB |
BIN
mkdocs/.cache/plugin/social/aa0707be07fb7b02ab3372711d954d83.png
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
mkdocs/.cache/plugin/social/ac04f515d128c1a7e264e731162351fb.png
Normal file
After Width: | Height: | Size: 41 KiB |
BIN
mkdocs/.cache/plugin/social/acfa7c461bffcaec0d19de3983eff13d.png
Normal file
After Width: | Height: | Size: 42 KiB |
BIN
mkdocs/.cache/plugin/social/af754735b566f6d264e3f21b80a2d139.png
Normal file
After Width: | Height: | Size: 32 KiB |
BIN
mkdocs/.cache/plugin/social/b2ea8eabbd3acb20839c119ed422a96c.png
Normal file
After Width: | Height: | Size: 25 KiB |
BIN
mkdocs/.cache/plugin/social/b3ed80eec9045cdbd3f44be9560ed73f.png
Normal file
After Width: | Height: | Size: 56 KiB |
BIN
mkdocs/.cache/plugin/social/ba7ec4e9e95e59b7a38a3d5ad9a6258a.png
Normal file
After Width: | Height: | Size: 40 KiB |
BIN
mkdocs/.cache/plugin/social/bbf330d53ce8ec46ba998fef04e7afb4.png
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
mkdocs/.cache/plugin/social/bcbb6631bf869d006e39f0f32c222578.png
Normal file
After Width: | Height: | Size: 39 KiB |
BIN
mkdocs/.cache/plugin/social/c10edc50e6bd3027ed2b503e6447a103.png
Normal file
After Width: | Height: | Size: 59 KiB |
BIN
mkdocs/.cache/plugin/social/c46f702e72488776dc207dd3ee60403d.png
Normal file
After Width: | Height: | Size: 58 KiB |
BIN
mkdocs/.cache/plugin/social/c581111ffc8ac6c7e3d5b4c02da0c3b5.png
Normal file
After Width: | Height: | Size: 59 KiB |
BIN
mkdocs/.cache/plugin/social/c86e8855647c3a781563d3e68227bae0.png
Normal file
After Width: | Height: | Size: 59 KiB |
BIN
mkdocs/.cache/plugin/social/c9d7d185f1027eb87ca3f660e74ff361.png
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
mkdocs/.cache/plugin/social/ca7a35746e5b24c6195659a5ce3f5a0d.png
Normal file
After Width: | Height: | Size: 26 KiB |
BIN
mkdocs/.cache/plugin/social/cf07b7c75795438c349c45ddf52194e6.png
Normal file
After Width: | Height: | Size: 28 KiB |