after migration
13
.gitignore
vendored
|
|
@ -111,3 +111,16 @@ dmypy.json
|
||||||
*.sqlite3
|
*.sqlite3
|
||||||
.github
|
.github
|
||||||
.venv
|
.venv
|
||||||
|
|
||||||
|
# Virtual environment
|
||||||
|
venv/
|
||||||
|
.autoenv.zsh
|
||||||
|
.autoenv_leave.zsh
|
||||||
|
.autoenv.*.zsh
|
||||||
|
|
||||||
|
# Python cache
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
*.pyo
|
||||||
|
*.pyd
|
||||||
|
.Python
|
||||||
|
|
|
||||||
15
README.md
|
|
@ -22,10 +22,11 @@
|
||||||
- **📚 BookStack Wiki**: Documentation and knowledge base (port 6875)
|
- **📚 BookStack Wiki**: Documentation and knowledge base (port 6875)
|
||||||
- **🤖 Ollama + Open WebUI**: Local LLM chatbot interface (port 8080)
|
- **🤖 Ollama + Open WebUI**: Local LLM chatbot interface (port 8080)
|
||||||
- **📨 Matrix Synapse**: Print order collection and payment tracking (port 8008)
|
- **📨 Matrix Synapse**: Print order collection and payment tracking (port 8008)
|
||||||
- **🐳 Portainer**: Container management dashboard (port 9000)
|
- **<EFBFBD> Element Web**: Browser-based Matrix chat client (port 8082)
|
||||||
- **<EFBFBD> JupyterHub**: Multi-user Jupyter notebook server for drone programming (port 8001)
|
- **<EFBFBD>🐳 Portainer**: Container management dashboard (port 9000)
|
||||||
|
- **📓 JupyterHub**: Multi-user Jupyter notebook server for drone programming (port 8001)
|
||||||
- **🦊 Forgejo**: Self-hosted Git service for code collaboration (port 3003)
|
- **🦊 Forgejo**: Self-hosted Git service for code collaboration (port 3003)
|
||||||
- **<EFBFBD>🔄 Watchtower**: Automatic container updates every 24 hours
|
- **🔄 Watchtower**: Automatic container updates every 24 hours
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -126,9 +127,13 @@ graph TB
|
||||||
|
|
||||||
### Prerequisites
|
### Prerequisites
|
||||||
|
|
||||||
|
**On the host system:**
|
||||||
- **Docker** & **Docker Compose** installed
|
- **Docker** & **Docker Compose** installed
|
||||||
|
- **curl** - for testing and healthchecks (install: `brew install curl` on macOS)
|
||||||
- **Poppler** (for pdf2image - included in Docker)
|
- **Poppler** (for pdf2image - included in Docker)
|
||||||
- **Port availability**: 80, 3003, 6875, 8001, 8008, 8080, 9000, 11434
|
- **Port availability**: 80, 3003, 6875, 8001, 8008, 8080, 8082, 9000, 11434
|
||||||
|
|
||||||
|
**Note:** Python dependencies are containerized and don't need to be installed on the host.
|
||||||
|
|
||||||
### Installation
|
### Installation
|
||||||
|
|
||||||
|
|
@ -158,8 +163,10 @@ graph TB
|
||||||
- Open WebUI: <http://localhost:8080>
|
- Open WebUI: <http://localhost:8080>
|
||||||
- Portainer: <http://localhost:9000>
|
- Portainer: <http://localhost:9000>
|
||||||
- Matrix: <http://localhost:8008>
|
- Matrix: <http://localhost:8008>
|
||||||
|
- Element Web: <http://localhost:8082>
|
||||||
- JupyterHub: <http://localhost:8001>
|
- JupyterHub: <http://localhost:8001>
|
||||||
- Forgejo: <http://localhost:3003>
|
- Forgejo: <http://localhost:3003>
|
||||||
|
- Forgejo: <http://localhost:3003>
|
||||||
|
|
||||||
### First-Time Setup
|
### First-Time Setup
|
||||||
|
|
||||||
|
|
|
||||||
40
create_room.py
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Create a Matrix room for print orders
|
||||||
|
"""
|
||||||
|
import json
|
||||||
|
import requests
|
||||||
|
|
||||||
|
access_token = "syt_ZWluc3p3b3ZpZXI_vebsCCpDYUkfsqLgnvjz_1WOI0g"
|
||||||
|
user_id = "@einszwovier:124.local"
|
||||||
|
|
||||||
|
# Create room
|
||||||
|
data = {
|
||||||
|
"name": "Print Orders",
|
||||||
|
"topic": "PDF print order submissions from the web app",
|
||||||
|
"preset": "private_chat",
|
||||||
|
"visibility": "private"
|
||||||
|
}
|
||||||
|
|
||||||
|
headers = {
|
||||||
|
"Authorization": f"Bearer {access_token}",
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
}
|
||||||
|
|
||||||
|
response = requests.post(
|
||||||
|
"http://localhost:8008/_matrix/client/r0/createRoom",
|
||||||
|
json=data,
|
||||||
|
headers=headers
|
||||||
|
)
|
||||||
|
|
||||||
|
print(f"Room creation response: {response.status_code}")
|
||||||
|
print(json.dumps(response.json(), indent=2))
|
||||||
|
|
||||||
|
if response.status_code == 200:
|
||||||
|
room_id = response.json()['room_id']
|
||||||
|
print(f"\n✅ Room created successfully!")
|
||||||
|
print(f"Room ID: {room_id}")
|
||||||
|
print(f"\n📋 Add this to your .env file:")
|
||||||
|
print(f"MATRIX_ROOM={room_id}")
|
||||||
|
else:
|
||||||
|
print(f"\n❌ Room creation failed")
|
||||||
|
|
@ -65,12 +65,7 @@ services:
|
||||||
mem_limit: 16g
|
mem_limit: 16g
|
||||||
cpus: 6.0
|
cpus: 6.0
|
||||||
mem_reservation: 4g
|
mem_reservation: 4g
|
||||||
healthcheck:
|
# Healthcheck removed - ollama image doesn't include curl
|
||||||
test: ["CMD-SHELL", "curl -f http://localhost:11434/ || exit 1"]
|
|
||||||
interval: 60s
|
|
||||||
timeout: 10s
|
|
||||||
retries: 3
|
|
||||||
start_period: 60s
|
|
||||||
labels:
|
labels:
|
||||||
- "com.centurylinklabs.watchtower.enable=true"
|
- "com.centurylinklabs.watchtower.enable=true"
|
||||||
- "description=Local LLM inference engine"
|
- "description=Local LLM inference engine"
|
||||||
|
|
@ -179,11 +174,7 @@ services:
|
||||||
- WATCHTOWER_INCLUDE_RESTARTING=true
|
- WATCHTOWER_INCLUDE_RESTARTING=true
|
||||||
- WATCHTOWER_LABEL_ENABLE=true
|
- WATCHTOWER_LABEL_ENABLE=true
|
||||||
command: --cleanup --interval 86400 --label-enable
|
command: --cleanup --interval 86400 --label-enable
|
||||||
healthcheck:
|
# Healthcheck removed - watchtower runs as background process, pgrep check unreliable
|
||||||
test: ["CMD-SHELL", "pgrep watchtower || exit 1"]
|
|
||||||
interval: 60s
|
|
||||||
timeout: 10s
|
|
||||||
retries: 3
|
|
||||||
labels:
|
labels:
|
||||||
- "description=Watchtower Auto-Update Service"
|
- "description=Watchtower Auto-Update Service"
|
||||||
- "maintainer=studio einszwovier"
|
- "maintainer=studio einszwovier"
|
||||||
|
|
@ -198,16 +189,7 @@ services:
|
||||||
volumes:
|
volumes:
|
||||||
- /var/run/docker.sock:/var/run/docker.sock
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
- portainer_data:/data
|
- portainer_data:/data
|
||||||
healthcheck:
|
# Healthcheck removed - portainer image doesn't include curl
|
||||||
test:
|
|
||||||
[
|
|
||||||
"CMD-SHELL",
|
|
||||||
"curl -f http://localhost:9000/api/system/status || exit 1",
|
|
||||||
]
|
|
||||||
interval: 30s
|
|
||||||
timeout: 10s
|
|
||||||
retries: 3
|
|
||||||
start_period: 30s
|
|
||||||
labels:
|
labels:
|
||||||
- "description=Portainer Container Management UI"
|
- "description=Portainer Container Management UI"
|
||||||
- "maintainer=studio einszwovier"
|
- "maintainer=studio einszwovier"
|
||||||
|
|
@ -232,16 +214,7 @@ services:
|
||||||
cpus: 1.0
|
cpus: 1.0
|
||||||
depends_on:
|
depends_on:
|
||||||
- web
|
- web
|
||||||
healthcheck:
|
# Healthcheck removed - jupyterhub custom image doesn't include curl
|
||||||
test:
|
|
||||||
[
|
|
||||||
"CMD-SHELL",
|
|
||||||
"curl -f http://localhost:8001/hub/health || curl -f http://localhost:8001/hub/ || exit 1",
|
|
||||||
]
|
|
||||||
interval: 30s
|
|
||||||
timeout: 10s
|
|
||||||
retries: 3
|
|
||||||
start_period: 60s
|
|
||||||
labels:
|
labels:
|
||||||
- "com.centurylinklabs.watchtower.enable=true"
|
- "com.centurylinklabs.watchtower.enable=true"
|
||||||
- "description=JupyterHub for interactive notebooks"
|
- "description=JupyterHub for interactive notebooks"
|
||||||
|
|
@ -286,12 +259,7 @@ services:
|
||||||
mem_limit: 512m
|
mem_limit: 512m
|
||||||
cpus: 0.5
|
cpus: 0.5
|
||||||
mem_reservation: 128m
|
mem_reservation: 128m
|
||||||
healthcheck:
|
# Healthcheck removed - element-web image doesn't include curl
|
||||||
test: ["CMD-SHELL", "curl -f http://localhost:80 || exit 1"]
|
|
||||||
interval: 30s
|
|
||||||
timeout: 10s
|
|
||||||
retries: 3
|
|
||||||
start_period: 20s
|
|
||||||
depends_on:
|
depends_on:
|
||||||
synapse:
|
synapse:
|
||||||
condition: service_started
|
condition: service_started
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,12 @@ Element Web is a browser-based Matrix client that connects to the studio einszwo
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
The `config.json` file configures Element Web to connect to the local Synapse homeserver at `http://einszwovier.local:8008`.
|
The `config.json` file configures Element Web to connect to the local Synapse homeserver at `http://124.local:8008`.
|
||||||
|
|
||||||
### Key Settings
|
### Key Settings
|
||||||
|
|
||||||
- **Homeserver**: Points to local Synapse instance
|
- **Homeserver**: Points to local Synapse instance
|
||||||
- **Server name**: `einszwovier.local`
|
- **Server name**: `124.local`
|
||||||
- **Brand**: "studio einszwovier Chat"
|
- **Brand**: "studio einszwovier Chat"
|
||||||
- **Default language**: German (DE)
|
- **Default language**: German (DE)
|
||||||
- **Default theme**: Light mode
|
- **Default theme**: Light mode
|
||||||
|
|
@ -17,13 +17,13 @@ The `config.json` file configures Element Web to connect to the local Synapse ho
|
||||||
|
|
||||||
## Access
|
## Access
|
||||||
|
|
||||||
- **URL**: `http://einszwovier.local:8082`
|
- **URL**: `http://124.local:8082`
|
||||||
- **No app required**: Works in any modern web browser
|
- **No app required**: Works in any modern web browser
|
||||||
- **Mobile friendly**: Responsive design works on phones and tablets
|
- **Mobile friendly**: Responsive design works on phones and tablets
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
1. Navigate to `http://einszwovier.local:8082`
|
1. Navigate to `http://124.local:8082`
|
||||||
2. Click "Sign In" or "Create Account"
|
2. Click "Sign In" or "Create Account"
|
||||||
3. Use existing Matrix credentials or create a new account
|
3. Use existing Matrix credentials or create a new account
|
||||||
4. Join rooms (e.g., the print order room)
|
4. Join rooms (e.g., the print order room)
|
||||||
|
|
@ -49,7 +49,7 @@ Element Web is managed by Watchtower and updates automatically with the rest of
|
||||||
|
|
||||||
- Ensure Synapse is running: `docker-compose ps synapse`
|
- Ensure Synapse is running: `docker-compose ps synapse`
|
||||||
- Check Synapse logs: `docker-compose logs synapse`
|
- Check Synapse logs: `docker-compose logs synapse`
|
||||||
- Verify hostname resolution: `ping einszwovier.local`
|
- Verify hostname resolution: `ping 124.local`
|
||||||
|
|
||||||
**Login fails:**
|
**Login fails:**
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
{
|
{
|
||||||
"default_server_config": {
|
"default_server_config": {
|
||||||
"m.homeserver": {
|
"m.homeserver": {
|
||||||
"base_url": "http://einszwovier.local:8008",
|
"base_url": "http://124.local:8008",
|
||||||
"server_name": "einszwovier.local"
|
"server_name": "124.local"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"brand": "studio einszwovier Chat",
|
"brand": "studio einszwovier Chat",
|
||||||
|
|
@ -16,11 +16,11 @@
|
||||||
},
|
},
|
||||||
"room_directory": {
|
"room_directory": {
|
||||||
"servers": [
|
"servers": [
|
||||||
"einszwovier.local"
|
"124.local"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"enable_presence_by_hs_url": {
|
"enable_presence_by_hs_url": {
|
||||||
"http://einszwovier.local:8008": true
|
"http://124.local:8008": true
|
||||||
},
|
},
|
||||||
"terms_and_conditions_links": [],
|
"terms_and_conditions_links": [],
|
||||||
"privacy_policy_url": null
|
"privacy_policy_url": null
|
||||||
|
|
|
||||||
0
forgejo/data/git/.ssh/authorized_keys
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
forgejo/data/gitea/avatars/aab74637b6c33c04d73a89d1f584f407
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
|
|
@ -1,5 +1,8 @@
|
||||||
APP_NAME = Forgejo: Beyond coding. We forge.
|
APP_NAME = einszwovier forge
|
||||||
RUN_MODE = prod
|
RUN_MODE = prod
|
||||||
|
APP_SLOGAN =
|
||||||
|
RUN_USER = git
|
||||||
|
WORK_PATH = /data/gitea
|
||||||
|
|
||||||
[repository]
|
[repository]
|
||||||
ROOT = /data/git/repositories
|
ROOT = /data/git/repositories
|
||||||
|
|
@ -13,14 +16,16 @@ TEMP_PATH = /data/gitea/uploads
|
||||||
|
|
||||||
[server]
|
[server]
|
||||||
APP_DATA_PATH = /data/gitea
|
APP_DATA_PATH = /data/gitea
|
||||||
DOMAIN = localhost
|
DOMAIN = 124.local
|
||||||
SSH_DOMAIN = localhost
|
SSH_DOMAIN = 124.local
|
||||||
HTTP_PORT = 3000
|
HTTP_PORT = 3000
|
||||||
ROOT_URL =
|
ROOT_URL = http://124.local:3003/
|
||||||
DISABLE_SSH = false
|
DISABLE_SSH = false
|
||||||
SSH_PORT = 22
|
SSH_PORT = 22
|
||||||
SSH_LISTEN_PORT = 22
|
SSH_LISTEN_PORT = 22
|
||||||
LFS_START_SERVER = false
|
LFS_START_SERVER = true
|
||||||
|
LFS_JWT_SECRET = hZvHy32wQr50I0x51W9WqQvYqNEfm45PqAoq7KJcw2k
|
||||||
|
OFFLINE_MODE = true
|
||||||
|
|
||||||
[database]
|
[database]
|
||||||
PATH = /data/gitea/gitea.db
|
PATH = /data/gitea/gitea.db
|
||||||
|
|
@ -30,12 +35,15 @@ NAME = gitea
|
||||||
USER = root
|
USER = root
|
||||||
PASSWD =
|
PASSWD =
|
||||||
LOG_SQL = false
|
LOG_SQL = false
|
||||||
|
SCHEMA =
|
||||||
|
SSL_MODE = disable
|
||||||
|
|
||||||
[indexer]
|
[indexer]
|
||||||
ISSUE_INDEXER_PATH = /data/gitea/indexers/issues.bleve
|
ISSUE_INDEXER_PATH = /data/gitea/indexers/issues.bleve
|
||||||
|
|
||||||
[session]
|
[session]
|
||||||
PROVIDER_CONFIG = /data/gitea/sessions
|
PROVIDER_CONFIG = /data/gitea/sessions
|
||||||
|
PROVIDER = file
|
||||||
|
|
||||||
[picture]
|
[picture]
|
||||||
AVATAR_UPLOAD_PATH = /data/gitea/avatars
|
AVATAR_UPLOAD_PATH = /data/gitea/avatars
|
||||||
|
|
@ -50,14 +58,43 @@ LEVEL = info
|
||||||
ROOT_PATH = /data/gitea/log
|
ROOT_PATH = /data/gitea/log
|
||||||
|
|
||||||
[security]
|
[security]
|
||||||
INSTALL_LOCK = false
|
INSTALL_LOCK = true
|
||||||
SECRET_KEY =
|
SECRET_KEY =
|
||||||
REVERSE_PROXY_LIMIT = 1
|
REVERSE_PROXY_LIMIT = 1
|
||||||
REVERSE_PROXY_TRUSTED_PROXIES = *
|
REVERSE_PROXY_TRUSTED_PROXIES = *
|
||||||
|
INTERNAL_TOKEN = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOjE3NjI3NzI1NDN9.XRM_LxqMxCNJC4odRqZcNZ-C8LNxCV1pDFGi5ks789s
|
||||||
|
PASSWORD_HASH_ALGO = pbkdf2_hi
|
||||||
|
|
||||||
[service]
|
[service]
|
||||||
DISABLE_REGISTRATION = false
|
DISABLE_REGISTRATION = false
|
||||||
REQUIRE_SIGNIN_VIEW = false
|
REQUIRE_SIGNIN_VIEW = false
|
||||||
|
REGISTER_EMAIL_CONFIRM = false
|
||||||
|
ENABLE_NOTIFY_MAIL = false
|
||||||
|
ALLOW_ONLY_EXTERNAL_REGISTRATION = false
|
||||||
|
ENABLE_CAPTCHA = true
|
||||||
|
DEFAULT_KEEP_EMAIL_PRIVATE = false
|
||||||
|
DEFAULT_ALLOW_CREATE_ORGANIZATION = true
|
||||||
|
DEFAULT_ENABLE_TIMETRACKING = true
|
||||||
|
NO_REPLY_ADDRESS = noreply.localhost
|
||||||
|
|
||||||
[lfs]
|
[lfs]
|
||||||
PATH = /data/git/lfs
|
PATH = /data/git/lfs
|
||||||
|
|
||||||
|
[mailer]
|
||||||
|
ENABLED = false
|
||||||
|
|
||||||
|
[openid]
|
||||||
|
ENABLE_OPENID_SIGNIN = true
|
||||||
|
ENABLE_OPENID_SIGNUP = true
|
||||||
|
|
||||||
|
[cron.update_checker]
|
||||||
|
ENABLED = true
|
||||||
|
|
||||||
|
[repository.pull-request]
|
||||||
|
DEFAULT_MERGE_STYLE = merge
|
||||||
|
|
||||||
|
[repository.signing]
|
||||||
|
DEFAULT_TRUST_MODEL = committer
|
||||||
|
|
||||||
|
[oauth2]
|
||||||
|
JWT_SECRET = DCWJLYmNbdJngkBLfZBNHX9IHBFE1A-Op3EvURtCsU0
|
||||||
|
|
|
||||||
22
forgejo/data/gitea/home/.gitconfig
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
[diff]
|
||||||
|
algorithm = histogram
|
||||||
|
[core]
|
||||||
|
logallrefupdates = true
|
||||||
|
quotePath = false
|
||||||
|
commitGraph = true
|
||||||
|
[gc]
|
||||||
|
reflogexpire = 90
|
||||||
|
writeCommitGraph = true
|
||||||
|
[user]
|
||||||
|
name = Gitea
|
||||||
|
email = gitea@fake.local
|
||||||
|
[receive]
|
||||||
|
advertisePushOptions = true
|
||||||
|
procReceiveRefs = refs/for
|
||||||
|
[fetch]
|
||||||
|
writeCommitGraph = true
|
||||||
|
[safe]
|
||||||
|
directory = *
|
||||||
|
[uploadpack]
|
||||||
|
allowfilter = true
|
||||||
|
allowAnySHA1InWant = true
|
||||||
1
forgejo/data/gitea/indexers/issues.bleve/index_meta.json
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
{"storage":"boltdb","index_type":"scorch"}
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
{"version":4}
|
||||||
BIN
forgejo/data/gitea/indexers/issues.bleve/store/root.bolt
Normal file
BIN
forgejo/data/gitea/queues/common/000002.ldb
Normal file
1
forgejo/data/gitea/queues/common/CURRENT
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
MANIFEST-000004
|
||||||
0
forgejo/data/gitea/queues/common/LOCK
Normal file
16
forgejo/data/gitea/queues/common/LOG
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
=============== Nov 10, 2025 (CET) ===============
|
||||||
|
12:02:27.139922 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed
|
||||||
|
12:02:27.141510 db@open opening
|
||||||
|
12:02:27.141943 version@stat F·[] S·0B[] Sc·[]
|
||||||
|
12:02:27.142289 db@janitor F·2 G·0
|
||||||
|
12:02:27.142333 db@open done T·789.083µs
|
||||||
|
=============== Nov 10, 2025 (CET) ===============
|
||||||
|
14:41:11.536178 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed
|
||||||
|
14:41:11.537691 version@stat F·[] S·0B[] Sc·[]
|
||||||
|
14:41:11.537733 db@open opening
|
||||||
|
14:41:11.537879 journal@recovery F·1
|
||||||
|
14:41:11.537972 journal@recovery recovering @1
|
||||||
|
14:41:11.538886 memdb@flush created L0@2 N·26 S·542B "act..igh,v26":"web..low,v17"
|
||||||
|
14:41:11.539093 version@stat F·[1] S·542B[542B] Sc·[0.25]
|
||||||
|
14:41:11.543912 db@janitor F·3 G·0
|
||||||
|
14:41:11.543982 db@open done T·6.225416ms
|
||||||
BIN
forgejo/data/gitea/queues/common/MANIFEST-000004
Normal file
BIN
forgejo/data/gitea/sessions/6/7/671ec6cf26e818fa
Normal file
|
|
@ -11,7 +11,7 @@ async def send_order(pdf_path: str, analysis: dict, room_id: str, name: str, com
|
||||||
"""
|
"""
|
||||||
matrix_user = os.environ.get("MATRIX_USER")
|
matrix_user = os.environ.get("MATRIX_USER")
|
||||||
matrix_pass = os.environ.get("MATRIX_PASS")
|
matrix_pass = os.environ.get("MATRIX_PASS")
|
||||||
homeserver = os.environ.get("MATRIX_HOMESERVER", "http://einszwovier.local:8008")
|
homeserver = os.environ.get("MATRIX_HOMESERVER", "http://124.local:8008")
|
||||||
|
|
||||||
if not matrix_user or not matrix_pass:
|
if not matrix_user or not matrix_pass:
|
||||||
raise RuntimeError("Missing MATRIX_USER or MATRIX_PASS in environment")
|
raise RuntimeError("Missing MATRIX_USER or MATRIX_PASS in environment")
|
||||||
|
|
|
||||||
4
main.py
|
|
@ -14,7 +14,7 @@ from dotenv import load_dotenv
|
||||||
load_dotenv()
|
load_dotenv()
|
||||||
|
|
||||||
# Get server hostname from environment
|
# Get server hostname from environment
|
||||||
SERVER_HOSTNAME = os.environ.get("SERVER_HOSTNAME", "einszwovier.local")
|
SERVER_HOSTNAME = os.environ.get("SERVER_HOSTNAME", "124.local")
|
||||||
BOOKSTACK_PORT = os.environ.get("BOOKSTACK_PORT", "6875")
|
BOOKSTACK_PORT = os.environ.get("BOOKSTACK_PORT", "6875")
|
||||||
OPENWEBUI_PORT = os.environ.get("OPENWEBUI_PORT", "8080")
|
OPENWEBUI_PORT = os.environ.get("OPENWEBUI_PORT", "8080")
|
||||||
PORTAINER_PORT = os.environ.get("PORTAINER_PORT", "9000")
|
PORTAINER_PORT = os.environ.get("PORTAINER_PORT", "9000")
|
||||||
|
|
@ -162,7 +162,7 @@ def send_order_endpoint(
|
||||||
analysis = analyze_pdf(path)
|
analysis = analyze_pdf(path)
|
||||||
|
|
||||||
# Get Matrix room ID from environment
|
# Get Matrix room ID from environment
|
||||||
matrix_room = os.environ.get("MATRIX_ROOM", "!eFWbWEnYsgeIKqyfjw:einszwovier.local")
|
matrix_room = os.environ.get("MATRIX_ROOM", "!PuLwSlWKNgNKvhhCIr:124.local")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
send_order_sync(
|
send_order_sync(
|
||||||
|
|
|
||||||
30
matrix/data.backup.einszwovier/homeserver.yaml
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
# Configuration file for Synapse.
|
||||||
|
|
||||||
|
server_name: "einszwovier.local"
|
||||||
|
pid_file: /data/homeserver.pid
|
||||||
|
listeners:
|
||||||
|
- port: 8008
|
||||||
|
tls: false
|
||||||
|
type: http
|
||||||
|
x_forwarded: true
|
||||||
|
resources:
|
||||||
|
- names: [client, federation]
|
||||||
|
compress: false
|
||||||
|
database:
|
||||||
|
name: sqlite3
|
||||||
|
args:
|
||||||
|
database: /data/homeserver.db
|
||||||
|
|
||||||
|
# Connection and performance settings
|
||||||
|
max_upload_size: 50M
|
||||||
|
url_preview_enabled: false
|
||||||
|
|
||||||
|
log_config: "/data/localhost.log.config"
|
||||||
|
media_store_path: /data/media_store
|
||||||
|
registration_shared_secret: "D2mw3LqNKe98ga-pYO1l5KbXf^jgx&s5yjq&ipAGjln:AzLag8"
|
||||||
|
report_stats: false
|
||||||
|
macaroon_secret_key: "T26aaiHWLHbm+P6fi_8:VXTIn0W_kHH__CQAdhPyaLhBe~OG*_"
|
||||||
|
form_secret: "k,C38Dw^6b8Y+9-cSQpLb@GPoS*1POr8GDWXsLMKLHEU2+&q-@"
|
||||||
|
signing_key_path: "/data/localhost.signing.key"
|
||||||
|
trusted_key_servers:
|
||||||
|
- server_name: "matrix.org"
|
||||||
39
matrix/data.backup.einszwovier/localhost.log.config
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
version: 1
|
||||||
|
|
||||||
|
formatters:
|
||||||
|
precise:
|
||||||
|
|
||||||
|
format: '%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(request)s - %(message)s'
|
||||||
|
|
||||||
|
|
||||||
|
handlers:
|
||||||
|
|
||||||
|
|
||||||
|
console:
|
||||||
|
class: logging.StreamHandler
|
||||||
|
formatter: precise
|
||||||
|
|
||||||
|
loggers:
|
||||||
|
# This is just here so we can leave `loggers` in the config regardless of whether
|
||||||
|
# we configure other loggers below (avoid empty yaml dict error).
|
||||||
|
_placeholder:
|
||||||
|
level: "INFO"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
synapse.storage.SQL:
|
||||||
|
# beware: increasing this to DEBUG will make synapse log sensitive
|
||||||
|
# information such as access tokens.
|
||||||
|
level: INFO
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
root:
|
||||||
|
level: INFO
|
||||||
|
|
||||||
|
|
||||||
|
handlers: [console]
|
||||||
|
|
||||||
|
|
||||||
|
disable_existing_loggers: false
|
||||||
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 7.7 KiB |
|
|
@ -1,6 +1,6 @@
|
||||||
# Configuration file for Synapse.
|
# Configuration file for Synapse.
|
||||||
|
|
||||||
server_name: "einszwovier.local"
|
server_name: "124.local"
|
||||||
pid_file: /data/homeserver.pid
|
pid_file: /data/homeserver.pid
|
||||||
listeners:
|
listeners:
|
||||||
- port: 8008
|
- port: 8008
|
||||||
|
|
@ -28,3 +28,10 @@ form_secret: "k,C38Dw^6b8Y+9-cSQpLb@GPoS*1POr8GDWXsLMKLHEU2+&q-@"
|
||||||
signing_key_path: "/data/localhost.signing.key"
|
signing_key_path: "/data/localhost.signing.key"
|
||||||
trusted_key_servers:
|
trusted_key_servers:
|
||||||
- server_name: "matrix.org"
|
- server_name: "matrix.org"
|
||||||
|
|
||||||
|
# Allow guest access
|
||||||
|
allow_guest_access: true
|
||||||
|
|
||||||
|
# Enable registration
|
||||||
|
enable_registration: true
|
||||||
|
enable_registration_without_verification: true
|
||||||
|
|
|
||||||
60
register_user.py
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Register a user on Matrix server using shared secret
|
||||||
|
"""
|
||||||
|
import hmac
|
||||||
|
import hashlib
|
||||||
|
import json
|
||||||
|
import requests
|
||||||
|
|
||||||
|
# Get nonce
|
||||||
|
nonce_response = requests.get("http://localhost:8008/_synapse/admin/v1/register")
|
||||||
|
nonce = nonce_response.json()["nonce"]
|
||||||
|
|
||||||
|
print(f"Got nonce: {nonce[:20]}...")
|
||||||
|
|
||||||
|
# Registration details
|
||||||
|
username = "einszwovier"
|
||||||
|
password = "einszwo4"
|
||||||
|
admin = False
|
||||||
|
shared_secret = "D2mw3LqNKe98ga-pYO1l5KbXf^jgx&s5yjq&ipAGjln:AzLag8"
|
||||||
|
|
||||||
|
# Generate MAC
|
||||||
|
mac = hmac.new(
|
||||||
|
key=shared_secret.encode('utf8'),
|
||||||
|
digestmod=hashlib.sha1,
|
||||||
|
)
|
||||||
|
|
||||||
|
mac.update(nonce.encode('utf8'))
|
||||||
|
mac.update(b"\x00")
|
||||||
|
mac.update(username.encode('utf8'))
|
||||||
|
mac.update(b"\x00")
|
||||||
|
mac.update(password.encode('utf8'))
|
||||||
|
mac.update(b"\x00")
|
||||||
|
mac.update(b"notadmin" if not admin else b"admin")
|
||||||
|
|
||||||
|
mac_str = mac.hexdigest()
|
||||||
|
|
||||||
|
# Register user
|
||||||
|
data = {
|
||||||
|
"nonce": nonce,
|
||||||
|
"username": username,
|
||||||
|
"password": password,
|
||||||
|
"admin": admin,
|
||||||
|
"mac": mac_str
|
||||||
|
}
|
||||||
|
|
||||||
|
response = requests.post(
|
||||||
|
"http://localhost:8008/_synapse/admin/v1/register",
|
||||||
|
json=data
|
||||||
|
)
|
||||||
|
|
||||||
|
print(f"\nRegistration response: {response.status_code}")
|
||||||
|
print(json.dumps(response.json(), indent=2))
|
||||||
|
|
||||||
|
if response.status_code == 200:
|
||||||
|
print(f"\n✅ User registered successfully!")
|
||||||
|
print(f"User ID: {response.json()['user_id']}")
|
||||||
|
print(f"Access Token: {response.json()['access_token']}")
|
||||||
|
else:
|
||||||
|
print(f"\n❌ Registration failed")
|
||||||
62
setup_matrix.py
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Setup script for Matrix: register user and create room
|
||||||
|
"""
|
||||||
|
import asyncio
|
||||||
|
import os
|
||||||
|
from nio import AsyncClient, RegisterResponse, RoomCreateResponse
|
||||||
|
|
||||||
|
async def setup_matrix():
|
||||||
|
homeserver = "http://124.local:8008"
|
||||||
|
username = "einszwovier"
|
||||||
|
password = "einszwo4"
|
||||||
|
|
||||||
|
client = AsyncClient(homeserver)
|
||||||
|
|
||||||
|
print("🔧 Setting up Matrix server...")
|
||||||
|
print(f" Homeserver: {homeserver}")
|
||||||
|
print(f" Username: {username}")
|
||||||
|
|
||||||
|
# Register the user
|
||||||
|
print("\n📝 Registering user...")
|
||||||
|
response = await client.register(
|
||||||
|
username=username,
|
||||||
|
password=password
|
||||||
|
)
|
||||||
|
|
||||||
|
if isinstance(response, RegisterResponse):
|
||||||
|
print(f" ✅ User registered: {response.user_id}")
|
||||||
|
user_id = response.user_id
|
||||||
|
else:
|
||||||
|
print(f" ℹ️ User might already exist, trying to login...")
|
||||||
|
# Try to login instead
|
||||||
|
response = await client.login(password)
|
||||||
|
if response.user_id:
|
||||||
|
print(f" ✅ Logged in as: {response.user_id}")
|
||||||
|
user_id = response.user_id
|
||||||
|
else:
|
||||||
|
print(f" ❌ Failed: {response}")
|
||||||
|
await client.close()
|
||||||
|
return
|
||||||
|
|
||||||
|
# Create a room
|
||||||
|
print("\n🏠 Creating print orders room...")
|
||||||
|
response = await client.room_create(
|
||||||
|
name="Print Orders",
|
||||||
|
topic="PDF print order submissions from the web app",
|
||||||
|
is_public=False
|
||||||
|
)
|
||||||
|
|
||||||
|
if isinstance(response, RoomCreateResponse):
|
||||||
|
print(f" ✅ Room created!")
|
||||||
|
print(f" Room ID: {response.room_id}")
|
||||||
|
print(f"\n✅ Setup complete!")
|
||||||
|
print(f"\n📋 Add this to your .env file:")
|
||||||
|
print(f"MATRIX_ROOM={response.room_id}")
|
||||||
|
else:
|
||||||
|
print(f" ❌ Failed to create room: {response}")
|
||||||
|
|
||||||
|
await client.close()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
asyncio.run(setup_matrix())
|
||||||
|
|
@ -538,6 +538,98 @@ a.link-card:hover,
|
||||||
font-size: 0.9rem;
|
font-size: 0.9rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ========================================
|
||||||
|
ARCHITECTURE SPOILER
|
||||||
|
======================================== */
|
||||||
|
|
||||||
|
.architecture-spoiler {
|
||||||
|
margin: 0 auto 1.5rem;
|
||||||
|
max-width: 1200px;
|
||||||
|
width: calc(100% - 2rem);
|
||||||
|
background: #ffffff;
|
||||||
|
border: 1px solid #dee2e6;
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.architecture-summary {
|
||||||
|
padding: 0.8rem 1.2rem;
|
||||||
|
background: #f8f9fa;
|
||||||
|
color: #2C3E50;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
font-weight: 600;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.6rem;
|
||||||
|
transition: background-color 0.2s ease;
|
||||||
|
user-select: none;
|
||||||
|
border-bottom: 1px solid #dee2e6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.architecture-summary:hover {
|
||||||
|
background: #e9ecef;
|
||||||
|
}
|
||||||
|
|
||||||
|
.architecture-summary i {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
color: #6c757d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.architecture-spoiler[open] .architecture-summary {
|
||||||
|
background: #e9ecef;
|
||||||
|
}
|
||||||
|
|
||||||
|
.architecture-content {
|
||||||
|
padding: 1.5rem;
|
||||||
|
background: #ffffff;
|
||||||
|
text-align: center;
|
||||||
|
max-height: 70vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.architecture-image {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto;
|
||||||
|
max-height: 60vh;
|
||||||
|
object-fit: contain;
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: opacity 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.architecture-image:hover {
|
||||||
|
opacity: 0.95;
|
||||||
|
}
|
||||||
|
|
||||||
|
.architecture-caption {
|
||||||
|
margin-top: 1rem;
|
||||||
|
color: #6c757d;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive adjustments for mobile */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.architecture-spoiler {
|
||||||
|
width: calc(100% - 1rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
.architecture-content {
|
||||||
|
padding: 1rem;
|
||||||
|
max-height: 50vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.architecture-image {
|
||||||
|
max-height: 45vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.architecture-summary {
|
||||||
|
font-size: 0.85rem;
|
||||||
|
padding: 0.7rem 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ========================================
|
/* ========================================
|
||||||
BUTTONS
|
BUTTONS
|
||||||
|
|
|
||||||
BIN
static/images/architecture.png
Normal file
|
After Width: | Height: | Size: 236 KiB |
|
|
@ -36,8 +36,6 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- Information and Tools Cards -->
|
<!-- Information and Tools Cards -->
|
||||||
<div class="link-cards-grid">
|
<div class="link-cards-grid">
|
||||||
<a class="link-card" href="/about">
|
<a class="link-card" href="/about">
|
||||||
|
|
@ -76,7 +74,7 @@
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a class="link-card" href="http://{{ server_hostname }}:8001" target="_blank">
|
<a class="link-card" href="http://{{ server_hostname }}:8001/hub/" target="_blank">
|
||||||
<i class="fas fa-code card-icon"></i>
|
<i class="fas fa-code card-icon"></i>
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<div class="title">JupyterHub</div>
|
<div class="title">JupyterHub</div>
|
||||||
|
|
@ -117,6 +115,20 @@
|
||||||
|
|
||||||
<!-- Footer with Admin Panel and Source Link -->
|
<!-- Footer with Admin Panel and Source Link -->
|
||||||
<footer class="footer">
|
<footer class="footer">
|
||||||
|
<!-- Architecture Diagram Spoiler -->
|
||||||
|
<details class="architecture-spoiler">
|
||||||
|
<summary class="architecture-summary">
|
||||||
|
<i class="fas fa-sitemap"></i>
|
||||||
|
<span>Systemarchitektur anzeigen</span>
|
||||||
|
</summary>
|
||||||
|
<div class="architecture-content">
|
||||||
|
<img src="/static/images/architecture.png" alt="Studio einszwovier Architecture Diagram"
|
||||||
|
class="architecture-image">
|
||||||
|
<p class="architecture-caption">Übersicht über alle Dienste und deren Zusammenspiel im Studio einszwovier
|
||||||
|
System</p>
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
|
||||||
<div class="footer-container">
|
<div class="footer-container">
|
||||||
<a href="http://{{ server_hostname }}:{{ portainer_port }}" target="_blank" class="admin-link">Admin Panel
|
<a href="http://{{ server_hostname }}:{{ portainer_port }}" target="_blank" class="admin-link">Admin Panel
|
||||||
(Portainer)</a>
|
(Portainer)</a>
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ async def test_connection(homeserver_url: str):
|
||||||
"""Test connection to Matrix homeserver"""
|
"""Test connection to Matrix homeserver"""
|
||||||
print(f"\n🔍 Testing connection to: {homeserver_url}")
|
print(f"\n🔍 Testing connection to: {homeserver_url}")
|
||||||
|
|
||||||
matrix_user = os.environ.get("MATRIX_USER", "@einszwovier:einszwovier.local")
|
matrix_user = os.environ.get("MATRIX_USER", "@einszwovier:124.local")
|
||||||
matrix_pass = os.environ.get("MATRIX_PASS", "einszwo4")
|
matrix_pass = os.environ.get("MATRIX_PASS", "einszwo4")
|
||||||
|
|
||||||
client = AsyncClient(homeserver_url, matrix_user)
|
client = AsyncClient(homeserver_url, matrix_user)
|
||||||
|
|
@ -44,7 +44,7 @@ async def main():
|
||||||
test_urls = [
|
test_urls = [
|
||||||
"http://localhost:8008",
|
"http://localhost:8008",
|
||||||
"http://127.0.0.1:8008",
|
"http://127.0.0.1:8008",
|
||||||
"http://einszwovier.local:8008",
|
"http://124.local:8008",
|
||||||
]
|
]
|
||||||
|
|
||||||
for url in test_urls:
|
for url in test_urls:
|
||||||
|
|
@ -52,7 +52,7 @@ async def main():
|
||||||
|
|
||||||
print("\n" + "=" * 60)
|
print("\n" + "=" * 60)
|
||||||
print("Recommendation:")
|
print("Recommendation:")
|
||||||
print("If localhost/127.0.0.1 works but einszwovier.local fails,")
|
print("If localhost/127.0.0.1 works but 124.local fails,")
|
||||||
print("configure Element to use: http://localhost:8008")
|
print("configure Element to use: http://localhost:8008")
|
||||||
print("=" * 60)
|
print("=" * 60)
|
||||||
|
|
||||||
|
|
|
||||||