Compare commits
55 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
0c5cf29f83
|
|||
|
43c38767a1
|
|||
| a68c01a68f | |||
| bdcdb1cb3d | |||
| fe2b80b51f | |||
| b8d5bc747d | |||
|
6f77b7d845
|
|||
| ea286e117d | |||
| 6a050669e8 | |||
| b9c11b8eab | |||
| 6dac841b2c | |||
| 526a2b617a | |||
| 1e08391e0e | |||
| 0a31601b77 | |||
| 25eb89c902 | |||
| d988b0f7db | |||
| e873f37159 | |||
| 8362f45bdc | |||
| fd5ea6cadd | |||
| aa3598ee3d | |||
|
c0205fa49b
|
|||
|
a22707770f
|
|||
|
646f988a86
|
|||
|
414d17d839
|
|||
|
812e4eb87a
|
|||
| 70b3b203fb | |||
| d857f3838d | |||
| f8620349a9 | |||
| b1acff5c85 | |||
| beb6dca6a2 | |||
| aed6ff31f7 | |||
| 73684af21b | |||
| cd5c90adcc | |||
| 578a1ca544 | |||
| 12ed20a306 | |||
|
400e7b6595
|
|||
| f58e96a25d | |||
| a3e5f5a78b | |||
|
1a09a62a4c
|
|||
|
2593b54402
|
|||
|
42826a037c
|
|||
|
d7a68237e5
|
|||
| bbb8bdf0a7 | |||
| f4d3bd9c6d | |||
|
6b919df7c6
|
|||
| 3ee0b96ed5 | |||
| d25d213d9b | |||
|
a3b7c4c889
|
|||
| 3dbb50c924 | |||
| 54da82432b | |||
| e5eb234c41 | |||
| 721348e67f | |||
|
d58ae2a5e7
|
|||
| aa516c3445 | |||
|
b5cc7d9a0d
|
@@ -0,0 +1,39 @@
|
|||||||
|
name: Deploy to Server
|
||||||
|
run-name: Deploying to ${{ runner.os}} server on ${{ gitea.ref }}
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- ci/gitea-actions
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
runs-on: prod
|
||||||
|
steps:
|
||||||
|
- name: Fetch and Diff Analysis
|
||||||
|
id: diff
|
||||||
|
run: |
|
||||||
|
cd ${{ secrets.PROD_DIR }}
|
||||||
|
git fetch origin main
|
||||||
|
CHANGES=$(git diff --name-only HEAD origin/main | cut -d/ -f1 | sort -u | tr '\n' ' ')
|
||||||
|
echo "dirs=$CHANGES" >> $GITHUB_OUTPUT
|
||||||
|
echo "Changed dirs: $CHANGES"
|
||||||
|
|
||||||
|
- name: Sync Server Files
|
||||||
|
run: |
|
||||||
|
cd ${{ secrets.PROD_DIR }}
|
||||||
|
git reset --hard origin/main
|
||||||
|
echo "Server files synced with origin/main"
|
||||||
|
|
||||||
|
- name: Deploy Services
|
||||||
|
run: |
|
||||||
|
cd ${{ secrets.PROD_DIR }}
|
||||||
|
for dir in ${{ steps.diff.outputs.dirs }}; do
|
||||||
|
if [ -d "$dir" ] && ([ -f "$dir/compose.yaml" ] || [ -f "$dir/docker-compose.yaml" ]); then
|
||||||
|
echo ">>> Deploying $dir"
|
||||||
|
cd "$dir"
|
||||||
|
DOCKER_BUILDKIT=1 BUILDKIT_PROGRESS=plain docker compose up -d --build --no-color
|
||||||
|
cd ..
|
||||||
|
else
|
||||||
|
echo ">>> Skipping $dir: no compose file found"
|
||||||
|
fi
|
||||||
|
done
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
## BINARY MODE, USE WITH THE GITHUB RUNNER BINARY INSTALLED ON THE SERVER
|
||||||
|
|
||||||
|
name: Deploy to Server
|
||||||
|
run-name: Deploying onto server on ${{ github.ref }}
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- ci/actions
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
runs-on: [prod, self-hosted]
|
||||||
|
steps:
|
||||||
|
- name: Fetch and Diff Analysis
|
||||||
|
id: diff
|
||||||
|
run: |
|
||||||
|
cd ${{ secrets.PROD_DIR }}
|
||||||
|
git fetch origin main
|
||||||
|
CHANGES=$(git diff --name-only HEAD origin/main | cut -d/ -f1 | sort -u | tr '\n' ' ')
|
||||||
|
echo "dirs=$CHANGES" >> $GITHUB_OUTPUT
|
||||||
|
echo "Changed dirs: $CHANGES"
|
||||||
|
|
||||||
|
- name: Sync Server Files
|
||||||
|
run: |
|
||||||
|
cd ${{ secrets.PROD_DIR }}
|
||||||
|
git reset --hard origin/main
|
||||||
|
echo "Server files synced with origin/main"
|
||||||
|
|
||||||
|
- name: Deploy Services
|
||||||
|
run: |
|
||||||
|
cd ${{ secrets.PROD_DIR }}
|
||||||
|
for dir in ${{ steps.diff.outputs.dirs }}; do
|
||||||
|
if [ -d "$dir" ] && ([ -f "$dir/compose.yaml" ] || [ -f "$dir/docker-compose.yaml" ]); then
|
||||||
|
echo ">>> Deploying $dir"
|
||||||
|
cd "$dir"
|
||||||
|
DOCKER_BUILDKIT=1 BUILDKIT_PROGRESS=plain docker compose up -d --build --no-color
|
||||||
|
cd ..
|
||||||
|
else
|
||||||
|
echo ">>> Skipping $dir: no compose file found"
|
||||||
|
fi
|
||||||
|
done
|
||||||
@@ -5,12 +5,12 @@ sync.ffs_lock
|
|||||||
# Copyparty
|
# Copyparty
|
||||||
*.hist/
|
*.hist/
|
||||||
|
|
||||||
# Volumes and data directories
|
# Volumes, configs and data directories
|
||||||
gitea/gitea-db/
|
gitea/gitea-db/
|
||||||
gitea/gitea-data/*
|
gitea/gitea-data/*
|
||||||
n8n/n8n-data/*
|
n8n/n8n-data/*
|
||||||
n8n/n8n-node-data/*
|
n8n/n8n-node-data/*
|
||||||
adguardhome/data/*
|
adguardhome/conf/*
|
||||||
dockmon/data/*
|
dockmon/data/*
|
||||||
portainer/portainer_data/*
|
portainer/portainer_data/*
|
||||||
metube/MeTube_downloads
|
metube/MeTube_downloads
|
||||||
@@ -21,6 +21,7 @@ checkmk/checkmk/*
|
|||||||
downtify/Downtify_downloads
|
downtify/Downtify_downloads
|
||||||
headscale/config/*
|
headscale/config/*
|
||||||
headscale/data/*
|
headscale/data/*
|
||||||
|
searxng/core-config/*
|
||||||
|
|
||||||
# Steaming services files
|
# Steaming services files
|
||||||
streaming/jellyfin/*
|
streaming/jellyfin/*
|
||||||
@@ -32,12 +33,15 @@ streaming/qbittorrent/*
|
|||||||
streaming/prowlarr/*
|
streaming/prowlarr/*
|
||||||
|
|
||||||
# Homepage
|
# Homepage
|
||||||
homepages/forust_files/assets/images/team/*
|
|
||||||
homepages/forust_files/.well-known/*
|
homepages/forust_files/.well-known/*
|
||||||
|
|
||||||
# Traefik files
|
# Traefik files
|
||||||
traefik/letsencrypt/acme.json
|
traefik/letsencrypt/acme.json
|
||||||
traefik/dynamic/fileservers.yml
|
traefik/dynamic/fileservers.yml
|
||||||
|
traefik/dynamic/*.local.y*ml.*
|
||||||
|
traefik/dynamic/*.external.y*ml
|
||||||
|
|
||||||
|
|
||||||
traefik/logs/*
|
traefik/logs/*
|
||||||
|
|
||||||
# SSL Certificates
|
# SSL Certificates
|
||||||
@@ -77,6 +81,8 @@ replacements.txt
|
|||||||
|
|
||||||
# Git
|
# Git
|
||||||
.gitattributes
|
.gitattributes
|
||||||
|
# Gitea/github Runners
|
||||||
|
.runner
|
||||||
|
|
||||||
# Misc
|
# Misc
|
||||||
.DS_Store
|
.DS_Store
|
||||||
@@ -91,4 +97,4 @@ temp/*
|
|||||||
.env.anna
|
.env.anna
|
||||||
.env.forust
|
.env.forust
|
||||||
.env.*
|
.env.*
|
||||||
!*example
|
!*example
|
||||||
|
|||||||
@@ -11,28 +11,24 @@ services:
|
|||||||
# - "68:68/tcp" # DHCP
|
# - "68:68/tcp" # DHCP
|
||||||
# - "3000:3000/tcp"
|
# - "3000:3000/tcp"
|
||||||
volumes:
|
volumes:
|
||||||
- ./data/work:/opt/adguardhome/work
|
- data:/opt/adguardhome/work
|
||||||
- ./data/conf:/opt/adguardhome/conf
|
- ./conf:/opt/adguardhome/conf
|
||||||
- ./certs:/certs:ro
|
- ./certs:/certs:ro
|
||||||
labels:
|
labels:
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.docker.network=proxy"
|
|
||||||
- "traefik.http.services.adguard.loadbalancer.server.port=3000"
|
- "traefik.http.services.adguard.loadbalancer.server.port=3000"
|
||||||
|
|
||||||
# Prod Router
|
# Prod Router
|
||||||
- "traefik.http.routers.adguard.rule=Host(`dns.forust.xyz`) || Host(`adguard.forust.xyz`)"
|
- "traefik.http.routers.adguard.rule=Host(`dns.forust.xyz`) || Host(`adguard.forust.xyz`)"
|
||||||
- "traefik.http.routers.adguard.entrypoints=websecure"
|
- "traefik.http.routers.adguard.entrypoints=websecure"
|
||||||
- "traefik.http.routers.adguard.middlewares=security-headers@file"
|
|
||||||
- "traefik.http.routers.adguard.tls=true"
|
- "traefik.http.routers.adguard.tls=true"
|
||||||
# Local Router
|
# Local Router
|
||||||
- "traefik.http.routers.adguard-local.rule=Host(`adguard.workstation.internal`) || Host(`dns.workstation.internal`)"
|
- "traefik.http.routers.adguard-local.rule=Host(`adguard.workstation.internal`) || Host(`dns.workstation.internal`)"
|
||||||
- "traefik.http.routers.adguard-local.entrypoints=websecure"
|
- "traefik.http.routers.adguard-local.entrypoints=websecure"
|
||||||
- "traefik.http.routers.adguard-local.middlewares=security-headers@file"
|
|
||||||
- "traefik.http.routers.adguard-local.tls=true"
|
- "traefik.http.routers.adguard-local.tls=true"
|
||||||
# Dev Router
|
# Dev Router
|
||||||
- "traefik.http.routers.adguard-dev.rule=Host(`adguard.gigaforust.internal`) || Host(`dns.gigaforust.internal`)"
|
- "traefik.http.routers.adguard-dev.rule=Host(`adguard.gigaforust.internal`) || Host(`dns.gigaforust.internal`)"
|
||||||
- "traefik.http.routers.adguard-dev.entrypoints=websecure"
|
- "traefik.http.routers.adguard-dev.entrypoints=websecure"
|
||||||
- "traefik.http.routers.adguard-dev.middlewares=security-headers@file"
|
|
||||||
- "traefik.http.routers.adguard-dev.tls=true"
|
- "traefik.http.routers.adguard-dev.tls=true"
|
||||||
# DoH Router
|
# DoH Router
|
||||||
- "traefik.http.routers.dns.rule=(Host(`dns.forust.xyz`) && PathPrefix(`/dns-query`))"
|
- "traefik.http.routers.dns.rule=(Host(`dns.forust.xyz`) && PathPrefix(`/dns-query`))"
|
||||||
@@ -45,6 +41,8 @@ services:
|
|||||||
- glance.description=AdGuard Home is a network-wide software for blocking ads.
|
- glance.description=AdGuard Home is a network-wide software for blocking ads.
|
||||||
networks:
|
networks:
|
||||||
- proxy
|
- proxy
|
||||||
|
volumes:
|
||||||
|
data:
|
||||||
networks:
|
networks:
|
||||||
proxy:
|
proxy:
|
||||||
external: true
|
external: true
|
||||||
|
|||||||
@@ -42,26 +42,20 @@ services:
|
|||||||
- ./custom-templates:/templates
|
- ./custom-templates:/templates
|
||||||
labels:
|
labels:
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.docker.network=proxy"
|
|
||||||
- "traefik.http.services.authentik-server.loadbalancer.server.port=9000"
|
- "traefik.http.services.authentik-server.loadbalancer.server.port=9000"
|
||||||
|
|
||||||
# Prod Router
|
# Prod Router
|
||||||
- "traefik.http.routers.authentik-server.rule=Host(`auth.forust.xyz`)"
|
- "traefik.http.routers.authentik-server.rule=Host(`auth.forust.xyz`)"
|
||||||
- "traefik.http.routers.authentik-server.entrypoints=websecure"
|
- "traefik.http.routers.authentik-server.entrypoints=websecure"
|
||||||
- "traefik.http.routers.authentik-server.middlewares=security-headers@file"
|
|
||||||
- "traefik.http.routers.authentik-server.service=authentik-server"
|
|
||||||
- "traefik.http.routers.authentik-server.tls=true"
|
- "traefik.http.routers.authentik-server.tls=true"
|
||||||
# Local Router
|
# Local Router
|
||||||
- "traefik.http.routers.authentik-server-local.rule=Host(`auth.workstation.internal`) || Host(`auth-dashboard.internal`)"
|
- "traefik.http.routers.authentik-server-local.rule=Host(`auth.workstation.internal`)"
|
||||||
- "traefik.http.routers.authentik-server-local.entrypoints=websecure"
|
- "traefik.http.routers.authentik-server-local.entrypoints=websecure"
|
||||||
- "traefik.http.routers.authentik-server-local.middlewares=security-headers@file"
|
|
||||||
- "traefik.http.routers.authentik-server-local.service=authentik-server"
|
|
||||||
- "traefik.http.routers.authentik-server-local.tls=true"
|
- "traefik.http.routers.authentik-server-local.tls=true"
|
||||||
# Dev Router
|
# Dev Router
|
||||||
- "traefik.http.routers.authentik-server-dev.rule=Host(`auth.gigaforust.internal`)"
|
- "traefik.http.routers.authentik-server-dev.rule=Host(`auth.gigaforust.internal`)"
|
||||||
- "traefik.http.routers.authentik-server-dev.entrypoints=websecure"
|
- "traefik.http.routers.authentik-server-dev.entrypoints=websecure"
|
||||||
- "traefik.http.routers.authentik-server-dev.middlewares=security-headers@file"
|
- "traefik.http.routers.authentik-server-dev.middlewares=security-headers@file"
|
||||||
- "traefik.http.routers.authentik-server-dev.service=authentik-server"
|
|
||||||
- "traefik.http.routers.authentik-server-dev.tls=true"
|
- "traefik.http.routers.authentik-server-dev.tls=true"
|
||||||
networks:
|
networks:
|
||||||
- proxy
|
- proxy
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
CLOUDFLARE_API_TOKEN=YOUR_CLOUDFLARE_API_TOKEN
|
||||||
|
DOMAINS=example.com,dns.example.com,mc.example.com,auth.example.com,ssh.example.com
|
||||||
|
IP4_DOMAINS=
|
||||||
|
IP6_DOMAINS=
|
||||||
|
IP4_PROVIDER=cloudflare.trace
|
||||||
|
IP6_PROVIDER=none # change if you want to update AAAA
|
||||||
|
UPDATE_CRON=@every 5m
|
||||||
|
UPDATE_ON_START=true
|
||||||
|
DELETE_ON_STOP=false
|
||||||
|
DELETE_ON_FAILURE=true
|
||||||
|
TTL=1
|
||||||
|
PROXIED=!is(dns.example.com) && !is(mc.example.com) && !is(ssh.example.com)
|
||||||
|
EMOJI=true
|
||||||
|
UPTIMEKUMA=https://uptime-kuma.example.com/api/push/AsaSDFGFkfklaFALSKffkfFKfkfkfkFK?status=up&msg=OK&ping=
|
||||||
|
REJECT_CLOUDFLARE_IPS=true
|
||||||
@@ -6,8 +6,25 @@ services:
|
|||||||
security_opt:
|
security_opt:
|
||||||
- no-new-privileges:true
|
- no-new-privileges:true
|
||||||
network_mode: 'host'
|
network_mode: 'host'
|
||||||
|
# https://github.com/timothymiller/cloudflare-ddns#-quick-start
|
||||||
environment:
|
environment:
|
||||||
- PUID=1000
|
- CLOUDFLARE_API_TOKEN=${CLOUDFLARE_API_TOKEN:?Cloudflare API token is required}
|
||||||
- PGID=1000
|
- DOMAINS=${DOMAINS:-}
|
||||||
volumes:
|
- IP4_DOMAINS=${IP4_DOMAINS:-}
|
||||||
- ./config.json:/config.json
|
- IP6_DOMAINS=${IP6_DOMAINS:-}
|
||||||
|
- IP4_PROVIDER=${IP4_PROVIDER:-cloudflare.trace}
|
||||||
|
- IP6_PROVIDER=${IP6_PROVIDER:-none}
|
||||||
|
- UPDATE_CRON=${UPDATE_CRON:-@every 5m}
|
||||||
|
- UPDATE_ON_START=${UPDATE_ON_START:-true}
|
||||||
|
- DELETE_ON_STOP=${DELETE_ON_STOP:-false}
|
||||||
|
- DELETE_ON_FAILURE=${DELETE_ON_FAILURE:-true}
|
||||||
|
- TTL=${TTL:-1} # 1=auto
|
||||||
|
# to proxy only "dns.example.com" and "wfs.example.com" use "!is(dns.domain.com) && !is (wfs.domain.com)"
|
||||||
|
- PROXIED=${PROXIED:-true}
|
||||||
|
- EMOJI=${EMOJI:-true}
|
||||||
|
- UPTIMEKUMA=${UPTIMEKUMA:-}
|
||||||
|
- HEALTHCHECKS=${HEALTHCHECKS:-}
|
||||||
|
- REJECT_CLOUDFLARE_IPS=${REJECT_CLOUDFLARE_IPS:-true}
|
||||||
|
# volumes:
|
||||||
|
# Prefer using environment variables for configuration, config.json legacy support
|
||||||
|
# - ./config.json:/config.json
|
||||||
|
|||||||
@@ -16,22 +16,18 @@ services:
|
|||||||
- TZ=${TZ:-Etc/UTC}
|
- TZ=${TZ:-Etc/UTC}
|
||||||
labels:
|
labels:
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.docker.network=proxy"
|
|
||||||
- "traefik.http.services.checkmk.loadbalancer.server.port=5000"
|
- "traefik.http.services.checkmk.loadbalancer.server.port=5000"
|
||||||
|
|
||||||
# Prod Router
|
# Prod Router
|
||||||
- "traefik.http.routers.checkmk.rule=Host(`cmk.forust.xyz`)"
|
- "traefik.http.routers.checkmk.rule=Host(`cmk.forust.xyz`)"
|
||||||
- "traefik.http.routers.checkmk.entrypoints=websecure"
|
- "traefik.http.routers.checkmk.entrypoints=websecure"
|
||||||
- "traefik.http.routers.checkmk.middlewares=security-headers@file"
|
|
||||||
- "traefik.http.routers.checkmk.tls=true"
|
- "traefik.http.routers.checkmk.tls=true"
|
||||||
# Local Router
|
# Local Router
|
||||||
- "traefik.http.routers.checkmk-local.rule=Host(`cmk.workstation.internal`)"
|
- "traefik.http.routers.checkmk-local.rule=Host(`cmk.workstation.internal`)"
|
||||||
- "traefik.http.routers.checkmk-local.entrypoints=websecure"
|
- "traefik.http.routers.checkmk-local.entrypoints=websecure"
|
||||||
- "traefik.http.routers.checkmk-local.middlewares=security-headers@file"
|
|
||||||
- "traefik.http.routers.checkmk-local.tls=true"
|
- "traefik.http.routers.checkmk-local.tls=true"
|
||||||
# Dev Router
|
# Dev Router
|
||||||
- "traefik.http.routers.checkmk-dev.rule=Host(`cmk.gigaforust.internal`)"
|
- "traefik.http.routers.checkmk-dev.rule=Host(`cmk.gigaforust.internal`)"
|
||||||
- "traefik.http.routers.checkmk-dev.middlewares=security-headers@file"
|
|
||||||
- "traefik.http.routers.checkmk-dev.entrypoints=websecure"
|
- "traefik.http.routers.checkmk-dev.entrypoints=websecure"
|
||||||
- "traefik.http.routers.checkmk-dev.tls=true"
|
- "traefik.http.routers.checkmk-dev.tls=true"
|
||||||
networks:
|
networks:
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ services:
|
|||||||
retries: 3
|
retries: 3
|
||||||
labels:
|
labels:
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.docker.network=proxy"
|
|
||||||
- "traefik.http.services.dockmon.loadbalancer.server.port=443"
|
- "traefik.http.services.dockmon.loadbalancer.server.port=443"
|
||||||
- "traefik.http.services.dockmon.loadbalancer.server.scheme=https"
|
- "traefik.http.services.dockmon.loadbalancer.server.scheme=https"
|
||||||
- "traefik.http.services.dockmon.loadbalancer.serverstransport=insecureTransport@file"
|
- "traefik.http.services.dockmon.loadbalancer.serverstransport=insecureTransport@file"
|
||||||
@@ -23,18 +22,15 @@ services:
|
|||||||
# Prod Router
|
# Prod Router
|
||||||
- "traefik.http.routers.dockmon.rule=Host(`dockmon.forust.xyz`)"
|
- "traefik.http.routers.dockmon.rule=Host(`dockmon.forust.xyz`)"
|
||||||
- "traefik.http.routers.dockmon.entrypoints=websecure"
|
- "traefik.http.routers.dockmon.entrypoints=websecure"
|
||||||
- "traefik.http.routers.dockmon.middlewares=security-chain@file"
|
- "traefik.http.routers.dockmon.middlewares=security-headers@file"
|
||||||
- "traefik.http.routers.dockmon.service=dockmon"
|
|
||||||
- "traefik.http.routers.dockmon.tls=true"
|
- "traefik.http.routers.dockmon.tls=true"
|
||||||
# Local Router
|
# Local Router
|
||||||
- "traefik.http.routers.dockmon-local.rule=Host(`dockmon.workstation.internal`)"
|
- "traefik.http.routers.dockmon-local.rule=Host(`dockmon.workstation.internal`)"
|
||||||
- "traefik.http.routers.dockmon-local.entrypoints=websecure"
|
- "traefik.http.routers.dockmon-local.entrypoints=websecure"
|
||||||
- "traefik.http.routers.dockmon-local.middlewares=security-headers@file"
|
|
||||||
- "traefik.http.routers.dockmon-local.tls=true"
|
- "traefik.http.routers.dockmon-local.tls=true"
|
||||||
# Dev Router
|
# Dev Router
|
||||||
- "traefik.http.routers.dockmon-dev.rule=Host(`dockmon.gigaforust.internal`)"
|
- "traefik.http.routers.dockmon-dev.rule=Host(`dockmon.gigaforust.internal`)"
|
||||||
- "traefik.http.routers.dockmon-dev.entrypoints=websecure"
|
- "traefik.http.routers.dockmon-dev.entrypoints=websecure"
|
||||||
- "traefik.http.routers.dockmon-dev.middlewares=security-chain@file"
|
|
||||||
- "traefik.http.routers.dockmon-dev.tls=true"
|
- "traefik.http.routers.dockmon-dev.tls=true"
|
||||||
|
|
||||||
# Glance Metadata
|
# Glance Metadata
|
||||||
|
|||||||
@@ -2,30 +2,28 @@ services:
|
|||||||
downtify:
|
downtify:
|
||||||
container_name: downtify
|
container_name: downtify
|
||||||
image: ghcr.io/henriquesebastiao/downtify:latest
|
image: ghcr.io/henriquesebastiao/downtify:latest
|
||||||
|
restart: unless-stopped
|
||||||
# ports:
|
# ports:
|
||||||
# - '7077:8000'
|
# - '7077:8000'
|
||||||
volumes:
|
volumes:
|
||||||
- ./Downtify_downloads:/downloads
|
- ./Downtify_downloads:/downloads
|
||||||
labels:
|
labels:
|
||||||
- traefik.enable=true
|
- "traefik.enable=true"
|
||||||
- traefik.docker.network=proxy
|
- "traefik.http.services.downtify.loadbalancer.server.port=8000"
|
||||||
- traefik.http.services.downtify.loadbalancer.server.port=8000
|
|
||||||
|
|
||||||
# Prod Router
|
# Prod Router
|
||||||
- traefik.http.routers.downtify.rule=Host(`downtify.forust.xyz`)
|
- "traefik.http.routers.downtify.rule=Host(`downtify.forust.xyz`)"
|
||||||
- traefik.http.routers.downtify.entrypoints=websecure
|
- "traefik.http.routers.downtify.entrypoints=websecure"
|
||||||
- traefik.http.routers.downtify.middlewares=security-chain@file
|
- "traefik.http.routers.downtify.middlewares=security-chain@file"
|
||||||
- traefik.http.routers.downtify.tls=true
|
- "traefik.http.routers.downtify.tls=true"
|
||||||
# Local Router
|
# Local Router
|
||||||
- traefik.http.routers.downtify-local.rule=Host(`downtify.workstation.internal`) || Host(`downtify.internal`)
|
- "traefik.http.routers.downtify-local.rule=Host(`downtify.workstation.internal`)"
|
||||||
- traefik.http.routers.downtify-local.entrypoints=websecure
|
- "traefik.http.routers.downtify-local.entrypoints=websecure"
|
||||||
- traefik.http.routers.downtify-local.middlewares=security-headers@file
|
- "traefik.http.routers.downtify-local.tls=true"
|
||||||
- traefik.http.routers.downtify-local.tls=true
|
|
||||||
# Dev Router
|
# Dev Router
|
||||||
- traefik.http.routers.downtify-dev.rule=Host(`downtify.gigaforust.internal`)
|
- "traefik.http.routers.downtify-dev.rule=Host(`downtify.gigaforust.internal`)"
|
||||||
- traefik.http.routers.downtify-dev.entrypoints=websecure
|
- "traefik.http.routers.downtify-dev.entrypoints=websecure"
|
||||||
- traefik.http.routers.downtify-dev.middlewares=security-chain@file
|
- "traefik.http.routers.downtify-dev.tls=true"
|
||||||
- traefik.http.routers.downtify-dev.tls=true
|
|
||||||
networks:
|
networks:
|
||||||
- proxy
|
- proxy
|
||||||
networks:
|
networks:
|
||||||
|
|||||||
@@ -3,12 +3,13 @@ services:
|
|||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
|
image: gcr.forust.xyz/forust/dtek-notif:latest
|
||||||
|
pull_policy: build
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
- TZ=Europe/Kyiv
|
- TZ=Europe/Kyiv
|
||||||
|
|
||||||
dns:
|
dns:
|
||||||
- 1.1.1.1
|
- 1.1.1.1
|
||||||
- 8.8.8.8
|
- 8.8.8.8
|
||||||
networks:
|
networks:
|
||||||
- default
|
- default
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ services:
|
|||||||
|
|
||||||
session-keeper:
|
session-keeper:
|
||||||
build: ./phpsessid-bot
|
build: ./phpsessid-bot
|
||||||
|
image: gcr.forust.xyz/forust/session-keeper:latest
|
||||||
|
pull_policy: build
|
||||||
env_file: .env
|
env_file: .env
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
depends_on:
|
depends_on:
|
||||||
@@ -31,6 +33,8 @@ services:
|
|||||||
|
|
||||||
webinar-checker:
|
webinar-checker:
|
||||||
build: ./webinar-checker
|
build: ./webinar-checker
|
||||||
|
image: gcr.forust.xyz/forust/webinar-checker:latest
|
||||||
|
pull_policy: build
|
||||||
env_file: .env
|
env_file: .env
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
depends_on:
|
depends_on:
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
import os
|
import os
|
||||||
|
import re
|
||||||
import logging
|
import logging
|
||||||
import redis
|
import redis
|
||||||
import json
|
import json
|
||||||
|
import time
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup, ChatMember
|
from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup, ChatMember
|
||||||
from telegram.constants import ChatType
|
from telegram.constants import ChatType
|
||||||
@@ -30,6 +33,7 @@ def _env(key, default=None):
|
|||||||
EDU_BASE = _env('EDU_URL_BASE', 'https://edu.edu.vn.ua')
|
EDU_BASE = _env('EDU_URL_BASE', 'https://edu.edu.vn.ua')
|
||||||
EDU_WEBINAR_PATH = _env('EDU_URL_WEBINAR', '/webinar/useractive')
|
EDU_WEBINAR_PATH = _env('EDU_URL_WEBINAR', '/webinar/useractive')
|
||||||
WEBINAR_URL = f"{EDU_BASE.rstrip('/')}/{EDU_WEBINAR_PATH.lstrip('/')}"
|
WEBINAR_URL = f"{EDU_BASE.rstrip('/')}/{EDU_WEBINAR_PATH.lstrip('/')}"
|
||||||
|
DIARY_URL = f"{EDU_BASE.rstrip('/')}/user/diary"
|
||||||
|
|
||||||
WEBINAR_CHECK_INTERVAL = int(_env('WEBINAR_CHECK_INTERVAL', 60))
|
WEBINAR_CHECK_INTERVAL = int(_env('WEBINAR_CHECK_INTERVAL', 60))
|
||||||
REDIS_HOST = _env('REDIS_HOST', 'redis')
|
REDIS_HOST = _env('REDIS_HOST', 'redis')
|
||||||
@@ -252,6 +256,205 @@ def get_admin_keyboard(user_id: int):
|
|||||||
]
|
]
|
||||||
return InlineKeyboardMarkup(keyboard)
|
return InlineKeyboardMarkup(keyboard)
|
||||||
|
|
||||||
|
# --- Diary Functions ---
|
||||||
|
|
||||||
|
DIARY_MONTH_NAMES = ['', 'Січня', 'Лютого', 'Березня', 'Квітня', 'Травня', 'Червня',
|
||||||
|
'Липня', 'Серпня', 'Вересня', 'Жовтня', 'Листопада', 'Грудня']
|
||||||
|
|
||||||
|
DIARY_WEEKDAYS_SHORT = ['Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб', 'Нд']
|
||||||
|
|
||||||
|
def get_diary_keyboard():
|
||||||
|
today = datetime.now()
|
||||||
|
keyboard = [
|
||||||
|
[
|
||||||
|
InlineKeyboardButton(f"📌 Сьогодні ({today.day}.{today.month:02d})", callback_data="diary_today"),
|
||||||
|
InlineKeyboardButton("📌 Завтра", callback_data="diary_tomorrow"),
|
||||||
|
],
|
||||||
|
[
|
||||||
|
InlineKeyboardButton("📅 Цей тиждень", callback_data="diary_week"),
|
||||||
|
InlineKeyboardButton("📅 Весь місяць", callback_data="diary_month"),
|
||||||
|
],
|
||||||
|
]
|
||||||
|
return InlineKeyboardMarkup(keyboard)
|
||||||
|
|
||||||
|
def _parse_calendar_html(table_html: str) -> tuple:
|
||||||
|
"""Parse calendar HTML table into (month_text, {day_num: {weekday, events}})."""
|
||||||
|
days = {}
|
||||||
|
weekdays = []
|
||||||
|
rows = re.findall(r'<tr[^>]*>(.*?)</tr>', table_html, re.DOTALL)
|
||||||
|
|
||||||
|
month_text = ''
|
||||||
|
for r_idx, row in enumerate(rows):
|
||||||
|
cells = re.findall(r'<t[dh][^>]*>(.*?)</t[dh]>', row, re.DOTALL)
|
||||||
|
|
||||||
|
if r_idx == 0:
|
||||||
|
# Month navigation row: extract "Травень 2026" from nav text
|
||||||
|
raw = re.sub(r'<[^>]+>', ' ', row).strip()
|
||||||
|
raw = re.sub(r'\s+', ' ', raw)
|
||||||
|
m = re.search(r'([А-Яа-яіїєґ\']+\s*:?\s*\d{4})', raw)
|
||||||
|
if m:
|
||||||
|
month_text = m.group(1).replace(' : ', ' ').strip()
|
||||||
|
else:
|
||||||
|
month_text = raw
|
||||||
|
elif r_idx == 1:
|
||||||
|
# Day names row
|
||||||
|
for cell in cells:
|
||||||
|
name = re.sub(r'<[^>]+>', '', cell).strip()
|
||||||
|
if name:
|
||||||
|
weekdays.append(name)
|
||||||
|
else:
|
||||||
|
# Data rows: each cell = a day
|
||||||
|
for col_idx, cell in enumerate(cells):
|
||||||
|
# Extract day number — first number in the cell text
|
||||||
|
text = re.sub(r'<[^>]+>', ' ', cell).strip()
|
||||||
|
text = re.sub(r'\s+', ' ', text)
|
||||||
|
dm = re.match(r'(\d+)', text)
|
||||||
|
if not dm:
|
||||||
|
continue
|
||||||
|
day_num = dm.group(1)
|
||||||
|
|
||||||
|
# Extract events: title attribute (full name) of ALL <a> tags inside the cell
|
||||||
|
events = []
|
||||||
|
for a_match in re.finditer(r'<a[^>]*>(.*?)</a>', cell, re.DOTALL):
|
||||||
|
a_tag = a_match.group(0)
|
||||||
|
# Prefer the title attribute (contains full name, not truncated)
|
||||||
|
title_m = re.search(r'title\s*=\s*"([^"]*)"', a_tag)
|
||||||
|
if title_m:
|
||||||
|
et = title_m.group(1).strip()
|
||||||
|
else:
|
||||||
|
et = re.sub(r'<[^>]+>', '', a_match.group(1)).strip()
|
||||||
|
if et:
|
||||||
|
events.append(et)
|
||||||
|
|
||||||
|
weekday = weekdays[col_idx] if col_idx < len(weekdays) else ''
|
||||||
|
days[day_num] = {'weekday': weekday, 'events': events}
|
||||||
|
|
||||||
|
return month_text, days
|
||||||
|
|
||||||
|
|
||||||
|
async def fetch_diary_data(phpsessid: str) -> dict | None:
|
||||||
|
logger.info("Fetching diary data via Playwright...")
|
||||||
|
try:
|
||||||
|
async with async_playwright() as p:
|
||||||
|
browser = await p.chromium.connect(PLAYWRIGHT_WS)
|
||||||
|
try:
|
||||||
|
context_browser = await browser.new_context(user_agent=USER_AGENT)
|
||||||
|
await context_browser.add_cookies([{
|
||||||
|
'name': 'PHPSESSID',
|
||||||
|
'value': phpsessid,
|
||||||
|
'domain': 'edu.edu.vn.ua',
|
||||||
|
'path': '/'
|
||||||
|
}])
|
||||||
|
page = await context_browser.new_page()
|
||||||
|
|
||||||
|
try:
|
||||||
|
await page.goto(DIARY_URL, wait_until='domcontentloaded')
|
||||||
|
await page.wait_for_selector('table.calendar', timeout=10000)
|
||||||
|
await page.wait_for_timeout(1500)
|
||||||
|
|
||||||
|
table_html = await page.evaluate("""
|
||||||
|
() => {
|
||||||
|
const t = document.querySelector('table.calendar');
|
||||||
|
return t ? t.outerHTML : null;
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
if not table_html:
|
||||||
|
logger.error("table.calendar not found in DOM")
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Debug: save HTML for troubleshooting
|
||||||
|
try:
|
||||||
|
with open('/tmp/diary_debug.html', 'w', encoding='utf-8') as f:
|
||||||
|
f.write(table_html)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
month_text, days = _parse_calendar_html(table_html)
|
||||||
|
logger.info(f"Diary parsed: month={month_text!r}, days_with_events={sum(1 for d in days.values() if d['events'])}/{len(days)}")
|
||||||
|
|
||||||
|
return {'monthFullText': month_text, 'days': days}
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error parsing diary: {e}")
|
||||||
|
return None
|
||||||
|
finally:
|
||||||
|
await page.close()
|
||||||
|
await context_browser.close()
|
||||||
|
finally:
|
||||||
|
await browser.close()
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Playwright error in diary fetch: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _parse_diary_month(text: str) -> str:
|
||||||
|
match = re.search(r'([А-Яа-яіїєґ\']+\s*:\s*\d{4})', text)
|
||||||
|
if match:
|
||||||
|
return match.group(1).replace(' : ', ' ').strip()
|
||||||
|
return text.strip()
|
||||||
|
|
||||||
|
def format_diary_day(data: dict, day_num: int) -> str:
|
||||||
|
days = data.get('days', {})
|
||||||
|
month_str = _parse_diary_month(data.get('monthFullText', ''))
|
||||||
|
day_data = days.get(str(day_num))
|
||||||
|
lines = [f"📅 <b>{day_num} {month_str}</b>", "─" * 18]
|
||||||
|
if not day_data or not day_data.get('events'):
|
||||||
|
lines.append("Немає подій")
|
||||||
|
else:
|
||||||
|
for e in day_data['events']:
|
||||||
|
lines.append(f"📌 {e}")
|
||||||
|
lines.append(f"\n🔗 {DIARY_URL}")
|
||||||
|
return "\n".join(lines)
|
||||||
|
|
||||||
|
def format_diary_week(data: dict, today: datetime) -> str:
|
||||||
|
days = data.get('days', {})
|
||||||
|
month_str = _parse_diary_month(data.get('monthFullText', ''))
|
||||||
|
monday = today - timedelta(days=today.weekday())
|
||||||
|
sunday = monday + timedelta(days=6)
|
||||||
|
lines = [f"📅 <b>Тиждень {monday.day}.{monday.month} – {sunday.day}.{sunday.month}</b>\n"]
|
||||||
|
for i in range(7):
|
||||||
|
d = monday + timedelta(days=i)
|
||||||
|
day_data = days.get(str(d.day))
|
||||||
|
lines.append(f"─ <b>{DIARY_WEEKDAYS_SHORT[i]} {d.day}.{d.month}</b> ─")
|
||||||
|
if not day_data or not day_data.get('events'):
|
||||||
|
lines.append("Немає подій\n")
|
||||||
|
else:
|
||||||
|
for e in day_data['events']:
|
||||||
|
lines.append(f"📌 {e}")
|
||||||
|
lines.append("")
|
||||||
|
lines.append(f"🔗 {DIARY_URL}")
|
||||||
|
return "\n".join(lines)
|
||||||
|
|
||||||
|
def format_diary_month(data: dict) -> str:
|
||||||
|
days = data.get('days', {})
|
||||||
|
month_str = _parse_diary_month(data.get('monthFullText', ''))
|
||||||
|
lines = [f"📅 <b>{month_str}</b>\n"]
|
||||||
|
for day_num in sorted(days.keys(), key=int):
|
||||||
|
day_data = days[day_num]
|
||||||
|
events = day_data.get('events', [])
|
||||||
|
weekday = day_data.get('weekday', '')
|
||||||
|
lines.append(f"─ <b>{weekday} {day_num}</b> ─")
|
||||||
|
if not events:
|
||||||
|
lines.append("Немає подій\n")
|
||||||
|
else:
|
||||||
|
for e in events:
|
||||||
|
lines.append(f"📌 {e}")
|
||||||
|
lines.append("")
|
||||||
|
lines.append(f"🔗 {DIARY_URL}")
|
||||||
|
return "\n".join(lines)
|
||||||
|
|
||||||
|
async def _get_diary_data(context: ContextTypes.DEFAULT_TYPE) -> dict | None:
|
||||||
|
cached = context.user_data.get('diary_cache')
|
||||||
|
now_ts = time.time()
|
||||||
|
if cached and (now_ts - cached.get('timestamp', 0)) < 300:
|
||||||
|
return cached['data']
|
||||||
|
phpsessid = redis_client.get(KEY_PHPSESSID)
|
||||||
|
if not phpsessid:
|
||||||
|
return None
|
||||||
|
data = await fetch_diary_data(phpsessid)
|
||||||
|
if data:
|
||||||
|
context.user_data['diary_cache'] = {'data': data, 'timestamp': now_ts}
|
||||||
|
return data
|
||||||
|
|
||||||
# --- Command Handlers ---
|
# --- Command Handlers ---
|
||||||
|
|
||||||
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||||
@@ -376,6 +579,74 @@ async def clear_history(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
|||||||
logger.error(f"Failed to clear history: {e}")
|
logger.error(f"Failed to clear history: {e}")
|
||||||
await update.message.reply_text(t(admin_id, 'history_clear_failed'))
|
await update.message.reply_text(t(admin_id, 'history_clear_failed'))
|
||||||
|
|
||||||
|
async def diary_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||||
|
user = update.effective_user
|
||||||
|
chat = update.effective_chat
|
||||||
|
if not is_whitelisted(user.id):
|
||||||
|
await update.message.reply_text(t(user.id, 'access_denied'))
|
||||||
|
return
|
||||||
|
await update.message.reply_text(
|
||||||
|
"📅 <b>Щоденник</b> — виберіть період:",
|
||||||
|
parse_mode='HTML',
|
||||||
|
reply_markup=get_diary_keyboard()
|
||||||
|
)
|
||||||
|
|
||||||
|
async def diary_callback(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||||
|
query = update.callback_query
|
||||||
|
user_id = query.from_user.id
|
||||||
|
await query.answer()
|
||||||
|
|
||||||
|
if user_id != ADMIN_ID:
|
||||||
|
if not is_whitelisted(user_id):
|
||||||
|
await query.edit_message_text("⛔ Доступ заборонено.")
|
||||||
|
return
|
||||||
|
|
||||||
|
data = query.data
|
||||||
|
if data == "diary_refresh":
|
||||||
|
context.user_data.pop('diary_cache', None)
|
||||||
|
await query.edit_message_text("🔄 Завантажую щоденник...")
|
||||||
|
diary_data = await _get_diary_data(context)
|
||||||
|
if not diary_data:
|
||||||
|
await query.edit_message_text("❌ Не вдалося завантажити щоденник. Немає сесії або помилка.")
|
||||||
|
return
|
||||||
|
await query.edit_message_text(
|
||||||
|
"📅 <b>Щоденник</b> — виберіть період:",
|
||||||
|
parse_mode='HTML',
|
||||||
|
reply_markup=get_diary_keyboard()
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
await query.edit_message_text("🔄 Завантажую щоденник...")
|
||||||
|
diary_data = await _get_diary_data(context)
|
||||||
|
if not diary_data:
|
||||||
|
await query.edit_message_text("❌ Не вдалося завантажити щоденник.")
|
||||||
|
return
|
||||||
|
|
||||||
|
today = datetime.now()
|
||||||
|
if data == "diary_today":
|
||||||
|
text = format_diary_day(diary_data, today.day)
|
||||||
|
elif data == "diary_tomorrow":
|
||||||
|
tomorrow = today + timedelta(days=1)
|
||||||
|
if tomorrow.day < today.day:
|
||||||
|
text = "❌ Дані за наступний місяць недоступні. Перейдіть на сайт."
|
||||||
|
else:
|
||||||
|
text = format_diary_day(diary_data, tomorrow.day)
|
||||||
|
elif data == "diary_week":
|
||||||
|
text = format_diary_week(diary_data, today)
|
||||||
|
elif data == "diary_month":
|
||||||
|
text = format_diary_month(diary_data)
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
|
||||||
|
if len(text) > 4096:
|
||||||
|
text = text[:4090] + "\n\n✂️ ...(обрізано)"
|
||||||
|
|
||||||
|
await query.edit_message_text(
|
||||||
|
text,
|
||||||
|
parse_mode='HTML',
|
||||||
|
reply_markup=get_diary_keyboard()
|
||||||
|
)
|
||||||
|
|
||||||
# --- Admin Callbacks ---
|
# --- Admin Callbacks ---
|
||||||
|
|
||||||
async def admin_callback(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
async def admin_callback(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||||
@@ -649,8 +920,10 @@ def main():
|
|||||||
app.add_handler(CommandHandler("adduser", add_user))
|
app.add_handler(CommandHandler("adduser", add_user))
|
||||||
app.add_handler(CommandHandler("removeuser", remove_user))
|
app.add_handler(CommandHandler("removeuser", remove_user))
|
||||||
app.add_handler(CommandHandler("clearhistory", clear_history))
|
app.add_handler(CommandHandler("clearhistory", clear_history))
|
||||||
|
app.add_handler(CommandHandler("diary", diary_command))
|
||||||
|
|
||||||
# Callback handlers - language selection first, then admin panel
|
# Callback handlers - diary first, then language selection, then admin panel
|
||||||
|
app.add_handler(CallbackQueryHandler(diary_callback, pattern="^diary_"))
|
||||||
app.add_handler(CallbackQueryHandler(language_callback, pattern="^lang_"))
|
app.add_handler(CallbackQueryHandler(language_callback, pattern="^lang_"))
|
||||||
app.add_handler(CallbackQueryHandler(admin_callback))
|
app.add_handler(CallbackQueryHandler(admin_callback))
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
services:
|
services:
|
||||||
errorpage:
|
errorpage:
|
||||||
build: .
|
build: .
|
||||||
|
image: gcr.forust.xyz/forust/error-pages:latest
|
||||||
|
pull_policy: build
|
||||||
container_name: error-pages
|
container_name: error-pages
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
# ports:
|
# ports:
|
||||||
@@ -9,11 +11,10 @@ services:
|
|||||||
- proxy
|
- proxy
|
||||||
labels:
|
labels:
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.docker.network=proxy"
|
- "traefik.http.services.error-pages.loadbalancer.server.port=80"
|
||||||
- "traefik.http.services.errorpage.loadbalancer.server.port=80"
|
|
||||||
# Error handler middleware
|
# Error handler middleware
|
||||||
- "traefik.http.middlewares.error-pages.errors.status=400-599"
|
- "traefik.http.middlewares.error-pages.errors.status=400,402-599"
|
||||||
- "traefik.http.middlewares.error-pages.errors.service=errorpage"
|
- "traefik.http.middlewares.error-pages.errors.service=error-pages"
|
||||||
- "traefik.http.middlewares.error-pages.errors.query=/{status}.html"
|
- "traefik.http.middlewares.error-pages.errors.query=/{status}.html"
|
||||||
networks:
|
networks:
|
||||||
proxy:
|
proxy:
|
||||||
|
|||||||
@@ -7,7 +7,175 @@
|
|||||||
<title>403 // Forbidden</title>
|
<title>403 // Forbidden</title>
|
||||||
<script src="https://kit.fontawesome.com/a076d05399.js" crossorigin="anonymous"></script>
|
<script src="https://kit.fontawesome.com/a076d05399.js" crossorigin="anonymous"></script>
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||||
<link rel="stylesheet" href="style.css">
|
<style>
|
||||||
|
/* hidden in a plain sight? */
|
||||||
|
:root {
|
||||||
|
--bg-color: #050505;
|
||||||
|
--text-color: #e0e0e0;
|
||||||
|
--accent: #ffffff;
|
||||||
|
--dim: #666666;
|
||||||
|
--font-mono: 'Courier New', Courier, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: var(--bg-color);
|
||||||
|
color: var(--text-color);
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
line-height: 1.6;
|
||||||
|
font-size: 16px;
|
||||||
|
padding: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--text-color);
|
||||||
|
text-decoration: none;
|
||||||
|
border-bottom: 1px solid var(--dim);
|
||||||
|
transition: all 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
background-color: var(--text-color);
|
||||||
|
color: var(--bg-color);
|
||||||
|
border-color: var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TEXT */
|
||||||
|
h1 {
|
||||||
|
font-size: 2.5rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: -2px;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
border-bottom: 1px solid var(--dim);
|
||||||
|
display: inline-block;
|
||||||
|
padding-right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subtitle {
|
||||||
|
color: var(--dim);
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
border: 0;
|
||||||
|
border-top: 1px dashed var(--dim);
|
||||||
|
margin: 2rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.comment {
|
||||||
|
color: var(--dim);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SECTIONS */
|
||||||
|
section {
|
||||||
|
margin-bottom: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* LISTS */
|
||||||
|
ul {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link-list li {
|
||||||
|
margin-bottom: 0.8rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* STACK GRID */
|
||||||
|
.grid-2 {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skill-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.level {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.special .level {
|
||||||
|
color: var(--text-color);
|
||||||
|
text-shadow: 1px 0 0 red, -1px 0 0 blue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* my dudes */
|
||||||
|
.team-grid {
|
||||||
|
display: flex;
|
||||||
|
gap: 2rem;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.member {
|
||||||
|
text-align: center;
|
||||||
|
width: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
|
background-color: #222;
|
||||||
|
border: 2px solid var(--text-color);
|
||||||
|
margin: 0 auto 10px auto;
|
||||||
|
background-size: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if no avatar added: */
|
||||||
|
.placeholder::before {
|
||||||
|
content: "?";
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 100%;
|
||||||
|
font-size: 2rem;
|
||||||
|
color: var(--dim);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* REPOS */
|
||||||
|
.repo-list li {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* FOOTER */
|
||||||
|
footer {
|
||||||
|
text-align: center;
|
||||||
|
color: var(--dim);
|
||||||
|
font-size: 0.8rem;
|
||||||
|
/* flag{why-are-you-here?} */
|
||||||
|
margin-top: 4rem;
|
||||||
|
}
|
||||||
|
/* SMTH RESPONSIVE */
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.grid-2 {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
@@ -24,7 +192,7 @@
|
|||||||
<h2>./error_message</h2>
|
<h2>./error_message</h2>
|
||||||
<p>You do not have permission to access this resource.</p>
|
<p>You do not have permission to access this resource.</p>
|
||||||
<br>
|
<br>
|
||||||
<p>Check the <a href="https://uptime.forust.xyz/status/forust">System Status</a> if you believe this is an
|
<p>Check the <a href="https://status.forust.xyz">System Status</a> if you believe this is an
|
||||||
error.</p>
|
error.</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,175 @@
|
|||||||
<title>404 // Not Found</title>
|
<title>404 // Not Found</title>
|
||||||
<script src="https://kit.fontawesome.com/a076d05399.js" crossorigin="anonymous"></script>
|
<script src="https://kit.fontawesome.com/a076d05399.js" crossorigin="anonymous"></script>
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||||
<link rel="stylesheet" href="style.css">
|
<style>
|
||||||
|
/* hidden in a plain sight? */
|
||||||
|
:root {
|
||||||
|
--bg-color: #050505;
|
||||||
|
--text-color: #e0e0e0;
|
||||||
|
--accent: #ffffff;
|
||||||
|
--dim: #666666;
|
||||||
|
--font-mono: 'Courier New', Courier, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: var(--bg-color);
|
||||||
|
color: var(--text-color);
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
line-height: 1.6;
|
||||||
|
font-size: 16px;
|
||||||
|
padding: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--text-color);
|
||||||
|
text-decoration: none;
|
||||||
|
border-bottom: 1px solid var(--dim);
|
||||||
|
transition: all 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
background-color: var(--text-color);
|
||||||
|
color: var(--bg-color);
|
||||||
|
border-color: var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TEXT */
|
||||||
|
h1 {
|
||||||
|
font-size: 2.5rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: -2px;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
border-bottom: 1px solid var(--dim);
|
||||||
|
display: inline-block;
|
||||||
|
padding-right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subtitle {
|
||||||
|
color: var(--dim);
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
border: 0;
|
||||||
|
border-top: 1px dashed var(--dim);
|
||||||
|
margin: 2rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.comment {
|
||||||
|
color: var(--dim);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SECTIONS */
|
||||||
|
section {
|
||||||
|
margin-bottom: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* LISTS */
|
||||||
|
ul {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link-list li {
|
||||||
|
margin-bottom: 0.8rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* STACK GRID */
|
||||||
|
.grid-2 {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skill-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.level {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.special .level {
|
||||||
|
color: var(--text-color);
|
||||||
|
text-shadow: 1px 0 0 red, -1px 0 0 blue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* my dudes */
|
||||||
|
.team-grid {
|
||||||
|
display: flex;
|
||||||
|
gap: 2rem;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.member {
|
||||||
|
text-align: center;
|
||||||
|
width: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
|
background-color: #222;
|
||||||
|
border: 2px solid var(--text-color);
|
||||||
|
margin: 0 auto 10px auto;
|
||||||
|
background-size: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if no avatar added: */
|
||||||
|
.placeholder::before {
|
||||||
|
content: "?";
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 100%;
|
||||||
|
font-size: 2rem;
|
||||||
|
color: var(--dim);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* REPOS */
|
||||||
|
.repo-list li {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* FOOTER */
|
||||||
|
footer {
|
||||||
|
text-align: center;
|
||||||
|
color: var(--dim);
|
||||||
|
font-size: 0.8rem;
|
||||||
|
/* flag{why-are-you-here?} */
|
||||||
|
margin-top: 4rem;
|
||||||
|
}
|
||||||
|
/* SMTH RESPONSIVE */
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.grid-2 {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
@@ -24,7 +192,7 @@
|
|||||||
<h2>./error_message</h2>
|
<h2>./error_message</h2>
|
||||||
<p>The page you are looking for does not exist or has been moved.</p>
|
<p>The page you are looking for does not exist or has been moved.</p>
|
||||||
<br>
|
<br>
|
||||||
<p>Check the <a href="https://uptime.forust.xyz/status/forust">System Status</a> if you believe this is an error.</p>
|
<p>Check the <a href="https://status.forust.xyz">System Status</a> if you believe this is an error.</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<footer>
|
<footer>
|
||||||
|
|||||||
@@ -7,7 +7,175 @@
|
|||||||
<title>500 // Server Error</title>
|
<title>500 // Server Error</title>
|
||||||
<script src="https://kit.fontawesome.com/a076d05399.js" crossorigin="anonymous"></script>
|
<script src="https://kit.fontawesome.com/a076d05399.js" crossorigin="anonymous"></script>
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||||
<link rel="stylesheet" href="style.css">
|
<style>
|
||||||
|
/* hidden in a plain sight? */
|
||||||
|
:root {
|
||||||
|
--bg-color: #050505;
|
||||||
|
--text-color: #e0e0e0;
|
||||||
|
--accent: #ffffff;
|
||||||
|
--dim: #666666;
|
||||||
|
--font-mono: 'Courier New', Courier, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: var(--bg-color);
|
||||||
|
color: var(--text-color);
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
line-height: 1.6;
|
||||||
|
font-size: 16px;
|
||||||
|
padding: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--text-color);
|
||||||
|
text-decoration: none;
|
||||||
|
border-bottom: 1px solid var(--dim);
|
||||||
|
transition: all 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
background-color: var(--text-color);
|
||||||
|
color: var(--bg-color);
|
||||||
|
border-color: var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TEXT */
|
||||||
|
h1 {
|
||||||
|
font-size: 2.5rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: -2px;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
border-bottom: 1px solid var(--dim);
|
||||||
|
display: inline-block;
|
||||||
|
padding-right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subtitle {
|
||||||
|
color: var(--dim);
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
border: 0;
|
||||||
|
border-top: 1px dashed var(--dim);
|
||||||
|
margin: 2rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.comment {
|
||||||
|
color: var(--dim);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SECTIONS */
|
||||||
|
section {
|
||||||
|
margin-bottom: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* LISTS */
|
||||||
|
ul {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link-list li {
|
||||||
|
margin-bottom: 0.8rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* STACK GRID */
|
||||||
|
.grid-2 {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skill-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.level {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.special .level {
|
||||||
|
color: var(--text-color);
|
||||||
|
text-shadow: 1px 0 0 red, -1px 0 0 blue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* my dudes */
|
||||||
|
.team-grid {
|
||||||
|
display: flex;
|
||||||
|
gap: 2rem;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.member {
|
||||||
|
text-align: center;
|
||||||
|
width: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
|
background-color: #222;
|
||||||
|
border: 2px solid var(--text-color);
|
||||||
|
margin: 0 auto 10px auto;
|
||||||
|
background-size: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if no avatar added: */
|
||||||
|
.placeholder::before {
|
||||||
|
content: "?";
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 100%;
|
||||||
|
font-size: 2rem;
|
||||||
|
color: var(--dim);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* REPOS */
|
||||||
|
.repo-list li {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* FOOTER */
|
||||||
|
footer {
|
||||||
|
text-align: center;
|
||||||
|
color: var(--dim);
|
||||||
|
font-size: 0.8rem;
|
||||||
|
/* flag{why-are-you-here?} */
|
||||||
|
margin-top: 4rem;
|
||||||
|
}
|
||||||
|
/* SMTH RESPONSIVE */
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.grid-2 {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
@@ -24,7 +192,7 @@
|
|||||||
<h2>./error_message</h2>
|
<h2>./error_message</h2>
|
||||||
<p>Something went wrong on our end. We are working to fix it.</p>
|
<p>Something went wrong on our end. We are working to fix it.</p>
|
||||||
<br>
|
<br>
|
||||||
<p>Check the <a href="https://uptime.forust.xyz/status/forust">System Status</a> for more information.</p>
|
<p>Check the <a href="https://status.forust.xyz">System Status</a> for more information.</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<footer>
|
<footer>
|
||||||
|
|||||||
@@ -7,7 +7,175 @@
|
|||||||
<title>502 // Bad Gateway</title>
|
<title>502 // Bad Gateway</title>
|
||||||
<script src="https://kit.fontawesome.com/a076d05399.js" crossorigin="anonymous"></script>
|
<script src="https://kit.fontawesome.com/a076d05399.js" crossorigin="anonymous"></script>
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||||
<link rel="stylesheet" href="style.css">
|
<style>
|
||||||
|
/* hidden in a plain sight? */
|
||||||
|
:root {
|
||||||
|
--bg-color: #050505;
|
||||||
|
--text-color: #e0e0e0;
|
||||||
|
--accent: #ffffff;
|
||||||
|
--dim: #666666;
|
||||||
|
--font-mono: 'Courier New', Courier, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: var(--bg-color);
|
||||||
|
color: var(--text-color);
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
line-height: 1.6;
|
||||||
|
font-size: 16px;
|
||||||
|
padding: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--text-color);
|
||||||
|
text-decoration: none;
|
||||||
|
border-bottom: 1px solid var(--dim);
|
||||||
|
transition: all 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
background-color: var(--text-color);
|
||||||
|
color: var(--bg-color);
|
||||||
|
border-color: var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TEXT */
|
||||||
|
h1 {
|
||||||
|
font-size: 2.5rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: -2px;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
border-bottom: 1px solid var(--dim);
|
||||||
|
display: inline-block;
|
||||||
|
padding-right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subtitle {
|
||||||
|
color: var(--dim);
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
border: 0;
|
||||||
|
border-top: 1px dashed var(--dim);
|
||||||
|
margin: 2rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.comment {
|
||||||
|
color: var(--dim);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SECTIONS */
|
||||||
|
section {
|
||||||
|
margin-bottom: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* LISTS */
|
||||||
|
ul {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link-list li {
|
||||||
|
margin-bottom: 0.8rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* STACK GRID */
|
||||||
|
.grid-2 {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skill-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.level {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.special .level {
|
||||||
|
color: var(--text-color);
|
||||||
|
text-shadow: 1px 0 0 red, -1px 0 0 blue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* my dudes */
|
||||||
|
.team-grid {
|
||||||
|
display: flex;
|
||||||
|
gap: 2rem;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.member {
|
||||||
|
text-align: center;
|
||||||
|
width: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
|
background-color: #222;
|
||||||
|
border: 2px solid var(--text-color);
|
||||||
|
margin: 0 auto 10px auto;
|
||||||
|
background-size: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if no avatar added: */
|
||||||
|
.placeholder::before {
|
||||||
|
content: "?";
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 100%;
|
||||||
|
font-size: 2rem;
|
||||||
|
color: var(--dim);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* REPOS */
|
||||||
|
.repo-list li {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* FOOTER */
|
||||||
|
footer {
|
||||||
|
text-align: center;
|
||||||
|
color: var(--dim);
|
||||||
|
font-size: 0.8rem;
|
||||||
|
/* flag{why-are-you-here?} */
|
||||||
|
margin-top: 4rem;
|
||||||
|
}
|
||||||
|
/* SMTH RESPONSIVE */
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.grid-2 {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
@@ -24,7 +192,7 @@
|
|||||||
<h2>./error_message</h2>
|
<h2>./error_message</h2>
|
||||||
<p>The server received an invalid response from the upstream server.</p>
|
<p>The server received an invalid response from the upstream server.</p>
|
||||||
<br>
|
<br>
|
||||||
<p>Check the <a href="https://uptime.forust.xyz/status/forust">System Status</a> for more information.</p>
|
<p>Check the <a href="https://status.forust.xyz">System Status</a> for more information.</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<footer>
|
<footer>
|
||||||
|
|||||||
@@ -7,7 +7,175 @@
|
|||||||
<title>503 // Service Unavailable</title>
|
<title>503 // Service Unavailable</title>
|
||||||
<script src="https://kit.fontawesome.com/a076d05399.js" crossorigin="anonymous"></script>
|
<script src="https://kit.fontawesome.com/a076d05399.js" crossorigin="anonymous"></script>
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||||
<link rel="stylesheet" href="style.css">
|
<style>
|
||||||
|
/* hidden in a plain sight? */
|
||||||
|
:root {
|
||||||
|
--bg-color: #050505;
|
||||||
|
--text-color: #e0e0e0;
|
||||||
|
--accent: #ffffff;
|
||||||
|
--dim: #666666;
|
||||||
|
--font-mono: 'Courier New', Courier, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: var(--bg-color);
|
||||||
|
color: var(--text-color);
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
line-height: 1.6;
|
||||||
|
font-size: 16px;
|
||||||
|
padding: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--text-color);
|
||||||
|
text-decoration: none;
|
||||||
|
border-bottom: 1px solid var(--dim);
|
||||||
|
transition: all 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
background-color: var(--text-color);
|
||||||
|
color: var(--bg-color);
|
||||||
|
border-color: var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TEXT */
|
||||||
|
h1 {
|
||||||
|
font-size: 2.5rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: -2px;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
border-bottom: 1px solid var(--dim);
|
||||||
|
display: inline-block;
|
||||||
|
padding-right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subtitle {
|
||||||
|
color: var(--dim);
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
border: 0;
|
||||||
|
border-top: 1px dashed var(--dim);
|
||||||
|
margin: 2rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.comment {
|
||||||
|
color: var(--dim);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SECTIONS */
|
||||||
|
section {
|
||||||
|
margin-bottom: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* LISTS */
|
||||||
|
ul {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link-list li {
|
||||||
|
margin-bottom: 0.8rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* STACK GRID */
|
||||||
|
.grid-2 {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skill-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.level {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.special .level {
|
||||||
|
color: var(--text-color);
|
||||||
|
text-shadow: 1px 0 0 red, -1px 0 0 blue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* my dudes */
|
||||||
|
.team-grid {
|
||||||
|
display: flex;
|
||||||
|
gap: 2rem;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.member {
|
||||||
|
text-align: center;
|
||||||
|
width: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
|
background-color: #222;
|
||||||
|
border: 2px solid var(--text-color);
|
||||||
|
margin: 0 auto 10px auto;
|
||||||
|
background-size: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if no avatar added: */
|
||||||
|
.placeholder::before {
|
||||||
|
content: "?";
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 100%;
|
||||||
|
font-size: 2rem;
|
||||||
|
color: var(--dim);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* REPOS */
|
||||||
|
.repo-list li {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* FOOTER */
|
||||||
|
footer {
|
||||||
|
text-align: center;
|
||||||
|
color: var(--dim);
|
||||||
|
font-size: 0.8rem;
|
||||||
|
/* flag{why-are-you-here?} */
|
||||||
|
margin-top: 4rem;
|
||||||
|
}
|
||||||
|
/* SMTH RESPONSIVE */
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.grid-2 {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
@@ -24,7 +192,7 @@
|
|||||||
<h2>./error_message</h2>
|
<h2>./error_message</h2>
|
||||||
<p>The server is currently unable to handle the request due to maintenance or overload.</p>
|
<p>The server is currently unable to handle the request due to maintenance or overload.</p>
|
||||||
<br>
|
<br>
|
||||||
<p>Check the <a href="https://uptime.forust.xyz/status/forust">System Status</a> for more information.</p>
|
<p>Check the <a href="https://status.forust.xyz">System Status</a> for more information.</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<footer>
|
<footer>
|
||||||
|
|||||||
@@ -7,7 +7,175 @@
|
|||||||
<title>504 // Gateway Timeout</title>
|
<title>504 // Gateway Timeout</title>
|
||||||
<script src="https://kit.fontawesome.com/a076d05399.js" crossorigin="anonymous"></script>
|
<script src="https://kit.fontawesome.com/a076d05399.js" crossorigin="anonymous"></script>
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||||
<link rel="stylesheet" href="style.css">
|
<style>
|
||||||
|
/* hidden in a plain sight? */
|
||||||
|
:root {
|
||||||
|
--bg-color: #050505;
|
||||||
|
--text-color: #e0e0e0;
|
||||||
|
--accent: #ffffff;
|
||||||
|
--dim: #666666;
|
||||||
|
--font-mono: 'Courier New', Courier, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: var(--bg-color);
|
||||||
|
color: var(--text-color);
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
line-height: 1.6;
|
||||||
|
font-size: 16px;
|
||||||
|
padding: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--text-color);
|
||||||
|
text-decoration: none;
|
||||||
|
border-bottom: 1px solid var(--dim);
|
||||||
|
transition: all 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
background-color: var(--text-color);
|
||||||
|
color: var(--bg-color);
|
||||||
|
border-color: var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TEXT */
|
||||||
|
h1 {
|
||||||
|
font-size: 2.5rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: -2px;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
border-bottom: 1px solid var(--dim);
|
||||||
|
display: inline-block;
|
||||||
|
padding-right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subtitle {
|
||||||
|
color: var(--dim);
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
border: 0;
|
||||||
|
border-top: 1px dashed var(--dim);
|
||||||
|
margin: 2rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.comment {
|
||||||
|
color: var(--dim);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SECTIONS */
|
||||||
|
section {
|
||||||
|
margin-bottom: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* LISTS */
|
||||||
|
ul {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link-list li {
|
||||||
|
margin-bottom: 0.8rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* STACK GRID */
|
||||||
|
.grid-2 {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skill-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.level {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.special .level {
|
||||||
|
color: var(--text-color);
|
||||||
|
text-shadow: 1px 0 0 red, -1px 0 0 blue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* my dudes */
|
||||||
|
.team-grid {
|
||||||
|
display: flex;
|
||||||
|
gap: 2rem;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.member {
|
||||||
|
text-align: center;
|
||||||
|
width: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
|
background-color: #222;
|
||||||
|
border: 2px solid var(--text-color);
|
||||||
|
margin: 0 auto 10px auto;
|
||||||
|
background-size: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if no avatar added: */
|
||||||
|
.placeholder::before {
|
||||||
|
content: "?";
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 100%;
|
||||||
|
font-size: 2rem;
|
||||||
|
color: var(--dim);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* REPOS */
|
||||||
|
.repo-list li {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* FOOTER */
|
||||||
|
footer {
|
||||||
|
text-align: center;
|
||||||
|
color: var(--dim);
|
||||||
|
font-size: 0.8rem;
|
||||||
|
/* flag{why-are-you-here?} */
|
||||||
|
margin-top: 4rem;
|
||||||
|
}
|
||||||
|
/* SMTH RESPONSIVE */
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.grid-2 {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
@@ -24,7 +192,7 @@
|
|||||||
<h2>./error_message</h2>
|
<h2>./error_message</h2>
|
||||||
<p>The server did not receive a timely response from the upstream server.</p>
|
<p>The server did not receive a timely response from the upstream server.</p>
|
||||||
<br>
|
<br>
|
||||||
<p>Check the <a href="https://uptime.forust.xyz/status/forust">System Status</a> for more information.</p>
|
<p>Check the <a href="https://status.forust.xyz">System Status</a> for more information.</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<footer>
|
<footer>
|
||||||
|
|||||||
@@ -1,167 +0,0 @@
|
|||||||
/* hidden in a plain sight? */
|
|
||||||
:root {
|
|
||||||
--bg-color: #050505;
|
|
||||||
--text-color: #e0e0e0;
|
|
||||||
--accent: #ffffff;
|
|
||||||
--dim: #666666;
|
|
||||||
--font-mono: 'Courier New', Courier, monospace;
|
|
||||||
}
|
|
||||||
|
|
||||||
* {
|
|
||||||
box-sizing: border-box;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
background-color: var(--bg-color);
|
|
||||||
color: var(--text-color);
|
|
||||||
font-family: var(--font-mono);
|
|
||||||
line-height: 1.6;
|
|
||||||
font-size: 16px;
|
|
||||||
padding: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: var(--text-color);
|
|
||||||
text-decoration: none;
|
|
||||||
border-bottom: 1px solid var(--dim);
|
|
||||||
transition: all 0.2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:hover {
|
|
||||||
background-color: var(--text-color);
|
|
||||||
color: var(--bg-color);
|
|
||||||
border-color: var(--text-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
|
||||||
max-width: 800px;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* TEXT */
|
|
||||||
h1 {
|
|
||||||
font-size: 2.5rem;
|
|
||||||
text-transform: uppercase;
|
|
||||||
letter-spacing: -2px;
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
font-size: 1.2rem;
|
|
||||||
margin-bottom: 1.5rem;
|
|
||||||
border-bottom: 1px solid var(--dim);
|
|
||||||
display: inline-block;
|
|
||||||
padding-right: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.subtitle {
|
|
||||||
color: var(--dim);
|
|
||||||
margin-bottom: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
hr {
|
|
||||||
border: 0;
|
|
||||||
border-top: 1px dashed var(--dim);
|
|
||||||
margin: 2rem 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.comment {
|
|
||||||
color: var(--dim);
|
|
||||||
font-size: 0.9rem;
|
|
||||||
margin-left: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* SECTIONS */
|
|
||||||
section {
|
|
||||||
margin-bottom: 3rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* LISTS */
|
|
||||||
ul {
|
|
||||||
list-style: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.link-list li {
|
|
||||||
margin-bottom: 0.8rem;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* STACK GRID */
|
|
||||||
.grid-2 {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 1fr 1fr;
|
|
||||||
gap: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.skill-item {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.level {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.special .level {
|
|
||||||
color: var(--text-color);
|
|
||||||
text-shadow: 1px 0 0 red, -1px 0 0 blue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* my dudes */
|
|
||||||
.team-grid {
|
|
||||||
display: flex;
|
|
||||||
gap: 2rem;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
margin-top: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.member {
|
|
||||||
text-align: center;
|
|
||||||
width: 100px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.avatar {
|
|
||||||
width: 80px;
|
|
||||||
height: 80px;
|
|
||||||
background-color: #222;
|
|
||||||
border: 2px solid var(--text-color);
|
|
||||||
margin: 0 auto 10px auto;
|
|
||||||
background-size: cover;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* if no avatar added: */
|
|
||||||
.placeholder::before {
|
|
||||||
content: "?";
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
height: 100%;
|
|
||||||
font-size: 2rem;
|
|
||||||
color: var(--dim);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* REPOS */
|
|
||||||
.repo-list li {
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* FOOTER */
|
|
||||||
footer {
|
|
||||||
text-align: center;
|
|
||||||
color: var(--dim);
|
|
||||||
font-size: 0.8rem;
|
|
||||||
/* flag{why-are-you-here?} */
|
|
||||||
margin-top: 4rem;
|
|
||||||
}
|
|
||||||
/* SMTH RESPONSIVE */
|
|
||||||
@media (max-width: 600px) {
|
|
||||||
.grid-2 {
|
|
||||||
grid-template-columns: 1fr;
|
|
||||||
gap: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
services:
|
services:
|
||||||
server:
|
server:
|
||||||
image: docker.gitea.com/gitea:1.25.1
|
image: docker.gitea.com/gitea:1.26
|
||||||
container_name: gitea
|
container_name: gitea
|
||||||
restart: always
|
restart: always
|
||||||
environment:
|
environment:
|
||||||
@@ -31,28 +31,29 @@ services:
|
|||||||
- /etc/localtime:/etc/localtime:ro
|
- /etc/localtime:/etc/localtime:ro
|
||||||
labels:
|
labels:
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.docker.network=proxy"
|
|
||||||
- "traefik.http.services.gitea.loadbalancer.server.port=3000"
|
- "traefik.http.services.gitea.loadbalancer.server.port=3000"
|
||||||
|
|
||||||
# Prod Router
|
# Prod Router
|
||||||
- "traefik.http.routers.gitea.rule=Host(`gitea.forust.xyz`)"
|
- "traefik.http.routers.gitea.rule=Host(`gitea.forust.xyz`)"
|
||||||
- "traefik.http.routers.gitea.entrypoints=websecure"
|
- "traefik.http.routers.gitea.entrypoints=websecure"
|
||||||
- "traefik.http.routers.gitea.middlewares=security-headers@file"
|
|
||||||
- "traefik.http.routers.gitea.tls=true"
|
- "traefik.http.routers.gitea.tls=true"
|
||||||
# Local Router
|
# Local Router
|
||||||
- "traefik.http.routers.gitea-local.rule=Host(`gitea.workstation.internal`)"
|
- "traefik.http.routers.gitea-local.rule=Host(`gitea.workstation.internal`)"
|
||||||
- "traefik.http.routers.gitea-local.entrypoints=websecure"
|
- "traefik.http.routers.gitea-local.entrypoints=websecure"
|
||||||
- "traefik.http.routers.gitea-local.middlewares=security-headers@file"
|
|
||||||
- "traefik.http.routers.gitea-local.tls=true"
|
- "traefik.http.routers.gitea-local.tls=true"
|
||||||
# Dev Router
|
# Dev Router
|
||||||
- "traefik.http.routers.gitea-dev.rule=Host(`gitea.gigaforust.internal`)"
|
- "traefik.http.routers.gitea-dev.rule=Host(`gitea.gigaforust.internal`)"
|
||||||
- "traefik.http.routers.gitea-dev.entrypoints=websecure"
|
- "traefik.http.routers.gitea-dev.entrypoints=websecure"
|
||||||
- "traefik.http.routers.gitea-dev.middlewares=security-headers@file"
|
|
||||||
- "traefik.http.routers.gitea-dev.tls=true"
|
- "traefik.http.routers.gitea-dev.tls=true"
|
||||||
# SSH Router
|
# SSH Router
|
||||||
- "traefik.tcp.services.gitea.loadbalancer.server.port=22"
|
- "traefik.tcp.services.gitea.loadbalancer.server.port=22"
|
||||||
- "traefik.tcp.routers.gitea.entrypoints=ssh"
|
- "traefik.tcp.routers.gitea.entrypoints=ssh"
|
||||||
- "traefik.tcp.routers.gitea.rule=HostSNI(`*`)"
|
- "traefik.tcp.routers.gitea.rule=HostSNI(`*`)"
|
||||||
|
# Gitea container registry Router
|
||||||
|
- "traefik.http.routers.gitea-registry.rule=Host(`gcr.forust.xyz`) && PathPrefix(`/v2`)"
|
||||||
|
- "traefik.http.routers.gitea-registry.entrypoints=websecure"
|
||||||
|
- "traefik.http.routers.gitea-registry.tls.certresolver=letsencrypt"
|
||||||
|
- "traefik.http.routers.gitea-registry.tls=true"
|
||||||
ports:
|
ports:
|
||||||
- "2221:22"
|
- "2221:22"
|
||||||
networks:
|
networks:
|
||||||
|
|||||||
@@ -12,22 +12,19 @@ services:
|
|||||||
labels:
|
labels:
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.docker.network=proxy"
|
- "traefik.docker.network=proxy"
|
||||||
# FIXME: traefik.services.glance.loadbalancer.server.port ??
|
- "traefik.services.glance.loadbalancer.server.port=8080"
|
||||||
|
|
||||||
# Prod Router
|
# Prod Router
|
||||||
- "traefik.http.routers.glance.rule=Host(`glance.forust.xyz`)"
|
- "traefik.http.routers.glance.rule=Host(`glance.forust.xyz`)"
|
||||||
- "traefik.http.routers.glance.entrypoints=websecure"
|
- "traefik.http.routers.glance.entrypoints=websecure"
|
||||||
- "traefik.http.routers.glance.middlewares=security-headers@file"
|
|
||||||
- "traefik.http.routers.glance.tls=true"
|
- "traefik.http.routers.glance.tls=true"
|
||||||
# Local Router
|
# Local Router
|
||||||
- "traefik.http.routers.glance-local.rule=Host(`glance.workstation.internal`)"
|
- "traefik.http.routers.glance-local.rule=Host(`glance.workstation.internal`)"
|
||||||
- "traefik.http.routers.glance-local.entrypoints=websecure"
|
- "traefik.http.routers.glance-local.entrypoints=websecure"
|
||||||
- "traefik.http.routers.glance-local.middlewares=security-headers@file"
|
|
||||||
- "traefik.http.routers.glance-local.tls=true"
|
- "traefik.http.routers.glance-local.tls=true"
|
||||||
# Dev Router
|
# Dev Router
|
||||||
- "traefik.http.routers.glance-dev.rule=Host(`glance.gigaforust.internal`)"
|
- "traefik.http.routers.glance-dev.rule=Host(`glance.gigaforust.internal`)"
|
||||||
- "traefik.http.routers.glance-dev.entrypoints=websecure"
|
- "traefik.http.routers.glance-dev.entrypoints=websecure"
|
||||||
- "traefik.http.routers.glance-dev.middlewares=security-headers@file"
|
|
||||||
- "traefik.http.routers.glance-dev.tls=true"
|
- "traefik.http.routers.glance-dev.tls=true"
|
||||||
networks:
|
networks:
|
||||||
- proxy
|
- proxy
|
||||||
|
|||||||
@@ -9,10 +9,10 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- ./config/headscale.yaml:/etc/headscale/config.yaml
|
- ./config/headscale.yaml:/etc/headscale/config.yaml
|
||||||
- data:/var/lib/headscale
|
- data:/var/lib/headscale
|
||||||
|
- ./config/policy.json:/var/lib/headscale/policy.json
|
||||||
labels:
|
labels:
|
||||||
- "me.tale.headplane.target: headscale"
|
- "me.tale.headplane.target: headscale"
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.docker.network=proxy"
|
|
||||||
- "traefik.http.services.headscale.loadbalancer.server.port=8080"
|
- "traefik.http.services.headscale.loadbalancer.server.port=8080"
|
||||||
- "traefik.http.services.headscale-metrics.loadbalancer.server.port=9090"
|
- "traefik.http.services.headscale-metrics.loadbalancer.server.port=9090"
|
||||||
|
|
||||||
@@ -62,12 +62,28 @@ services:
|
|||||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||||
networks:
|
networks:
|
||||||
- proxy
|
- proxy
|
||||||
healthcheck:
|
web:
|
||||||
test: [ "CMD", "/bin/hp_healthcheck" ]
|
image: goodieshq/headscale-admin:latest
|
||||||
interval: 30s
|
restart: unless-stopped
|
||||||
timeout: 5s
|
labels:
|
||||||
start_period: 5s
|
- "traefik.enable=true"
|
||||||
retries: 3
|
- "traefik.http.services.headscale-ui.loadbalancer.server.port=80"
|
||||||
|
|
||||||
|
# Prod Router
|
||||||
|
- "traefik.http.routers.headscale-ui.rule=Host(`hs.forust.xyz`) && PathPrefix(`/admin`)"
|
||||||
|
- "traefik.http.routers.headscale-ui.entrypoints=websecure"
|
||||||
|
- "traefik.http.routers.headscale-ui.middlewares=security-chain@file"
|
||||||
|
- "traefik.http.routers.headscale-ui.tls=true"
|
||||||
|
# Local Router
|
||||||
|
- "traefik.http.routers.headscale-ui-local.rule=Host(`hs.workstation.internal`) && PathPrefix(`/admin`)"
|
||||||
|
- "traefik.http.routers.headscale-ui-local.entrypoints=websecure"
|
||||||
|
- "traefik.http.routers.headscale-ui-local.tls=true"
|
||||||
|
# Dev Router
|
||||||
|
- "traefik.http.routers.headscale-ui-dev.rule=Host(`hs.gigaforust.internal`) && PathPrefix(`/admin`)"
|
||||||
|
- "traefik.http.routers.headscale-ui-dev.entrypoints=websecure"
|
||||||
|
- "traefik.http.routers.headscale-ui-dev.tls=true"
|
||||||
|
networks:
|
||||||
|
- proxy
|
||||||
volumes:
|
volumes:
|
||||||
data:
|
data:
|
||||||
headplane-data:
|
headplane-data:
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"groups": {
|
||||||
|
"group:admin": [
|
||||||
|
"admin@"
|
||||||
|
],
|
||||||
|
"group:users": []
|
||||||
|
},
|
||||||
|
"tagOwners": {},
|
||||||
|
"hosts": {},
|
||||||
|
"acls": [
|
||||||
|
{
|
||||||
|
"#ha-meta": {
|
||||||
|
"name": "users",
|
||||||
|
"open": true
|
||||||
|
},
|
||||||
|
"action": "accept",
|
||||||
|
"src": [
|
||||||
|
"autogroup:member"
|
||||||
|
],
|
||||||
|
"dst": [
|
||||||
|
"autogroup:self:*"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ssh": []
|
||||||
|
}
|
||||||
@@ -3,6 +3,8 @@ services:
|
|||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: Dockerfile.forust
|
dockerfile: Dockerfile.forust
|
||||||
|
image: gcr.forust.xyz/forust/forust-homepage:latest
|
||||||
|
pull_policy: build
|
||||||
# ports:
|
# ports:
|
||||||
# - "8085:80"
|
# - "8085:80"
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
@@ -12,28 +14,26 @@ services:
|
|||||||
- proxy
|
- proxy
|
||||||
labels:
|
labels:
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.docker.network=proxy"
|
|
||||||
- "traefik.http.services.forust-homepage.loadbalancer.server.port=80"
|
- "traefik.http.services.forust-homepage.loadbalancer.server.port=80"
|
||||||
|
|
||||||
# Prod Router
|
# Prod Router
|
||||||
- "traefik.http.routers.forust-homepage.rule=Host(`forust.xyz`)"
|
- "traefik.http.routers.forust-homepage.rule=Host(`forust.xyz`)"
|
||||||
- "traefik.http.routers.forust-homepage.entrypoints=websecure"
|
- "traefik.http.routers.forust-homepage.entrypoints=websecure"
|
||||||
- "traefik.http.routers.forust-homepage.middlewares=security-headers@file"
|
|
||||||
- "traefik.http.routers.forust-homepage.tls=true"
|
- "traefik.http.routers.forust-homepage.tls=true"
|
||||||
# Local Router
|
# Local Router
|
||||||
- "traefik.http.routers.forust-homepage-local.rule=Host(`landing.workstation.internal`)"
|
- "traefik.http.routers.forust-homepage-local.rule=Host(`landing.workstation.internal`)"
|
||||||
- "traefik.http.routers.forust-homepage-local.entrypoints=websecure"
|
- "traefik.http.routers.forust-homepage-local.entrypoints=websecure"
|
||||||
- "traefik.http.routers.forust-homepage-local.middlewares=security-headers@file"
|
|
||||||
- "traefik.http.routers.forust-homepage-local.tls=true"
|
- "traefik.http.routers.forust-homepage-local.tls=true"
|
||||||
# Dev Router
|
# Dev Router
|
||||||
- "traefik.http.routers.forust-homepage-dev.rule=Host(`landing.gigaforust.internal`)"
|
- "traefik.http.routers.forust-homepage-dev.rule=Host(`landing.gigaforust.internal`)"
|
||||||
- "traefik.http.routers.forust-homepage-dev.entrypoints=websecure"
|
- "traefik.http.routers.forust-homepage-dev.entrypoints=websecure"
|
||||||
- "traefik.http.routers.forust-homepage-dev.middlewares=security-headers@file"
|
|
||||||
- "traefik.http.routers.forust-homepage-dev.tls=true"
|
- "traefik.http.routers.forust-homepage-dev.tls=true"
|
||||||
xdfnx:
|
xdfnx:
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: Dockerfile.xdfnx
|
dockerfile: Dockerfile.xdfnx
|
||||||
|
image: gcr.forust.xyz/forust/xdfnx-homepage:latest
|
||||||
|
pull_policy: build
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
# ports:
|
# ports:
|
||||||
# - "8086:80"
|
# - "8086:80"
|
||||||
@@ -41,23 +41,20 @@ services:
|
|||||||
- ./xdfnx_files:/usr/share/nginx/html
|
- ./xdfnx_files:/usr/share/nginx/html
|
||||||
labels:
|
labels:
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.docker.network=proxy"
|
|
||||||
- "traefik.http.services.xdfnx-homepage.loadbalancer.server.port=80"
|
- "traefik.http.services.xdfnx-homepage.loadbalancer.server.port=80"
|
||||||
|
|
||||||
# Prod Router
|
# Prod Router
|
||||||
- "traefik.http.routers.xdfnx.rule=Host(`xdfnx.cfd`)"
|
- "traefik.http.routers.xdfnx.rule=Host(`xdfnx.cfd`)"
|
||||||
- "traefik.http.routers.xdfnx.entrypoints=websecure"
|
- "traefik.http.routers.xdfnx.entrypoints=websecure"
|
||||||
- "traefik.http.routers.xdfnx.middlewares=security-headers@file"
|
- "traefik.http.routers.xdfnx.tls.certresolver=letsencrypt"
|
||||||
- "traefik.http.routers.xdfnx.tls=true"
|
- "traefik.http.routers.xdfnx.tls=true"
|
||||||
# Local Router
|
# Local Router
|
||||||
- "traefik.http.routers.xdfnx-local.rule=Host(`xdfnx.workstation.internal`)"
|
- "traefik.http.routers.xdfnx-local.rule=Host(`xdfnx.workstation.internal`)"
|
||||||
- "traefik.http.routers.xdfnx-local.entrypoints=websecure"
|
- "traefik.http.routers.xdfnx-local.entrypoints=websecure"
|
||||||
- "traefik.http.routers.xdfnx-local.middlewares=security-headers@file"
|
|
||||||
- "traefik.http.routers.xdfnx-local.tls=true"
|
- "traefik.http.routers.xdfnx-local.tls=true"
|
||||||
# Dev Router
|
# Dev Router
|
||||||
- "traefik.http.routers.xdfnx-dev.rule=Host(`xdfnx.gigaforust.internal`)"
|
- "traefik.http.routers.xdfnx-dev.rule=Host(`xdfnx.gigaforust.internal`)"
|
||||||
- "traefik.http.routers.xdfnx-dev.entrypoints=websecure"
|
- "traefik.http.routers.xdfnx-dev.entrypoints=websecure"
|
||||||
- "traefik.http.routers.xdfnx-dev.middlewares=security-headers@file"
|
|
||||||
- "traefik.http.routers.xdfnx-dev.tls=true"
|
- "traefik.http.routers.xdfnx-dev.tls=true"
|
||||||
networks:
|
networks:
|
||||||
- proxy
|
- proxy
|
||||||
|
|||||||
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 95 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 59 KiB |
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 43 KiB |
|
After Width: | Height: | Size: 61 KiB |
@@ -41,7 +41,7 @@
|
|||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<i class="fas fa-envelope"></i>
|
<i class="fas fa-envelope"></i>
|
||||||
<a href="mailto:forust@forust.xyz">mail/contact@forust.xyz</a>
|
<a href="mailto:contact@forust.xyz">mail/contact@forust.xyz</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<i class="fa-solid fa-key"></i>
|
<i class="fa-solid fa-key"></i>
|
||||||
@@ -116,7 +116,7 @@
|
|||||||
<div class="avatar"
|
<div class="avatar"
|
||||||
style="background-image: url('assets/images/team/anna.jpg'); background-size: cover; background-position: center;">
|
style="background-image: url('assets/images/team/anna.jpg'); background-size: cover; background-position: center;">
|
||||||
</div>
|
</div>
|
||||||
<a href="" target="_blank">Anna~</a>
|
<a href="./assets/images/love.png" target="_blank">Anna~</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="member">
|
<div class="member">
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
services:
|
services:
|
||||||
kener:
|
kener:
|
||||||
image: rajnandan1/kener:latest
|
image: rajnandan1/kener:4.0.23
|
||||||
container_name: kener
|
container_name: kener
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
# ports:
|
# ports:
|
||||||
@@ -8,13 +8,16 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
- KENER_SECRET_KEY=${KENER_SECRET_KEY?Kener requires a secret key}
|
- KENER_SECRET_KEY=${KENER_SECRET_KEY?Kener requires a secret key}
|
||||||
- ORIGIN=${ORIGIN:-http://localhost:3000}
|
- ORIGIN=${ORIGIN:-http://localhost:3000}
|
||||||
|
- REDIS_URL=redis://redis:6379
|
||||||
- TZ:${TZ:-Etc/UTC}
|
- TZ:${TZ:-Etc/UTC}
|
||||||
|
depends_on:
|
||||||
|
redis:
|
||||||
|
condition: service_healthy
|
||||||
volumes:
|
volumes:
|
||||||
- db:/app/database
|
- db:/app/database
|
||||||
- uploads:/app/uploads
|
- uploads:/app/uploads
|
||||||
labels:
|
labels:
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.docker.network=proxy"
|
|
||||||
- "traefik.http.services.kener.loadbalancer.server.port=3000"
|
- "traefik.http.services.kener.loadbalancer.server.port=3000"
|
||||||
|
|
||||||
# Prod Router
|
# Prod Router
|
||||||
@@ -31,9 +34,26 @@ services:
|
|||||||
- "traefik.http.routers.kener-dev.tls=true"
|
- "traefik.http.routers.kener-dev.tls=true"
|
||||||
networks:
|
networks:
|
||||||
- proxy
|
- proxy
|
||||||
|
- kener
|
||||||
|
redis:
|
||||||
|
image: redis:7-alpine
|
||||||
|
container_name: kener-redis
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- redis:/data
|
||||||
|
healthcheck:
|
||||||
|
test: [ "CMD", "redis-cli", "ping" ]
|
||||||
|
interval: 6s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
networks:
|
||||||
|
- kener
|
||||||
volumes:
|
volumes:
|
||||||
db:
|
db:
|
||||||
uploads:
|
uploads:
|
||||||
|
redis:
|
||||||
networks:
|
networks:
|
||||||
proxy:
|
proxy:
|
||||||
external: true
|
external: true
|
||||||
|
kener:
|
||||||
|
external: false
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ services:
|
|||||||
- ./MeTube_downloads:/downloads
|
- ./MeTube_downloads:/downloads
|
||||||
labels:
|
labels:
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.docker.network=proxy"
|
|
||||||
- "traefik.http.services.metube.loadbalancer.server.port=8081"
|
- "traefik.http.services.metube.loadbalancer.server.port=8081"
|
||||||
|
|
||||||
# Prod Router
|
# Prod Router
|
||||||
@@ -25,12 +24,10 @@ services:
|
|||||||
# Local Router
|
# Local Router
|
||||||
- "traefik.http.routers.metube-local.rule=Host(`metube.workstation.internal`)"
|
- "traefik.http.routers.metube-local.rule=Host(`metube.workstation.internal`)"
|
||||||
- "traefik.http.routers.metube-local.entrypoints=websecure"
|
- "traefik.http.routers.metube-local.entrypoints=websecure"
|
||||||
- "traefik.http.routers.metube-local.middlewares=security-headers@file"
|
|
||||||
- "traefik.http.routers.metube-local.tls=true"
|
- "traefik.http.routers.metube-local.tls=true"
|
||||||
# Dev Router
|
# Dev Router
|
||||||
- "traefik.http.routers.metube-dev.rule=Host(`metube.gigaforust.internal`)"
|
- "traefik.http.routers.metube-dev.rule=Host(`metube.gigaforust.internal`)"
|
||||||
- "traefik.http.routers.metube-dev.entrypoints=websecure"
|
- "traefik.http.routers.metube-dev.entrypoints=websecure"
|
||||||
- "traefik.http.routers.metube-dev.middlewares=security-chain@file"
|
|
||||||
- "traefik.http.routers.metube-dev.tls=true"
|
- "traefik.http.routers.metube-dev.tls=true"
|
||||||
networks:
|
networks:
|
||||||
- proxy
|
- proxy
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ services:
|
|||||||
- N8N_SECURE_COOKIE=false
|
- N8N_SECURE_COOKIE=false
|
||||||
- N8N_COMMUNITY_PACKAGES_ALLOW_TOOL_USAGE=true
|
- N8N_COMMUNITY_PACKAGES_ALLOW_TOOL_USAGE=true
|
||||||
volumes:
|
volumes:
|
||||||
- ./n8n-node-data:/home/node/.n8n
|
- node-data:/home/node/.n8n
|
||||||
- ./n8n-files:/files
|
- files:/files
|
||||||
extra_hosts:
|
extra_hosts:
|
||||||
- "enterprise.n8n.io:104.26.13.187"
|
- "enterprise.n8n.io:104.26.13.187"
|
||||||
- "enterprise.n8n.io:104.26.12.187"
|
- "enterprise.n8n.io:104.26.12.187"
|
||||||
@@ -27,28 +27,19 @@ services:
|
|||||||
- n8n
|
- n8n
|
||||||
labels:
|
labels:
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.docker.network=proxy"
|
- "traefik.http.services.n8n.loadbalancer.server.port=5678"
|
||||||
|
|
||||||
# Prod Router
|
# Prod Router
|
||||||
- "traefik.http.routers.n8n.rule=Host(`n8n.forust.xyz`)"
|
- "traefik.http.routers.n8n.rule=Host(`n8n.forust.xyz`)"
|
||||||
- "traefik.http.routers.n8n.entrypoints=websecure"
|
- "traefik.http.routers.n8n.entrypoints=websecure"
|
||||||
- "traefik.http.routers.n8n.middlewares=security-headers@file"
|
|
||||||
- "traefik.http.routers.n8n.service=n8n"
|
|
||||||
- "traefik.http.routers.n8n.tls=true"
|
- "traefik.http.routers.n8n.tls=true"
|
||||||
- "traefik.http.services.n8n.loadbalancer.server.port=5678"
|
|
||||||
|
|
||||||
# Local Router
|
# Local Router
|
||||||
- "traefik.http.routers.n8n-local.rule=Host(`n8n.workstation.internal`) || Host(`n8n.internal`)"
|
- "traefik.http.routers.n8n-local.rule=Host(`n8n.workstation.internal`)"
|
||||||
- "traefik.http.routers.n8n-local.entrypoints=websecure"
|
- "traefik.http.routers.n8n-local.entrypoints=websecure"
|
||||||
- "traefik.http.routers.n8n-local.middlewares=security-headers@file"
|
|
||||||
- "traefik.http.routers.n8n-local.service=n8n"
|
|
||||||
- "traefik.http.routers.n8n-local.tls=true"
|
- "traefik.http.routers.n8n-local.tls=true"
|
||||||
|
|
||||||
# Dev Router
|
# Dev Router
|
||||||
- "traefik.http.routers.n8n-dev.rule=Host(`n8n.gigaforust.internal`)"
|
- "traefik.http.routers.n8n-dev.rule=Host(`n8n.gigaforust.internal`)"
|
||||||
- "traefik.http.routers.n8n-dev.entrypoints=websecure"
|
- "traefik.http.routers.n8n-dev.entrypoints=websecure"
|
||||||
- "traefik.http.routers.n8n-dev.middlewares=security-headers@file"
|
|
||||||
- "traefik.http.routers.n8n-dev.service=n8n"
|
|
||||||
- "traefik.http.routers.n8n-dev.tls=true"
|
- "traefik.http.routers.n8n-dev.tls=true"
|
||||||
|
|
||||||
- glance.name=n8n
|
- glance.name=n8n
|
||||||
@@ -60,3 +51,7 @@ networks:
|
|||||||
external: false
|
external: false
|
||||||
proxy:
|
proxy:
|
||||||
external: true
|
external: true
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
node-data:
|
||||||
|
files:
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
## https://github.com/autobrr/netronome?tab=readme-ov-file#environment-variables
|
||||||
|
|
||||||
|
# Server settings
|
||||||
|
NETRONOME__HOST=0.0.0.0 # Listen address
|
||||||
|
NETRONOME__PORT=7575 # Web UI port
|
||||||
|
NETRONOME__BASE_URL=/netronome # Base URL for reverse proxy
|
||||||
|
|
||||||
|
# Database (SQLite by default)
|
||||||
|
NETRONOME__DB_TYPE=postgres # sqlite or postgres
|
||||||
|
NETRONOME__DB_PATH=netronome.db # SQLite database path
|
||||||
|
|
||||||
|
# PostgreSQL (when DB_TYPE=postgres)
|
||||||
|
NETRONOME__DB_TYPE=postgres
|
||||||
|
NETRONOME__DB_HOST=postgres
|
||||||
|
NETRONOME__DB_PORT=5432
|
||||||
|
NETRONOME__DB_USER=netronome
|
||||||
|
NETRONOME__DB_PASSWORD=netronome
|
||||||
|
NETRONOME__DB_NAME=netronome
|
||||||
|
NETRONOME__DB_SSLMODE=disable
|
||||||
|
|
||||||
|
# Authentication
|
||||||
|
NETRONOME__AUTH_WHITELIST=127.0.0.1/32,192.168.1.0/24 # IP whitelist (comma-separated)
|
||||||
|
NETRONOME__SESSION_SECRET= # Session secret (auto-generated if empty)
|
||||||
|
|
||||||
|
# OIDC (optional)
|
||||||
|
NETRONOME__OIDC_ISSUER=https://accounts.google.com
|
||||||
|
NETRONOME__OIDC_CLIENT_ID=your-client-id
|
||||||
|
NETRONOME__OIDC_CLIENT_SECRET=your-secret
|
||||||
|
NETRONOME__OIDC_REDIRECT_URL=https://example.com/api/auth/oidc/callback
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
services:
|
||||||
|
netronome:
|
||||||
|
image: ghcr.io/autobrr/netronome:latest
|
||||||
|
restart: unless-stopped
|
||||||
|
container_name: netronome
|
||||||
|
ports:
|
||||||
|
- "7575:7575"
|
||||||
|
cap_add:
|
||||||
|
- NET_RAW
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.services.netronome.loadbalancer.server.port=7575"
|
||||||
|
|
||||||
|
# Prod Router
|
||||||
|
- "traefik.http.routers.netronome.rule=Host(`nm.forust.xyz`)"
|
||||||
|
- "traefik.http.routers.netronome.entrypoints=websecure"
|
||||||
|
- "traefik.http.routers.netronome.tls=true"
|
||||||
|
# Local Router
|
||||||
|
- "traefik.http.routers.netronome-local.rule=Host(`nm.workstation.internal`)"
|
||||||
|
- "traefik.http.routers.netronome-local.entrypoints=websecure"
|
||||||
|
- "traefik.http.routers.netronome-local.tls=true"
|
||||||
|
# Dev Router
|
||||||
|
- "traefik.http.routers.netronome-dev.rule=Host(`nm.gigaforust.internal`)"
|
||||||
|
- "traefik.http.routers.netronome-dev.entrypoints=websecure"
|
||||||
|
- "traefik.http.routers.netronome-dev.tls=true"
|
||||||
|
networks:
|
||||||
|
- proxy
|
||||||
|
- netronome
|
||||||
|
depends_on:
|
||||||
|
postgres:
|
||||||
|
condition: service_healthy
|
||||||
|
postgres:
|
||||||
|
container_name: netronome-postgres
|
||||||
|
image: postgres:17-alpine
|
||||||
|
environment:
|
||||||
|
- POSTGRES_USER=netronome
|
||||||
|
- POSTGRES_PASSWORD=netronome
|
||||||
|
- POSTGRES_DB=netronome
|
||||||
|
volumes:
|
||||||
|
- data:/var/lib/postgresql/data
|
||||||
|
healthcheck:
|
||||||
|
test: [ "CMD-SHELL", "pg_isready -U netronome" ]
|
||||||
|
interval: 5s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- netronome
|
||||||
|
volumes:
|
||||||
|
data:
|
||||||
|
|
||||||
|
|
||||||
|
networks:
|
||||||
|
proxy:
|
||||||
|
external: true
|
||||||
|
netronome:
|
||||||
|
external: false
|
||||||
@@ -7,6 +7,7 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- nextcloud_aio_mastercontainer:/mnt/docker-aio-config # Do not change (backup)
|
- nextcloud_aio_mastercontainer:/mnt/docker-aio-config # Do not change (backup)
|
||||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||||
|
- /dev/dri:/dev/dri
|
||||||
networks:
|
networks:
|
||||||
- nextcloud-aio
|
- nextcloud-aio
|
||||||
- proxy # Optional: Connects the mastercontainer to the proxy network in order to make the built-in reverse proxy detection work. See https://github.com/nextcloud/all-in-one/blob/main/reverse-proxy.md
|
- proxy # Optional: Connects the mastercontainer to the proxy network in order to make the built-in reverse proxy detection work. See https://github.com/nextcloud/all-in-one/blob/main/reverse-proxy.md
|
||||||
@@ -17,7 +18,6 @@ services:
|
|||||||
|
|
||||||
labels:
|
labels:
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.docker.network=proxy"
|
|
||||||
# AIO Services configuration
|
# AIO Services configuration
|
||||||
- "traefik.http.services.nextcloud-aio.loadbalancer.server.port=8080"
|
- "traefik.http.services.nextcloud-aio.loadbalancer.server.port=8080"
|
||||||
- "traefik.http.services.nextcloud-aio.loadbalancer.server.scheme=https"
|
- "traefik.http.services.nextcloud-aio.loadbalancer.server.scheme=https"
|
||||||
@@ -27,17 +27,14 @@ services:
|
|||||||
# - "traefik.http.routers.nextcloud-aio.rule=Host(`naio.forust.xyz`)"
|
# - "traefik.http.routers.nextcloud-aio.rule=Host(`naio.forust.xyz`)"
|
||||||
# - "traefik.http.routers.nextcloud-aio.entrypoints=websecure"
|
# - "traefik.http.routers.nextcloud-aio.entrypoints=websecure"
|
||||||
# - "traefik.http.routers.nextcloud-aio.middlewares=security-chain@file"
|
# - "traefik.http.routers.nextcloud-aio.middlewares=security-chain@file"
|
||||||
# - "traefik.http.routers.nextcloud-aio.service=nextcloud-aio"
|
|
||||||
# - "traefik.http.routers.nextcloud-aio.tls=true"
|
# - "traefik.http.routers.nextcloud-aio.tls=true"
|
||||||
# Local Router
|
# Local Router
|
||||||
- "traefik.http.routers.nextcloud-aio-local.rule=Host(`naio.workstation.internal`)"
|
- "traefik.http.routers.nextcloud-aio-local.rule=Host(`naio.workstation.internal`)"
|
||||||
- "traefik.http.routers.nextcloud-aio-local.entrypoints=websecure"
|
- "traefik.http.routers.nextcloud-aio-local.entrypoints=websecure"
|
||||||
- "traefik.http.routers.nextcloud-aio-local.middlewares=security-headers@file"
|
|
||||||
- "traefik.http.routers.nextcloud-aio-local.tls=true"
|
- "traefik.http.routers.nextcloud-aio-local.tls=true"
|
||||||
# Dev Router
|
# Dev Router
|
||||||
- "traefik.http.routers.nextcloud-aio-dev.rule=Host(`naio.gigaforust.internal`)"
|
- "traefik.http.routers.nextcloud-aio-dev.rule=Host(`naio.gigaforust.internal`)"
|
||||||
- "traefik.http.routers.nextcloud-aio-dev.entrypoints=websecure"
|
- "traefik.http.routers.nextcloud-aio-dev.entrypoints=websecure"
|
||||||
- "traefik.http.routers.nextcloud-aio-dev.middlewares=security-headers@file"
|
|
||||||
- "traefik.http.routers.nextcloud-aio-dev.tls=true"
|
- "traefik.http.routers.nextcloud-aio-dev.tls=true"
|
||||||
|
|
||||||
# Glance Metadata
|
# Glance Metadata
|
||||||
@@ -61,7 +58,7 @@ services:
|
|||||||
NEXTCLOUD_STARTUP_APPS: deck twofactor_totp tasks calendar contacts notes # Allows to modify the Nextcloud apps that are installed on starting AIO the first time. See https://github.com/nextcloud/all-in-one#how-to-change-the-nextcloud-apps-that-are-installed-on-the-first-startup
|
NEXTCLOUD_STARTUP_APPS: deck twofactor_totp tasks calendar contacts notes # Allows to modify the Nextcloud apps that are installed on starting AIO the first time. See https://github.com/nextcloud/all-in-one#how-to-change-the-nextcloud-apps-that-are-installed-on-the-first-startup
|
||||||
NEXTCLOUD_ADDITIONAL_APKS: imagemagick # This allows to add additional packages to the Nextcloud container permanently. Default is imagemagick but can be overwritten by modifying this value. See https://github.com/nextcloud/all-in-one#how-to-add-os-packages-permanently-to-the-nextcloud-container
|
NEXTCLOUD_ADDITIONAL_APKS: imagemagick # This allows to add additional packages to the Nextcloud container permanently. Default is imagemagick but can be overwritten by modifying this value. See https://github.com/nextcloud/all-in-one#how-to-add-os-packages-permanently-to-the-nextcloud-container
|
||||||
NEXTCLOUD_ADDITIONAL_PHP_EXTENSIONS: imagick # dditional php extensions to the Nextcloud container permanently. Default is imagick but can be overwritten by modifying this value. See https://github.com/nextcloud/all-in-one#how-to-add-php-extensions-permanently-to-the-nextcloud-container
|
NEXTCLOUD_ADDITIONAL_PHP_EXTENSIONS: imagick # dditional php extensions to the Nextcloud container permanently. Default is imagick but can be overwritten by modifying this value. See https://github.com/nextcloud/all-in-one#how-to-add-php-extensions-permanently-to-the-nextcloud-container
|
||||||
NEXTCLOUD_ENABLE_DRI_DEVICE: true # This allows to enable the /dev/dri device for containers that profit from it. ⚠️⚠️⚠️ Warning: this only works if the '/dev/dri' device is present on the host! If it should not exist on your host, don't set this to true as otherwise the Nextcloud container will fail to start! See https://github.com/nextcloud/all-in-one#how-to-enable-hardware-acceleration-for-nextcloud
|
# NEXTCLOUD_ENABLE_DRI_DEVICE: true # This allows to enable the /dev/dri device for containers that profit from it. ⚠️⚠️⚠️ Warning: this only works if the '/dev/dri' device is present on the host! If it should not exist on your host, don't set this to true as otherwise the Nextcloud container will fail to start! See https://github.com/nextcloud/all-in-one#how-to-enable-hardware-acceleration-for-nextcloud
|
||||||
# NEXTCLOUD_KEEP_DISABLED_APPS: false # Setting this to true will keep Nextcloud apps that are disabled in the AIO interface and not uninstall them if they should be installed. See https://github.com/nextcloud/all-in-one#how-to-keep-disabled-apps
|
# NEXTCLOUD_KEEP_DISABLED_APPS: false # Setting this to true will keep Nextcloud apps that are disabled in the AIO interface and not uninstall them if they should be installed. See https://github.com/nextcloud/all-in-one#how-to-keep-disabled-apps
|
||||||
SKIP_DOMAIN_VALIDATION: true # This should only be set to true if things are correctly configured. See https://github.com/nextcloud/all-in-one?tab=readme-ov-file#how-to-skip-the-domain-validation
|
SKIP_DOMAIN_VALIDATION: true # This should only be set to true if things are correctly configured. See https://github.com/nextcloud/all-in-one?tab=readme-ov-file#how-to-skip-the-domain-validation
|
||||||
# TALK_PORT: 3478 # This a-llows to adjust the port that the talk container is using which is exposed on the host. See https://github.com/nextcloud/all-in-one#how-to-adjust-the-talk-port
|
# TALK_PORT: 3478 # This a-llows to adjust the port that the talk container is using which is exposed on the host. See https://github.com/nextcloud/all-in-one#how-to-adjust-the-talk-port
|
||||||
@@ -71,6 +68,7 @@ networks:
|
|||||||
proxy:
|
proxy:
|
||||||
external: true
|
external: true
|
||||||
nextcloud-aio:
|
nextcloud-aio:
|
||||||
|
name: nextcloud-aio
|
||||||
driver: bridge
|
driver: bridge
|
||||||
external: false
|
external: false
|
||||||
volumes:
|
volumes:
|
||||||
|
|||||||
@@ -102,23 +102,19 @@ services:
|
|||||||
|
|
||||||
labels:
|
labels:
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.docker.network=proxy"
|
|
||||||
- "traefik.http.services.penpot.loadbalancer.server.port=8080"
|
- "traefik.http.services.penpot.loadbalancer.server.port=8080"
|
||||||
|
|
||||||
# Prod Router
|
# Prod Router
|
||||||
- "traefik.http.routers.penpot.rule=Host(`penpot.forust.xyz`)"
|
- "traefik.http.routers.penpot.rule=Host(`penpot.forust.xyz`)"
|
||||||
- "traefik.http.routers.penpot.entrypoints=websecure"
|
- "traefik.http.routers.penpot.entrypoints=websecure"
|
||||||
- "traefik.http.routers.penpot.middlewares=security-headers@file"
|
|
||||||
- "traefik.http.routers.penpot.tls=true"
|
- "traefik.http.routers.penpot.tls=true"
|
||||||
# Local Router
|
# Local Router
|
||||||
- "traefik.http.routers.penpot-local.rule=Host(`penpot.workstation.internal`)"
|
- "traefik.http.routers.penpot-local.rule=Host(`penpot.workstation.internal`)"
|
||||||
- "traefik.http.routers.penpot-local.entrypoints=websecure"
|
- "traefik.http.routers.penpot-local.entrypoints=websecure"
|
||||||
- "traefik.http.routers.penpot-local.middlewares=security-headers@file"
|
|
||||||
- "traefik.http.routers.penpot-local.tls=true"
|
- "traefik.http.routers.penpot-local.tls=true"
|
||||||
# Dev Router
|
# Dev Router
|
||||||
- "traefik.http.routers.penpot-dev.rule=Host(`penpot.gigaforust.internal`)"
|
- "traefik.http.routers.penpot-dev.rule=Host(`penpot.gigaforust.internal`)"
|
||||||
- "traefik.http.routers.penpot-dev.entrypoints=websecure"
|
- "traefik.http.routers.penpot-dev.entrypoints=websecure"
|
||||||
- "traefik.http.routers.penpot-dev.middlewares=security-headers@file"
|
|
||||||
- "traefik.http.routers.penpot-dev.tls=true"
|
- "traefik.http.routers.penpot-dev.tls=true"
|
||||||
environment:
|
environment:
|
||||||
<<: [ *penpot-flags, *penpot-http-body-size ]
|
<<: [ *penpot-flags, *penpot-http-body-size ]
|
||||||
|
|||||||
@@ -1,35 +1,29 @@
|
|||||||
services:
|
services:
|
||||||
portainer:
|
portainer:
|
||||||
image: portainer/portainer-ce:latest
|
image: portainer/portainer-ce:2.41.0
|
||||||
container_name: portainer
|
container_name: portainer
|
||||||
restart: always
|
restart: always
|
||||||
volumes:
|
volumes:
|
||||||
- /var/run/docker.sock:/var/run/docker.sock
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
- data:/data
|
- data:/data
|
||||||
ports:
|
ports:
|
||||||
- 9443:9443
|
- 9000:9000
|
||||||
# - 8000:8000 # Remove if you do not intend to use Edge Agents
|
# - 8000:8000 # Remove if you do not intend to use Edge Agents
|
||||||
labels:
|
labels:
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.docker.network=proxy"
|
- "traefik.http.services.portainer.loadbalancer.server.port=9000"
|
||||||
- "traefik.http.services.portainer.loadbalancer.server.port=9443"
|
|
||||||
- "traefik.http.services.portainer.loadbalancer.server.scheme=https"
|
|
||||||
- "traefik.http.services.portainer.loadbalancer.serverstransport=insecureTransport@file"
|
|
||||||
|
|
||||||
# Prod Router
|
# Prod Router
|
||||||
- "traefik.http.routers.portainer.rule=Host(`portainer.forust.xyz`)"
|
- "traefik.http.routers.portainer.rule=Host(`portainer.forust.xyz`)"
|
||||||
- "traefik.http.routers.portainer.entrypoints=websecure"
|
- "traefik.http.routers.portainer.entrypoints=websecure"
|
||||||
- "traefik.http.routers.portainer.middlewares=security-headers@file"
|
|
||||||
- "traefik.http.routers.portainer.tls=true"
|
- "traefik.http.routers.portainer.tls=true"
|
||||||
# Local Router
|
# Local Router
|
||||||
- "traefik.http.routers.portainer-local.rule=Host(`portainer.workstation.internal`)"
|
- "traefik.http.routers.portainer-local.rule=Host(`portainer.workstation.internal`)"
|
||||||
- "traefik.http.routers.portainer-local.entrypoints=websecure"
|
- "traefik.http.routers.portainer-local.entrypoints=websecure"
|
||||||
- "traefik.http.routers.portainer-local.middlewares=security-headers@file"
|
|
||||||
- "traefik.http.routers.portainer-local.tls=true"
|
- "traefik.http.routers.portainer-local.tls=true"
|
||||||
# Dev Router
|
# Dev Router
|
||||||
- "traefik.http.routers.portainer-dev.rule=Host(`portainer.gigaforust.internal`)"
|
- "traefik.http.routers.portainer-dev.rule=Host(`portainer.gigaforust.internal`)"
|
||||||
- "traefik.http.routers.portainer-dev.entrypoints=websecure"
|
- "traefik.http.routers.portainer-dev.entrypoints=websecure"
|
||||||
- "traefik.http.routers.portainer-dev.middlewares=security-headers@file"
|
|
||||||
- "traefik.http.routers.portainer-dev.tls=true"
|
- "traefik.http.routers.portainer-dev.tls=true"
|
||||||
|
|
||||||
# Glance Metadata
|
# Glance Metadata
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
# Read the documentation before using the `docker-compose.yml` file:
|
||||||
|
# https://docs.searxng.org/admin/installation-docker.html
|
||||||
|
#
|
||||||
|
# Additional ENVs:
|
||||||
|
# https://docs.searxng.org/admin/settings/settings_general.html#settings-general
|
||||||
|
# https://docs.searxng.org/admin/settings/settings_server.html#settings-server
|
||||||
|
|
||||||
|
# Use a specific version tag. E.g. "latest" or "2026.3.25-541c6c3cb".
|
||||||
|
#SEARXNG_VERSION=latest
|
||||||
|
|
||||||
|
# Listen to a specific address.
|
||||||
|
#SEARXNG_HOST=[::]
|
||||||
|
|
||||||
|
# Listen to a specific port.
|
||||||
|
SEARXNG_PORT=8080
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
# Read the documentation before using the `docker-compose.yml` file:
|
||||||
|
# https://docs.searxng.org/admin/installation-docker.html
|
||||||
|
services:
|
||||||
|
core:
|
||||||
|
container_name: searxng-core
|
||||||
|
image: docker.io/searxng/searxng:${SEARXNG_VERSION:-latest}
|
||||||
|
restart: unless-stopped
|
||||||
|
# ports:
|
||||||
|
# - ${SEARXNG_PORT:-8080}
|
||||||
|
env_file: .env
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.services.searxng.loadbalancer.server.port=8080"
|
||||||
|
# Prod Router
|
||||||
|
- "traefik.http.routers.searxng.rule=Host(`s.forust.xyz` || `searxng.forust.xyz`)"
|
||||||
|
- "traefik.http.routers.searxng.entrypoints=websecure"
|
||||||
|
- "traefik.http.routers.searxng.tls=true"
|
||||||
|
# Local Router
|
||||||
|
- "traefik.http.routers.searxng-local.rule=Host(`s.workstation.internal` || `searxng.workstation.internal`)"
|
||||||
|
- "traefik.http.routers.searxng-local.entrypoints=websecure"
|
||||||
|
- "traefik.http.routers.searxng-local.tls=true"
|
||||||
|
# Dev Router
|
||||||
|
- "traefik.http.routers.searxng-dev.rule=Host(`searxng.gigaforust.internal`)"
|
||||||
|
- "traefik.http.routers.searxng-dev.entrypoints=websecure"
|
||||||
|
- "traefik.http.routers.searxng-dev.tls=true"
|
||||||
|
networks:
|
||||||
|
- proxy
|
||||||
|
volumes:
|
||||||
|
- ./core-config/:/etc/searxng/:Z
|
||||||
|
- core-data:/var/cache/searxng/
|
||||||
|
|
||||||
|
valkey:
|
||||||
|
container_name: searxng-valkey
|
||||||
|
image: docker.io/valkey/valkey:9-alpine
|
||||||
|
command: valkey-server --save 30 1 --loglevel warning
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- valkey-data:/data/
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
core-data:
|
||||||
|
valkey-data:
|
||||||
|
networks:
|
||||||
|
proxy:
|
||||||
|
external: true
|
||||||
@@ -11,23 +11,19 @@ services:
|
|||||||
PORT: "8080"
|
PORT: "8080"
|
||||||
labels:
|
labels:
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.docker.network=proxy"
|
|
||||||
- "traefik.http.services.termix.loadbalancer.server.port=8080"
|
- "traefik.http.services.termix.loadbalancer.server.port=8080"
|
||||||
|
|
||||||
# Prod Router
|
# Prod Router
|
||||||
- "traefik.http.routers.termix.rule=Host(`termix.forust.xyz`)"
|
- "traefik.http.routers.termix.rule=Host(`termix.forust.xyz`)"
|
||||||
- "traefik.http.routers.termix.entrypoints=websecure"
|
- "traefik.http.routers.termix.entrypoints=websecure"
|
||||||
- "traefik.http.routers.termix.middlewares=security-headers@file"
|
|
||||||
- "traefik.http.routers.termix.tls=true"
|
- "traefik.http.routers.termix.tls=true"
|
||||||
# Local Router
|
# Local Router
|
||||||
- "traefik.http.routers.termix-local.rule=Host(`termix.workstation.internal`)"
|
- "traefik.http.routers.termix-local.rule=Host(`termix.workstation.internal`)"
|
||||||
- "traefik.http.routers.termix-local.entrypoints=websecure"
|
- "traefik.http.routers.termix-local.entrypoints=websecure"
|
||||||
- "traefik.http.routers.termix-local.middlewares=security-headers@file"
|
|
||||||
- "traefik.http.routers.termix-local.tls=true"
|
- "traefik.http.routers.termix-local.tls=true"
|
||||||
# Dev Router
|
# Dev Router
|
||||||
- "traefik.http.routers.termix-dev.rule=Host(`termix.gigaforust.internal`)"
|
- "traefik.http.routers.termix-dev.rule=Host(`termix.gigaforust.internal`)"
|
||||||
- "traefik.http.routers.termix-dev.entrypoints=websecure"
|
- "traefik.http.routers.termix-dev.entrypoints=websecure"
|
||||||
- "traefik.http.routers.termix-dev.middlewares=security-headers@file"
|
|
||||||
- "traefik.http.routers.termix-dev.tls=true"
|
- "traefik.http.routers.termix-dev.tls=true"
|
||||||
networks:
|
networks:
|
||||||
- proxy
|
- proxy
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
services:
|
services:
|
||||||
traefik:
|
traefik:
|
||||||
image: traefik:latest
|
image: traefik:v3.7
|
||||||
container_name: traefik
|
container_name: traefik
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
command:
|
command:
|
||||||
@@ -17,8 +17,8 @@ services:
|
|||||||
|
|
||||||
# EntryPoints
|
# EntryPoints
|
||||||
- "--entryPoints.web.address=:80"
|
- "--entryPoints.web.address=:80"
|
||||||
# - "--entryPoints.web.http.middlewares=error-pages@docker"
|
|
||||||
- "--entryPoints.web.http.redirections.entryPoint.scheme=https"
|
- "--entryPoints.web.http.redirections.entryPoint.scheme=https"
|
||||||
|
- "--entryPoints.web.http.redirections.entryPoint.to=websecure"
|
||||||
- "--entryPoints.websecure.address=:443"
|
- "--entryPoints.websecure.address=:443"
|
||||||
- "--entryPoints.websecure.http.middlewares=error-pages@docker"
|
- "--entryPoints.websecure.http.middlewares=error-pages@docker"
|
||||||
- "--entryPoints.websecure.http.tls=true"
|
- "--entryPoints.websecure.http.tls=true"
|
||||||
@@ -35,14 +35,14 @@ services:
|
|||||||
# Cloudflare
|
# Cloudflare
|
||||||
- "--entryPoints.web.forwardedHeaders.trustedIPs=173.245.48.0/20,103.21.244.0/22,103.22.200.0/22,103.31.4.0/22,141.101.64.0/18,108.162.192.0/18,190.93.240.0/20,188.114.96.0/20,197.234.240.0/22,198.41.128.0/17,162.158.0.0/15,104.16.0.0/13,104.24.0.0/14,172.64.0.0/13,131.0.72.0/22"
|
- "--entryPoints.web.forwardedHeaders.trustedIPs=173.245.48.0/20,103.21.244.0/22,103.22.200.0/22,103.31.4.0/22,141.101.64.0/18,108.162.192.0/18,190.93.240.0/20,188.114.96.0/20,197.234.240.0/22,198.41.128.0/17,162.158.0.0/15,104.16.0.0/13,104.24.0.0/14,172.64.0.0/13,131.0.72.0/22"
|
||||||
- "--entryPoints.websecure.forwardedHeaders.trustedIPs=173.245.48.0/20,103.21.244.0/22,103.22.200.0/22,103.31.4.0/22,141.101.64.0/18,108.162.192.0/18,190.93.240.0/20,188.114.96.0/20,197.234.240.0/22,198.41.128.0/17,162.158.0.0/15,104.16.0.0/13,104.24.0.0/14,172.64.0.0/13,131.0.72.0/22"
|
- "--entryPoints.websecure.forwardedHeaders.trustedIPs=173.245.48.0/20,103.21.244.0/22,103.22.200.0/22,103.31.4.0/22,141.101.64.0/18,108.162.192.0/18,190.93.240.0/20,188.114.96.0/20,197.234.240.0/22,198.41.128.0/17,162.158.0.0/15,104.16.0.0/13,104.24.0.0/14,172.64.0.0/13,131.0.72.0/22"
|
||||||
# # Logging
|
# Logging
|
||||||
# - "--log.level=INFO"
|
- "--log.level=INFO"
|
||||||
# - "--log.filePath=/var/log/traefik/traefik.log"
|
- "--log.filePath=/var/log/traefik/traefik.log"
|
||||||
# - "--accesslog=true"
|
- "--log.format=json"
|
||||||
# - "--accesslog.filepath=/var/log/traefik/access.log"
|
- "--accesslog=true"
|
||||||
|
- "--accesslog.filepath=/var/log/traefik/access.log"
|
||||||
labels:
|
labels:
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.docker.network=proxy"
|
|
||||||
|
|
||||||
# Prod Router (Dash)
|
# Prod Router (Dash)
|
||||||
- "traefik.http.routers.traefik-dashboard.rule=Host(`traefik.forust.xyz`)"
|
- "traefik.http.routers.traefik-dashboard.rule=Host(`traefik.forust.xyz`)"
|
||||||
@@ -53,13 +53,11 @@ services:
|
|||||||
# Local Router
|
# Local Router
|
||||||
- "traefik.http.routers.traefik-dashboard-local.rule=Host(`traefik.workstation.internal`)"
|
- "traefik.http.routers.traefik-dashboard-local.rule=Host(`traefik.workstation.internal`)"
|
||||||
- "traefik.http.routers.traefik-dashboard-local.entrypoints=websecure"
|
- "traefik.http.routers.traefik-dashboard-local.entrypoints=websecure"
|
||||||
- "traefik.http.routers.traefik-dashboard-local.middlewares=security-headers@file"
|
|
||||||
- "traefik.http.routers.traefik-dashboard-local.service=api@internal"
|
- "traefik.http.routers.traefik-dashboard-local.service=api@internal"
|
||||||
- "traefik.http.routers.traefik-dashboard-local.tls=true"
|
- "traefik.http.routers.traefik-dashboard-local.tls=true"
|
||||||
# Dev Router
|
# Dev Router
|
||||||
- "traefik.http.routers.traefik-dashboard-dev.rule=Host(`traefik.gigaforust.internal`)"
|
- "traefik.http.routers.traefik-dashboard-dev.rule=Host(`traefik.gigaforust.internal`)"
|
||||||
- "traefik.http.routers.traefik-dashboard-dev.entrypoints=websecure"
|
- "traefik.http.routers.traefik-dashboard-dev.entrypoints=websecure"
|
||||||
- "traefik.http.routers.traefik-dashboard-dev.middlewares=security-headers@file"
|
|
||||||
- "traefik.http.routers.traefik-dashboard-dev.service=api@internal"
|
- "traefik.http.routers.traefik-dashboard-dev.service=api@internal"
|
||||||
- "traefik.http.routers.traefik-dashboard-dev.tls=true"
|
- "traefik.http.routers.traefik-dashboard-dev.tls=true"
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
http:
|
http:
|
||||||
serversTransports:
|
serversTransports:
|
||||||
insecureTransport:
|
insecureTransport:
|
||||||
insecureSkipVerify: true
|
insecureSkipVerify: true
|
||||||
@@ -35,6 +35,7 @@ http:
|
|||||||
forwardAuth:
|
forwardAuth:
|
||||||
address: "http://authentik-server:9000/outpost.goauthentik.io/auth/traefik"
|
address: "http://authentik-server:9000/outpost.goauthentik.io/auth/traefik"
|
||||||
trustForwardHeader: true
|
trustForwardHeader: true
|
||||||
|
maxResponseBodySize: 1048576
|
||||||
authResponseHeaders:
|
authResponseHeaders:
|
||||||
- X-authentik-username
|
- X-authentik-username
|
||||||
- X-authentik-groups
|
- X-authentik-groups
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ http:
|
|||||||
rule: "Host(`nextcloud.workstation.internal`)"
|
rule: "Host(`nextcloud.workstation.internal`)"
|
||||||
entrypoints:
|
entrypoints:
|
||||||
- websecure
|
- websecure
|
||||||
- web
|
|
||||||
service: nextcloud
|
service: nextcloud
|
||||||
middlewares:
|
middlewares:
|
||||||
- nextcloud-chain
|
- nextcloud-chain
|
||||||
@@ -26,7 +25,6 @@ http:
|
|||||||
rule: "Host(`nextcloud.gigaforust.internal`)"
|
rule: "Host(`nextcloud.gigaforust.internal`)"
|
||||||
entrypoints:
|
entrypoints:
|
||||||
- websecure
|
- websecure
|
||||||
- web
|
|
||||||
service: nextcloud
|
service: nextcloud
|
||||||
middlewares:
|
middlewares:
|
||||||
- nextcloud-chain
|
- nextcloud-chain
|
||||||
|
|||||||
@@ -4,14 +4,12 @@ tls:
|
|||||||
# Cloudflare Origin CA *.forust.xyz
|
# Cloudflare Origin CA *.forust.xyz
|
||||||
# - certFile: /certs/cloudflare.pem
|
# - certFile: /certs/cloudflare.pem
|
||||||
# keyFile: /certs/cloudflare.key
|
# keyFile: /certs/cloudflare.key
|
||||||
- certFile: /certs/xdfnx.pem
|
# stores:
|
||||||
keyFile: /certs/xdfnx.key
|
# default:
|
||||||
stores:
|
# defaultCertificate:
|
||||||
default:
|
# # Fallback local certificate
|
||||||
defaultCertificate:
|
# certFile: /certs/local.pem
|
||||||
# Fallback local certificate
|
# keyFile: /certs/local-key.pem
|
||||||
certFile: /certs/gigaforust.internal+1.pem
|
|
||||||
keyFile: /certs/gigaforust.internal+1-key.pem
|
|
||||||
|
|
||||||
options:
|
options:
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ services:
|
|||||||
# - "3001:3001"
|
# - "3001:3001"
|
||||||
labels:
|
labels:
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.docker.network=proxy"
|
|
||||||
- "traefik.http.services.uptime-kuma.loadbalancer.server.port=3001"
|
- "traefik.http.services.uptime-kuma.loadbalancer.server.port=3001"
|
||||||
|
|
||||||
# Prod Router
|
# Prod Router
|
||||||
|
|||||||
@@ -1 +1,12 @@
|
|||||||
.unused
|
.unused
|
||||||
|
Downloads
|
||||||
|
.venv
|
||||||
|
volumes
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
my_account.session
|
||||||
|
.gitignore
|
||||||
|
README.md
|
||||||
|
.git
|
||||||
|
uv.lock
|
||||||
|
.deepsource.toml
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
API_ID=""
|
||||||
|
API_HASH=""
|
||||||
|
STRINGSESSION=""
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
# apiflash api key only for webshot plugin
|
||||||
|
APIFLASH_KEY=""
|
||||||
|
# gemini api key only for gemini plugin
|
||||||
|
GEMINI_KEY=""
|
||||||
|
# VT api key only for VirusTotal plugin
|
||||||
|
VT_KEY=""
|
||||||
|
# rmbg api key only for removebg plugin
|
||||||
|
RMBG_KEY=""
|
||||||
|
# cohere api key only for cohere plugin
|
||||||
|
COHERE_KEY=""
|
||||||
|
# sqlite/sqlite3 or mongo/mongodb
|
||||||
|
DATABASE_TYPE=""
|
||||||
|
# file name for sqlite3, database name for mongodb
|
||||||
|
DATABASE_NAME=""
|
||||||
@@ -14,3 +14,6 @@ __pycache__/
|
|||||||
/downloads/
|
/downloads/
|
||||||
/Downloads/
|
/Downloads/
|
||||||
config.ini
|
config.ini
|
||||||
|
|
||||||
|
k8s/*/*secret*.yaml
|
||||||
|
!k8s/account-secrets.yaml.example
|
||||||
@@ -1,10 +1,18 @@
|
|||||||
FROM python:3.11
|
FROM python:3.11-slim
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
git wget ffmpeg mediainfo && \
|
||||||
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
COPY requirements.txt /app/
|
||||||
|
|
||||||
|
RUN python -m pip install --no-cache-dir --upgrade pip && \
|
||||||
|
python -m pip install --no-cache-dir -r /app/requirements.txt
|
||||||
|
|
||||||
COPY . /app
|
COPY . /app
|
||||||
RUN apt-get -qq update && apt-get -qq install -y git wget ffmpeg mediainfo\
|
|
||||||
&& apt-get clean \
|
ENV PYTHONUNBUFFERED=1
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
|
||||||
RUN python -m venv --copies /opt/venv
|
CMD ["python", "-u", "main.py"]
|
||||||
ENV PATH="/opt/venv/bin:$PATH"
|
|
||||||
RUN pip install --no-cache-dir -r requirements.txt
|
|
||||||
CMD ["python", "main.py"]
|
|
||||||
@@ -3,6 +3,8 @@ services:
|
|||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
|
image: gcr.forust.xyz/forust/userbot:latest
|
||||||
|
pull_policy: build
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env
|
||||||
@@ -10,15 +12,16 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- ./volumes/data_forust:/app/data
|
- ./volumes/data_forust:/app/data
|
||||||
- ./Downloads:/app/downloads
|
- ./Downloads:/app/downloads
|
||||||
- ./volumes/logs_forust-:/app/logs
|
- ./volumes/logs_forust:/app/logs
|
||||||
dns:
|
dns:
|
||||||
- 8.8.8.8
|
- 8.8.8.8
|
||||||
- 1.1.1.1
|
- 1.1.1.1
|
||||||
|
|
||||||
anna:
|
anna:
|
||||||
build:
|
image: gcr.forust.xyz/forust/userbot:latest
|
||||||
context: .
|
pull_policy: build
|
||||||
dockerfile: Dockerfile
|
depends_on:
|
||||||
|
- forust
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env
|
||||||
@@ -29,4 +32,4 @@ services:
|
|||||||
- ./volumes/logs_anna:/app/logs
|
- ./volumes/logs_anna:/app/logs
|
||||||
dns:
|
dns:
|
||||||
- 8.8.8.8
|
- 8.8.8.8
|
||||||
- 1.1.1.1
|
- 1.1.1.1
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: userbot-common-config
|
||||||
|
data:
|
||||||
|
DATABASE_TYPE: ""
|
||||||
|
DATABASE_NAME: ""
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
|
||||||
|
resources:
|
||||||
|
- userbots.yaml
|
||||||
|
- common-secret.yaml
|
||||||
|
- common-config.yaml
|
||||||
|
- forust-secrets.yaml
|
||||||
|
- anna-secrets.yaml
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: forust-userbot-deployment
|
||||||
|
labels:
|
||||||
|
app: forust-userbot
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: forust-userbot
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: forust-userbot
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: forust-userbot
|
||||||
|
image: gcr.forust.xyz/forust/userbot:latest
|
||||||
|
imagePullPolicy: Always
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
memory: "512Mi"
|
||||||
|
cpu: "500m"
|
||||||
|
requests:
|
||||||
|
memory: "256Mi"
|
||||||
|
cpu: "100m"
|
||||||
|
envFrom:
|
||||||
|
- secretRef:
|
||||||
|
name: userbot-common-secrets
|
||||||
|
- configMapRef:
|
||||||
|
name: userbot-common-config
|
||||||
|
- secretRef:
|
||||||
|
name: userbot-forust-secrets
|
||||||
|
volumeMounts:
|
||||||
|
- name: forust-storage
|
||||||
|
mountPath: /app/data
|
||||||
|
subPath: data
|
||||||
|
- name: forust-storage
|
||||||
|
mountPath: /app/logs
|
||||||
|
subPath: logs
|
||||||
|
volumes:
|
||||||
|
- name: forust-storage
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: forust-pvc
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolumeClaim
|
||||||
|
metadata:
|
||||||
|
name: forust-pvc
|
||||||
|
spec:
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 1Gi
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteOnce
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: anna-userbot-deployment
|
||||||
|
labels:
|
||||||
|
app: anna-userbot
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: anna-userbot
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: anna-userbot
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: anna-userbot
|
||||||
|
image: gcr.forust.xyz/forust/userbot:latest
|
||||||
|
imagePullPolicy: Always
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
memory: "512Mi"
|
||||||
|
cpu: "500m"
|
||||||
|
requests:
|
||||||
|
memory: "256Mi"
|
||||||
|
cpu: "100m"
|
||||||
|
envFrom:
|
||||||
|
- secretRef:
|
||||||
|
name: userbot-common-secrets
|
||||||
|
- configMapRef:
|
||||||
|
name: userbot-common-config
|
||||||
|
- secretRef:
|
||||||
|
name: userbot-anna-secrets
|
||||||
|
volumeMounts:
|
||||||
|
- name: anna-storage
|
||||||
|
mountPath: /app/data
|
||||||
|
subPath: data
|
||||||
|
- name: anna-storage
|
||||||
|
mountPath: /app/logs
|
||||||
|
subPath: logs
|
||||||
|
volumes:
|
||||||
|
- name: anna-storage
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: anna-pvc
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolumeClaim
|
||||||
|
metadata:
|
||||||
|
name: anna-pvc
|
||||||
|
spec:
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 1Gi
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteOnce
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
|
||||||
|
resources:
|
||||||
|
- ../../base
|
||||||
|
|
||||||
|
patches:
|
||||||
|
- path: patch-downloads.yaml
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: forust-userbot-deployment
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: forust-userbot
|
||||||
|
volumeMounts:
|
||||||
|
- name: downloads
|
||||||
|
mountPath: /app/downloads
|
||||||
|
volumes:
|
||||||
|
- name: downloads
|
||||||
|
hostPath:
|
||||||
|
path: /home/user/projects/homelab/userbot/Downloads
|
||||||
|
type: DirectoryOrCreate
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: anna-userbot-deployment
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: anna-userbot
|
||||||
|
volumeMounts:
|
||||||
|
- name: downloads
|
||||||
|
mountPath: /app/downloads
|
||||||
|
volumes:
|
||||||
|
- name: downloads
|
||||||
|
hostPath:
|
||||||
|
path: /home/user/projects/homelab/userbot/Downloads
|
||||||
|
type: DirectoryOrCreate
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
|
||||||
|
resources:
|
||||||
|
- ../../base
|
||||||
|
|
||||||
|
patches:
|
||||||
|
- path: patch-downloads.yaml
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: forust-userbot-deployment
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: forust-userbot
|
||||||
|
volumeMounts:
|
||||||
|
- name: downloads
|
||||||
|
mountPath: /app/downloads
|
||||||
|
volumes:
|
||||||
|
- name: downloads
|
||||||
|
hostPath:
|
||||||
|
path: /srv/homelab/userbot/Downloads
|
||||||
|
type: DirectoryOrCreate
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: anna-userbot-deployment
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: anna-userbot
|
||||||
|
volumeMounts:
|
||||||
|
- name: downloads
|
||||||
|
mountPath: /app/downloads
|
||||||
|
volumes:
|
||||||
|
- name: downloads
|
||||||
|
hostPath:
|
||||||
|
path: /srv/homelab/userbot/Downloads
|
||||||
|
type: DirectoryOrCreate
|
||||||
@@ -90,11 +90,19 @@ def load_missing_modules():
|
|||||||
os.makedirs(custom_modules_path, exist_ok=True)
|
os.makedirs(custom_modules_path, exist_ok=True)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
f = requests.get(
|
resp = requests.get(
|
||||||
"https://raw.githubusercontent.com/The-MoonTg-project/custom_modules/main/full.txt"
|
"https://raw.githubusercontent.com/The-MoonTg-project/custom_modules/main/full.txt",
|
||||||
).text
|
timeout=10,
|
||||||
except Exception:
|
)
|
||||||
logging.error("Failed to fetch custom modules list")
|
if not resp.ok:
|
||||||
|
logging.error(
|
||||||
|
"Failed to fetch custom modules list: HTTP %s",
|
||||||
|
resp.status_code,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
f = resp.text
|
||||||
|
except Exception as e:
|
||||||
|
logging.error("Failed to fetch custom modules list: %s", e)
|
||||||
return
|
return
|
||||||
modules_dict = {
|
modules_dict = {
|
||||||
line.split("/")[-1].split()[0]: line.strip() for line in f.splitlines()
|
line.split("/")[-1].split()[0]: line.strip() for line in f.splitlines()
|
||||||
|
|||||||
@@ -59,26 +59,26 @@ async def version(client: Client, message: Message):
|
|||||||
|
|
||||||
await message.delete()
|
await message.delete()
|
||||||
|
|
||||||
remote_url = list(gitrepo.remote().urls)[0]
|
if gitrepo is not None:
|
||||||
commit_time = (
|
remote_url = list(gitrepo.remote().urls)[0]
|
||||||
datetime.datetime.fromtimestamp(gitrepo.head.commit.committed_date)
|
commit_time = (
|
||||||
.astimezone(datetime.timezone.utc)
|
datetime.datetime.fromtimestamp(gitrepo.head.commit.committed_date)
|
||||||
.strftime("%Y-%m-%d %H:%M:%S %Z")
|
.astimezone(datetime.timezone.utc)
|
||||||
)
|
.strftime("%Y-%m-%d %H:%M:%S %Z")
|
||||||
|
)
|
||||||
|
git_info = (
|
||||||
|
f"\n<b>Branch: <a href={remote_url}/tree/{gitrepo.active_branch}>{gitrepo.active_branch}</a>\n"
|
||||||
|
if gitrepo.active_branch != "master" else "\n"
|
||||||
|
) + f"Commit: <a href={remote_url}/commit/{gitrepo.head.commit.hexsha}>" f"{gitrepo.head.commit.hexsha[:7]}</a> by {gitrepo.head.commit.author.name}\n" f"Commit time: {commit_time}</b>"
|
||||||
|
else:
|
||||||
|
git_info = ""
|
||||||
|
|
||||||
await message.reply(
|
await message.reply(
|
||||||
f"<b>Moon Userbot version: {userbot_version}\n"
|
f"<b>Moon Userbot version: {userbot_version}\n"
|
||||||
f"Changelog </b><i><a href=https://t.me/moonuserbot/{changelog}>in channel</a></i>.<b>\n"
|
f"Changelog </b><i><a href=https://t.me/moonuserbot/{changelog}>in channel</a></i>.<b>\n"
|
||||||
f"Changelog written by </b><i>"
|
f"Changelog written by </b><i>"
|
||||||
f"<a href=https://t.me/Qbtaumai>Abhi</a></i>\n\n"
|
f"<a href=https://t.me/Qbtaumai>Abhi</a></i>\n\n"
|
||||||
+ (
|
f"{git_info}",
|
||||||
f"<b>Branch: <a href={remote_url}/tree/{gitrepo.active_branch}>{gitrepo.active_branch}</a>\n"
|
|
||||||
if gitrepo.active_branch != "master"
|
|
||||||
else ""
|
|
||||||
)
|
|
||||||
+ f"Commit: <a href={remote_url}/commit/{gitrepo.head.commit.hexsha}>"
|
|
||||||
f"{gitrepo.head.commit.hexsha[:7]}</a> by {gitrepo.head.commit.author.name}\n"
|
|
||||||
f"Commit time: {commit_time}</b>",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ name = "userbot"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
description = "Add your description here"
|
description = "Add your description here"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.13"
|
requires-python = ">=3.11"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aiofiles>=25.1.0",
|
"aiofiles>=25.1.0",
|
||||||
"aiohttp>=3.13.2",
|
"aiohttp>=3.13.2",
|
||||||
|
|||||||
@@ -18,3 +18,5 @@ aiohttp
|
|||||||
aiofiles
|
aiofiles
|
||||||
pySmartDL
|
pySmartDL
|
||||||
qrcode
|
qrcode
|
||||||
|
bottle
|
||||||
|
dulwich
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import os
|
import os
|
||||||
import environs
|
import environs
|
||||||
|
|
||||||
|
env = environs.Env()
|
||||||
try:
|
try:
|
||||||
env = environs.Env()
|
|
||||||
env.read_env("./.env")
|
env.read_env("./.env")
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
print("No .env file found, using os.environ.")
|
print("No .env file found, using os.environ.")
|
||||||
|
|||||||
@@ -1,19 +1,3 @@
|
|||||||
# Moon-Userbot - telegram userbot
|
|
||||||
# Copyright (C) 2020-present Moon Userbot Organization
|
|
||||||
#
|
|
||||||
# This program is free software: you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU General Public License as published by
|
|
||||||
# the Free Software Foundation, either version 3 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
|
|
||||||
# This program is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU General Public License for more details.
|
|
||||||
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
from sys import version_info
|
from sys import version_info
|
||||||
from .db import db
|
from .db import db
|
||||||
import git
|
import git
|
||||||
@@ -37,19 +21,11 @@ prefix = db.get("core.main", "prefix", ".")
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
gitrepo = git.Repo(".")
|
gitrepo = git.Repo(".")
|
||||||
except git.exc.InvalidGitRepositoryError:
|
except (git.exc.InvalidGitRepositoryError, git.exc.NoSuchPathError):
|
||||||
repo = git.Repo.init()
|
gitrepo = None
|
||||||
origin = repo.create_remote(
|
|
||||||
"origin", "https://github.com/The-MoonTg-project/Moon-Userbot"
|
|
||||||
)
|
|
||||||
origin.fetch()
|
|
||||||
repo.create_head("main", origin.refs.main)
|
|
||||||
repo.heads.main.set_tracking_branch(origin.refs.main)
|
|
||||||
repo.heads.main.checkout(True)
|
|
||||||
gitrepo = git.Repo(".")
|
|
||||||
|
|
||||||
if len(gitrepo.tags) > 0:
|
if gitrepo is not None and len(gitrepo.tags) > 0:
|
||||||
commits_since_tag = list(gitrepo.iter_commits(f"{gitrepo.tags[-1].name}..HEAD"))
|
commits_since_tag = list(gitrepo.iter_commits(f"{gitrepo.tags[-1].name}..HEAD"))
|
||||||
|
userbot_version = f"2.5.{len(commits_since_tag)}"
|
||||||
else:
|
else:
|
||||||
commits_since_tag = []
|
userbot_version = "2.5.0"
|
||||||
userbot_version = f"2.5.{len(commits_since_tag)}"
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import re
|
|||||||
import shlex
|
import shlex
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
import tempfile
|
||||||
import time
|
import time
|
||||||
import traceback
|
import traceback
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
@@ -85,13 +86,17 @@ async def edit_or_send_as_file(
|
|||||||
return
|
return
|
||||||
if len(tex) > 1024:
|
if len(tex) > 1024:
|
||||||
await message.edit("<code>OutPut is Too Large, Sending As File!</code>")
|
await message.edit("<code>OutPut is Too Large, Sending As File!</code>")
|
||||||
file_names = f"{file_name}.txt"
|
with tempfile.NamedTemporaryFile(
|
||||||
with open(file_names, "w") as fn:
|
"w", delete=False, suffix=".txt", prefix=f"{file_name}_"
|
||||||
|
) as fn:
|
||||||
fn.write(tex)
|
fn.write(tex)
|
||||||
await client.send_document(message.chat.id, file_names, caption=caption)
|
temp_path = fn.name
|
||||||
await message.delete()
|
try:
|
||||||
if os.path.exists(file_names):
|
await client.send_document(message.chat.id, temp_path, caption=caption)
|
||||||
os.remove(file_names)
|
await message.delete()
|
||||||
|
finally:
|
||||||
|
if os.path.exists(temp_path):
|
||||||
|
os.remove(temp_path)
|
||||||
return
|
return
|
||||||
return await message.edit(tex)
|
return await message.edit(tex)
|
||||||
|
|
||||||
@@ -249,12 +254,7 @@ def is_admin(func):
|
|||||||
chat_member = await client.get_chat_member(
|
chat_member = await client.get_chat_member(
|
||||||
message.chat.id, message.from_user.id
|
message.chat.id, message.from_user.id
|
||||||
)
|
)
|
||||||
chat_admins = []
|
if chat_member.status in ("administrator", "creator"):
|
||||||
async for member in client.get_chat_members(
|
|
||||||
message.chat.id, filter=ChatMembersFilter.ADMINISTRATORS
|
|
||||||
):
|
|
||||||
chat_admins.append(member)
|
|
||||||
if chat_member in chat_admins:
|
|
||||||
return await func(client, message)
|
return await func(client, message)
|
||||||
await message.edit("You need to be an admin to perform this action.")
|
await message.edit("You need to be an admin to perform this action.")
|
||||||
except UserNotParticipant:
|
except UserNotParticipant:
|
||||||
|
|||||||