Compare commits
1 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
d464cf89a0 |
|
|
@ -6,8 +6,7 @@ services:
|
||||||
networks:
|
networks:
|
||||||
- caddy_default
|
- caddy_default
|
||||||
volumes:
|
volumes:
|
||||||
- /docker/data/auth/db:/db
|
- /docker/data/auth/config:/config
|
||||||
- ./config:/config:ro
|
|
||||||
networks:
|
networks:
|
||||||
caddy_default:
|
caddy_default:
|
||||||
external: true
|
external: true
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,157 +0,0 @@
|
||||||
theme: 'dark'
|
|
||||||
|
|
||||||
default_2fa_method: 'totp'
|
|
||||||
|
|
||||||
totp:
|
|
||||||
issuer: 'auth.aggtaa.com'
|
|
||||||
|
|
||||||
identity_validation:
|
|
||||||
reset_password:
|
|
||||||
jwt_secret: 'ShnKq2VDRwA1fMxwhmPmkj3DJdt40CqO6WWyDKmdohFQH7WAypikiq109yKf9nUv'
|
|
||||||
|
|
||||||
authentication_backend:
|
|
||||||
file:
|
|
||||||
path: '/config/users_database.yml'
|
|
||||||
watch: true
|
|
||||||
search:
|
|
||||||
email: false
|
|
||||||
case_insensitive: false
|
|
||||||
password:
|
|
||||||
algorithm: 'argon2'
|
|
||||||
argon2:
|
|
||||||
variant: 'argon2id'
|
|
||||||
iterations: 3
|
|
||||||
memory: 65536
|
|
||||||
parallelism: 4
|
|
||||||
key_length: 32
|
|
||||||
salt_length: 16
|
|
||||||
|
|
||||||
access_control:
|
|
||||||
default_policy: 'deny'
|
|
||||||
|
|
||||||
rules:
|
|
||||||
- domain: "*.aggtaa.com"
|
|
||||||
policy: two_factor
|
|
||||||
networks:
|
|
||||||
- 192.168.0.0/16
|
|
||||||
- 10.0.0.0/8
|
|
||||||
- 172.16.0.0/12
|
|
||||||
|
|
||||||
- domain: "*.aggtaa.com"
|
|
||||||
policy: two_factor
|
|
||||||
|
|
||||||
session:
|
|
||||||
name: 'aas'
|
|
||||||
secret: 'It1PZBvUNXvfbRnaOSBkupXxCMt8FRrc'
|
|
||||||
cookies:
|
|
||||||
- name: 'aas'
|
|
||||||
domain: 'aggtaa.com'
|
|
||||||
authelia_url: 'https://auth.aggtaa.com'
|
|
||||||
|
|
||||||
regulation:
|
|
||||||
max_retries: 3
|
|
||||||
find_time: '2 minutes'
|
|
||||||
ban_time: '5 minutes'
|
|
||||||
|
|
||||||
storage:
|
|
||||||
encryption_key: '8Ei4XmiFM1GF7EWxiHyyReEWSuUgc4zH'
|
|
||||||
local:
|
|
||||||
path: '/db/db.sqlite3'
|
|
||||||
|
|
||||||
notifier:
|
|
||||||
smtp:
|
|
||||||
address: 'smtp://mail-eu.smtp2go.com:587'
|
|
||||||
username: 'robot@aggtaa.com'
|
|
||||||
password: 'ULCKdUexeCQVgDl3'
|
|
||||||
sender: 'auth.aggtaa.com <robot@aggtaa.com>'
|
|
||||||
|
|
||||||
subject: 'auth.aggtaa.com: {title}'
|
|
||||||
|
|
||||||
tls:
|
|
||||||
server_name: 'mail-eu.smtp2go.com'
|
|
||||||
|
|
||||||
identity_providers:
|
|
||||||
oidc:
|
|
||||||
hmac_secret: 'mbHg5s2JnQDuGdtBxrofu7uiu4MR7098'
|
|
||||||
jwks:
|
|
||||||
- key_id: "main"
|
|
||||||
algorithm: 'RS256'
|
|
||||||
use: "sig"
|
|
||||||
key: |
|
|
||||||
-----BEGIN PRIVATE KEY-----
|
|
||||||
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCiXH1drELlUhBh
|
|
||||||
RL0YayG5k+6jLnGD1646iHbB36rGTFRdhbz8h9v1g+QkPlHY9chzTtc67QD89myv
|
|
||||||
46+pYW32QmIlx86xrP7AMFxyHhL8XSVV3AHSWiNL1RwN59Aa1IMo2xhw36ZH0WH/
|
|
||||||
bAmodMQ9lIrP4T3EU4tUoRvO7RvcHW/ngrfd5xonmBLY13m+sAXtolG8yRzVW2Qh
|
|
||||||
ew1Y55r6Armrihvyo+/0L5raBrPt+w476t35rz/uUUIvdrQMUwIitpd3vP9j0rW/
|
|
||||||
sTkKrx9Djgs9ECIvIyhJcMiiCZnpqWnUvJzYAbbehU6T4ASW8qOD5d2LBA7cO9R+
|
|
||||||
QNrrj2wbAgMBAAECggEAFWyu/lVk3m3dy3gOm9JHOP3UV6QhRoyHaSHoydyB5Hje
|
|
||||||
CRlEvu4OkG8/A6lVk5ObR9v3escbgkXiQbOB0pAQupY37VRYagmx9BptmIFvb+26
|
|
||||||
p4HIm8FZNwCAGzWjuGaiiBmhOAPLJV7z14iiHaCK5LVdO+E1DVsY36oCyWNwcbMa
|
|
||||||
4+d6RGgbFeUHXGnuayd5hTvUlsXAbPo4/gJT1KDvqPPjZl8U6ur1mRIt+BTzrntv
|
|
||||||
C2oN1hq+cJQRrQhySt0/QNAE+k4+r70ZKC/4rDjYkdhyBqNPq7mjAYJ7miWF/YFZ
|
|
||||||
4AYzo+z7Mws1sMJkNG/SFaNXWgh8KWdFHfgZNWSogQKBgQDSf2w8j9WC1h30FtKy
|
|
||||||
kGYWFKcNYM2AGoE5PnT1bxvBOtgrttwOVsXESIjyXgRygKvZgExIx3nh8bUkXHWL
|
|
||||||
31wY5y1I6ZrvFIKNsfaQm8sf9PttH4biXJ3h9eBYeBx7y/3+QAOqqiDF+vcGOWJF
|
|
||||||
xA7ZKBjz2NEgdr7c7jFsIIOiwwKBgQDFdUeOm+lY24nU0/qC06Zk7tjf3xxRGq9d
|
|
||||||
Fddix1ENUS2BGcltOVr1UedWeoBeN5P004FqzRHyX4Z/1Yvzvax809TqyT36lQ/z
|
|
||||||
zBjizZKggAmfU5wCCpuSubT+Wq1o3FPQ5fLbnllFMf1UE64lZouAT1NHFHuwDrYV
|
|
||||||
e8bBCwzLyQKBgQCZSMkc4PDuMdXmJaiQ964fbjKn/1Imcyae9OheweZIM/2u954P
|
|
||||||
owipAtkXBXffmeuKm27xoLEU49qw+9NtY93BFLdZXSPB7gGUBYAzlf+46cEdmdOz
|
|
||||||
ixY9sbsJMY4saEQxnZQN942eHj88fRUfEMJvSE/DYqQHK/GZGKtMvfCd2QKBgF9Y
|
|
||||||
EvZUaGdkkng25yaWxijEf+oRlF3BMd4Tts3WileQ1BUbe3yHDlmYc8j5G9Tip0m3
|
|
||||||
ey0z2i+bWpmNZqeJ9ajMrGm2RHwjz/EbowSY2O0xBfRt7c26i4Zcr32GEWepw7sB
|
|
||||||
3bOYEWjtC3K2kgczLbcGFqMiy9qmL9vNyZnbGRGpAoGBAKbIM3P1XrfJ2Uogbq1g
|
|
||||||
ssjngQ/HvAbFwZlAP0mH6H1A8skJiqZ/unjlo98wAj7v912nd3rrm9VKZGkXakSR
|
|
||||||
MqhDyoDv+RIbyhznbRiGd7S6ddqTx2zm03svlCqQZUH92GmFgQlUJ7AngqlxqxEv
|
|
||||||
LHwFtrfVT+ViB1m8zP+RieKb
|
|
||||||
-----END PRIVATE KEY-----
|
|
||||||
|
|
||||||
enable_client_debug_messages: true
|
|
||||||
cors:
|
|
||||||
## List of endpoints in addition to the metadata endpoints to permit cross-origin requests on.
|
|
||||||
endpoints:
|
|
||||||
- 'authorization'
|
|
||||||
- 'pushed-authorization-request'
|
|
||||||
- 'token'
|
|
||||||
- 'revocation'
|
|
||||||
- 'introspection'
|
|
||||||
- 'userinfo'
|
|
||||||
|
|
||||||
clients:
|
|
||||||
- client_id: opencloud
|
|
||||||
client_name: files.aggtaa.com
|
|
||||||
public: true
|
|
||||||
consent_mode: pre-configured # store user consent for some time
|
|
||||||
pre_configured_consent_duration: 100y
|
|
||||||
scopes:
|
|
||||||
- openid
|
|
||||||
- email
|
|
||||||
- profile
|
|
||||||
- groups
|
|
||||||
redirect_uris:
|
|
||||||
- https://files.aggtaa.com/
|
|
||||||
- https://files.aggtaa.com/oidc-callback.html
|
|
||||||
- https://files.aggtaa.com/oidc-silent-redirect.html
|
|
||||||
|
|
||||||
- client_id: 'filebrowser-quantum'
|
|
||||||
client_name: 'files.aggtaa.com'
|
|
||||||
client_secret: '$pbkdf2-sha512$310000$Dtx8Y69nPActRIoqOWEXQQ$4F0bgcL7rf90toYT9tljgBRumTdgkoop4RMg3crSQNfiY/Y2cPKXqgUhU8G/1uf/hZv1Sz4Yl0Aec.xwG/VSnA'
|
|
||||||
public: false
|
|
||||||
consent_mode: implicit
|
|
||||||
require_pkce: false
|
|
||||||
pkce_challenge_method: ''
|
|
||||||
redirect_uris:
|
|
||||||
- 'https://files.aggtaa.com/api/auth/oidc/callback'
|
|
||||||
scopes:
|
|
||||||
- 'openid'
|
|
||||||
- 'profile'
|
|
||||||
- 'groups'
|
|
||||||
- 'email'
|
|
||||||
response_types:
|
|
||||||
- 'code'
|
|
||||||
grant_types:
|
|
||||||
- 'authorization_code'
|
|
||||||
access_token_signed_response_alg: 'none'
|
|
||||||
userinfo_signed_response_alg: 'none'
|
|
||||||
token_endpoint_auth_method: 'client_secret_basic'
|
|
||||||
|
|
@ -1,50 +0,0 @@
|
||||||
users:
|
|
||||||
anton:
|
|
||||||
password: $argon2id$v=19$m=65536,t=3,p=4$1V2lonkSH9bZoCrHm0eIkg$J4CiQ9fb0GXsadxLSOqkdPwQQZMcqFd0MIIgI8hY7VA
|
|
||||||
displayname: Anton Zykov
|
|
||||||
email: anton@ormo.cc
|
|
||||||
groups:
|
|
||||||
- admins
|
|
||||||
- dev
|
|
||||||
- users
|
|
||||||
- files-admin
|
|
||||||
- files-user
|
|
||||||
given_name: ""
|
|
||||||
middle_name: ""
|
|
||||||
family_name: ""
|
|
||||||
nickname: ""
|
|
||||||
gender: ""
|
|
||||||
birthdate: ""
|
|
||||||
website: ""
|
|
||||||
profile: ""
|
|
||||||
picture: ""
|
|
||||||
zoneinfo: ""
|
|
||||||
locale: ""
|
|
||||||
phone_number: ""
|
|
||||||
phone_extension: ""
|
|
||||||
disabled: false
|
|
||||||
address: null
|
|
||||||
extra: {}
|
|
||||||
jintara:
|
|
||||||
password: $argon2id$v=19$m=65536,t=3,p=4$vTZR3nzagcHGD/cxds197Q$piGaPOfeXoCRUbgyBkWGI5lwD8yaIJz4Hd17t/omBXI
|
|
||||||
displayname: Ekaterina Zykova
|
|
||||||
email: ekaterina.r.zykova@gmail.com
|
|
||||||
groups:
|
|
||||||
- users
|
|
||||||
- files-user
|
|
||||||
given_name: ""
|
|
||||||
middle_name: ""
|
|
||||||
family_name: ""
|
|
||||||
nickname: ""
|
|
||||||
gender: ""
|
|
||||||
birthdate: ""
|
|
||||||
website: ""
|
|
||||||
profile: ""
|
|
||||||
picture: ""
|
|
||||||
zoneinfo: ""
|
|
||||||
locale: ""
|
|
||||||
phone_number: ""
|
|
||||||
phone_extension: ""
|
|
||||||
disabled: false
|
|
||||||
address: null
|
|
||||||
extra: {}
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
PG_PASS=k6jvY2g0MMo7ObX7xMVNLL3fSkrVZNcqKzC6z1rNnhlb4slS
|
||||||
|
AUTHENTIK_SECRET_KEY=JCM1VPqhJ+5PSpq9Zizs9UHrEpa1lz7d1URxOXBSXvBW6X9KcTivPcocq3N6QuwhYhAJlmKgLH4H3qrG
|
||||||
|
AUTHENTIK_ERROR_REPORTING__ENABLED=false
|
||||||
|
|
@ -0,0 +1,85 @@
|
||||||
|
services:
|
||||||
|
|
||||||
|
postgresql:
|
||||||
|
image: docker.io/library/postgres:16-alpine
|
||||||
|
container_name: authentik_db
|
||||||
|
restart: unless-stopped
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
environment:
|
||||||
|
POSTGRES_DB: ${PG_DB:-authentik}
|
||||||
|
POSTGRES_PASSWORD: ${PG_PASS:?database password required}
|
||||||
|
POSTGRES_USER: ${PG_USER:-authentik}
|
||||||
|
healthcheck:
|
||||||
|
interval: 30s
|
||||||
|
retries: 5
|
||||||
|
start_period: 20s
|
||||||
|
test:
|
||||||
|
- CMD-SHELL
|
||||||
|
- pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}
|
||||||
|
timeout: 5s
|
||||||
|
volumes:
|
||||||
|
- /docker/data/authentik/db:/var/lib/postgresql/data
|
||||||
|
networks:
|
||||||
|
- net
|
||||||
|
|
||||||
|
server:
|
||||||
|
image: ghcr.io/goauthentik/server:2026.2
|
||||||
|
container_name: authentik_server
|
||||||
|
restart: always
|
||||||
|
user: root
|
||||||
|
depends_on:
|
||||||
|
postgresql:
|
||||||
|
condition: service_healthy
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
environment:
|
||||||
|
AUTHENTIK_POSTGRESQL__HOST: postgresql
|
||||||
|
AUTHENTIK_POSTGRESQL__NAME: ${PG_DB:-authentik}
|
||||||
|
AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS}
|
||||||
|
AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik}
|
||||||
|
AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY:?secret key required}
|
||||||
|
AUTHENTIK_POSTGRESQL__SSLMODE: disable
|
||||||
|
AUTHENTIK_OUTPOSTS__DISCOVER: false
|
||||||
|
ports:
|
||||||
|
- 9000:9000
|
||||||
|
shm_size: 512mb
|
||||||
|
volumes:
|
||||||
|
- /docker/data/authentik/data:/data
|
||||||
|
- /docker/data/authentik/templates:/templates
|
||||||
|
command: server
|
||||||
|
networks:
|
||||||
|
- net
|
||||||
|
- caddy_default
|
||||||
|
|
||||||
|
worker:
|
||||||
|
image: ghcr.io/goauthentik/server:2026.2
|
||||||
|
container_name: authentik_worker
|
||||||
|
restart: always
|
||||||
|
user: root
|
||||||
|
depends_on:
|
||||||
|
postgresql:
|
||||||
|
condition: service_healthy
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
environment:
|
||||||
|
AUTHENTIK_POSTGRESQL__HOST: postgresql
|
||||||
|
AUTHENTIK_POSTGRESQL__NAME: ${PG_DB:-authentik}
|
||||||
|
AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS}
|
||||||
|
AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik}
|
||||||
|
AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY:?secret key required}
|
||||||
|
shm_size: 512mb
|
||||||
|
volumes:
|
||||||
|
# - /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
- /docker/data/authentik/data:/data
|
||||||
|
- /docker/data/authentik/templates:/templates
|
||||||
|
- /docker/data/authentik/certs:/certsx
|
||||||
|
command: worker
|
||||||
|
networks:
|
||||||
|
- net
|
||||||
|
|
||||||
|
networks:
|
||||||
|
net:
|
||||||
|
internal: true
|
||||||
|
caddy_default:
|
||||||
|
external: true
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
services:
|
|
||||||
bunkerm:
|
|
||||||
image: bunkeriot/bunkerm:latest
|
|
||||||
container_name: bunkerm
|
|
||||||
restart: always
|
|
||||||
ports:
|
|
||||||
- "1883:1900"
|
|
||||||
volumes:
|
|
||||||
- /docker/data/bunkerm/mosquitto_data:/var/lib/mosquitto
|
|
||||||
- ./config/mosquitto:/etc/mosquitto
|
|
||||||
- /docker/data/bunkerm/data:/data
|
|
||||||
- /docker/data/bunkerm/history:/var/lib/history
|
|
||||||
environment:
|
|
||||||
- HISTORY_MAX_MESSAGES=50000
|
|
||||||
- HISTORY_MAX_AGE_DAYS=7
|
|
||||||
networks:
|
|
||||||
- caddy_default
|
|
||||||
networks:
|
|
||||||
caddy_default:
|
|
||||||
external: true
|
|
||||||
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
# MQTT listener on port 1900
|
|
||||||
listener 1900
|
|
||||||
per_listener_settings false
|
|
||||||
allow_anonymous false
|
|
||||||
|
|
||||||
# HTTP listener for Dynamic Security Plugin on port 8080
|
|
||||||
listener 8080
|
|
||||||
#password_file /etc/mosquitto/passwd
|
|
||||||
password_file /etc/mosquitto/mosquitto_passwd
|
|
||||||
# Dynamic Security Plugin configuration
|
|
||||||
plugin /usr/lib/mosquitto_dynamic_security.so
|
|
||||||
plugin_opt_config_file /var/lib/mosquitto/dynamic-security.json
|
|
||||||
log_dest file /var/log/mosquitto/mosquitto.log
|
|
||||||
log_type all
|
|
||||||
log_timestamp true
|
|
||||||
persistence true
|
|
||||||
persistence_location /var/lib/mosquitto/
|
|
||||||
persistence_file mosquitto.db
|
|
||||||
|
|
@ -103,46 +103,4 @@ git.aggtaa.com {
|
||||||
log {
|
log {
|
||||||
output file /var/log/caddy/git.aggtaa.com.log
|
output file /var/log/caddy/git.aggtaa.com.log
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
files.aggtaa.com {
|
|
||||||
reverse_proxy filebrowser-quantum:80
|
|
||||||
log {
|
|
||||||
output file /var/log/caddy/files.aggtaa.com.log
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
photo.aggtaa.com {
|
|
||||||
reverse_proxy immich_server:2283
|
|
||||||
log {
|
|
||||||
output file /var/log/caddy/photo.aggtaa.com.log
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mqtt.aggtaa.com {
|
|
||||||
forward_auth auth:9091 {
|
|
||||||
uri /api/authz/forward-auth
|
|
||||||
copy_headers Remote-User Remote-Groups Remote-Email Remote-Name
|
|
||||||
}
|
|
||||||
|
|
||||||
reverse_proxy bunkerm:2000
|
|
||||||
log {
|
|
||||||
output file /var/log/caddy/mqtt.aggtaa.com.log
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
quest.aggtaa.com {
|
|
||||||
|
|
||||||
reverse_proxy tidyquest:3000
|
|
||||||
log {
|
|
||||||
output file /var/log/caddy/quest.aggtaa.com.log
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
notes.aggtaa.com {
|
|
||||||
|
|
||||||
reverse_proxy joplin_app:22300
|
|
||||||
log {
|
|
||||||
output file /var/log/caddy/notes.aggtaa.com.log
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
services:
|
|
||||||
filebrowser:
|
|
||||||
image: ghcr.io/gtsteffaniak/filebrowser
|
|
||||||
container_name: filebrowser-quantum
|
|
||||||
restart: always
|
|
||||||
user: root
|
|
||||||
healthcheck:
|
|
||||||
test: ["CMD", "curl", "-f", "http://localhost/health"]
|
|
||||||
interval: 30s
|
|
||||||
timeout: 3s
|
|
||||||
start_period: 10s
|
|
||||||
retries: 3
|
|
||||||
volumes:
|
|
||||||
- /docker/data/files:/folder
|
|
||||||
- /docker/data/filebrowser-quantum/db:/home/filebrowser/data/database
|
|
||||||
- /docker/data/filebrowser-quantum/tmp:/home/filebrowser/data/tmp
|
|
||||||
- ./config/config.yaml:/home/filebrowser/data/config.yaml:ro
|
|
||||||
networks:
|
|
||||||
- caddy_default
|
|
||||||
networks:
|
|
||||||
caddy_default:
|
|
||||||
external: true
|
|
||||||
|
|
@ -1,41 +0,0 @@
|
||||||
server:
|
|
||||||
cacheDir: /home/filebrowser/data/tmp # using the data volume so it can persist across restarts
|
|
||||||
database: /home/filebrowser/data/database/quantum.db
|
|
||||||
sources:
|
|
||||||
|
|
||||||
- path: /folder # Do not use a root "/" directory or include the "/var" folder
|
|
||||||
name: "My Files"
|
|
||||||
config:
|
|
||||||
defaultEnabled: true
|
|
||||||
createUserDir: true
|
|
||||||
defaultUserScope: "/"
|
|
||||||
useLogicalSize: true
|
|
||||||
|
|
||||||
- path: "/folder/shared"
|
|
||||||
name: "Shared Files"
|
|
||||||
config:
|
|
||||||
defaultEnabled: true
|
|
||||||
useLogicalSize: true
|
|
||||||
|
|
||||||
auth:
|
|
||||||
methods:
|
|
||||||
|
|
||||||
password:
|
|
||||||
enabled: true # 'admin' uses password as fallback
|
|
||||||
signup: false
|
|
||||||
|
|
||||||
oidc:
|
|
||||||
enabled: true
|
|
||||||
clientId: 'filebrowser-quantum'
|
|
||||||
clientSecret: 'g4HptlioyTK9ozpWh09IvQ3w1rDCfJvX'
|
|
||||||
issuerUrl: 'https://auth.aggtaa.com'
|
|
||||||
scopes: 'email openid profile groups'
|
|
||||||
userIdentifier: 'preferred_username'
|
|
||||||
disableVerifyTLS: false
|
|
||||||
logoutRedirectUrl: ''
|
|
||||||
createUser: true
|
|
||||||
adminGroup: 'files-admin'
|
|
||||||
groupsClaim: 'groups'
|
|
||||||
|
|
||||||
userDefaults:
|
|
||||||
loginMethod: "oidc"
|
|
||||||
22
immich/.env
22
immich/.env
|
|
@ -1,22 +0,0 @@
|
||||||
# You can find documentation for all the supported env variables at https://docs.immich.app/install/environment-variables
|
|
||||||
|
|
||||||
# The location where your uploaded files are stored
|
|
||||||
UPLOAD_LOCATION=/storage/large/immich/uploads
|
|
||||||
|
|
||||||
# The location where your database files are stored. Network shares are not supported for the database
|
|
||||||
DB_DATA_LOCATION=/storage/large/immich/db
|
|
||||||
|
|
||||||
# To set a timezone, uncomment the next line and change Etc/UTC to a TZ identifier from this list: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List
|
|
||||||
TZ=Europe/Moscow
|
|
||||||
|
|
||||||
# The Immich version to use. You can pin this to a specific version like "v2.1.0"
|
|
||||||
IMMICH_VERSION=v2
|
|
||||||
|
|
||||||
# Connection secret for postgres. You should change it to a random password
|
|
||||||
# Please use only the characters `A-Za-z0-9`, without special characters or spaces
|
|
||||||
DB_PASSWORD=nUKz7KUS7x8mjQYsqBuyKVQpsw7Y1zYB
|
|
||||||
|
|
||||||
# The values below this line do not need to be changed
|
|
||||||
###################################################################################
|
|
||||||
DB_USERNAME=postgres
|
|
||||||
DB_DATABASE_NAME=immich
|
|
||||||
|
|
@ -1,91 +0,0 @@
|
||||||
#
|
|
||||||
# WARNING: To install Immich, follow our guide: https://docs.immich.app/install/docker-compose
|
|
||||||
#
|
|
||||||
# Make sure to use the docker-compose.yml of the current release:
|
|
||||||
#
|
|
||||||
# https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml
|
|
||||||
#
|
|
||||||
# The compose file on main may not be compatible with the latest release.
|
|
||||||
|
|
||||||
name: immich
|
|
||||||
|
|
||||||
services:
|
|
||||||
immich-server:
|
|
||||||
container_name: immich_server
|
|
||||||
image: ghcr.io/immich-app/immich-server:${IMMICH_VERSION:-release}
|
|
||||||
# extends:
|
|
||||||
# file: hwaccel.transcoding.yml
|
|
||||||
# service: cpu # set to one of [nvenc, quicksync, rkmpp, vaapi, vaapi-wsl] for accelerated transcoding
|
|
||||||
volumes:
|
|
||||||
# Do not edit the next line. If you want to change the media storage location on your system, edit the value of UPLOAD_LOCATION in the .env file
|
|
||||||
- ${UPLOAD_LOCATION}:/data
|
|
||||||
- /etc/localtime:/etc/localtime:ro
|
|
||||||
env_file:
|
|
||||||
- .env
|
|
||||||
depends_on:
|
|
||||||
- redis
|
|
||||||
- database
|
|
||||||
restart: always
|
|
||||||
healthcheck:
|
|
||||||
disable: false
|
|
||||||
networks:
|
|
||||||
- net
|
|
||||||
- caddy_default
|
|
||||||
|
|
||||||
immich-machine-learning:
|
|
||||||
container_name: immich_machine_learning
|
|
||||||
# For hardware acceleration, add one of -[armnn, cuda, rocm, openvino, rknn] to the image tag.
|
|
||||||
# Example tag: ${IMMICH_VERSION:-release}-cuda
|
|
||||||
# image: ghcr.io/immich-app/immich-machine-learning:${IMMICH_VERSION:-release}
|
|
||||||
image: ghcr.io/immich-app/immich-machine-learning:${IMMICH_VERSION:-release}-openvino
|
|
||||||
extends: # uncomment this section for hardware acceleration - see https://docs.immich.app/features/ml-hardware-acceleration
|
|
||||||
file: hwaccel.ml.yml
|
|
||||||
service: openvino # set to one of [armnn, cuda, rocm, openvino, openvino-wsl, rknn] for accelerated inference - use the `-wsl` version for WSL2 where applicable
|
|
||||||
cpus: 1
|
|
||||||
volumes:
|
|
||||||
- model-cache:/cache
|
|
||||||
env_file:
|
|
||||||
- .env
|
|
||||||
restart: always
|
|
||||||
healthcheck:
|
|
||||||
disable: false
|
|
||||||
networks:
|
|
||||||
- net
|
|
||||||
|
|
||||||
redis:
|
|
||||||
container_name: immich_redis
|
|
||||||
image: docker.io/valkey/valkey:9@sha256:3eeb09785cd61ec8e3be35f8804c8892080f3ca21934d628abc24ee4ed1698f6
|
|
||||||
healthcheck:
|
|
||||||
test: redis-cli ping || exit 1
|
|
||||||
restart: always
|
|
||||||
networks:
|
|
||||||
- net
|
|
||||||
|
|
||||||
database:
|
|
||||||
container_name: immich_postgres
|
|
||||||
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:bcf63357191b76a916ae5eb93464d65c07511da41e3bf7a8416db519b40b1c23
|
|
||||||
environment:
|
|
||||||
POSTGRES_PASSWORD: ${DB_PASSWORD}
|
|
||||||
POSTGRES_USER: ${DB_USERNAME}
|
|
||||||
POSTGRES_DB: ${DB_DATABASE_NAME}
|
|
||||||
POSTGRES_INITDB_ARGS: '--data-checksums'
|
|
||||||
# Uncomment the DB_STORAGE_TYPE: 'HDD' var if your database isn't stored on SSDs
|
|
||||||
# DB_STORAGE_TYPE: 'HDD'
|
|
||||||
volumes:
|
|
||||||
# Do not edit the next line. If you want to change the database storage location on your system, edit the value of DB_DATA_LOCATION in the .env file
|
|
||||||
- ${DB_DATA_LOCATION}:/var/lib/postgresql/data
|
|
||||||
shm_size: 128mb
|
|
||||||
restart: always
|
|
||||||
healthcheck:
|
|
||||||
disable: false
|
|
||||||
networks:
|
|
||||||
- net
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
model-cache:
|
|
||||||
|
|
||||||
networks:
|
|
||||||
net:
|
|
||||||
internal: true
|
|
||||||
caddy_default:
|
|
||||||
external: true
|
|
||||||
|
|
@ -1,57 +0,0 @@
|
||||||
# Configurations for hardware-accelerated machine learning
|
|
||||||
|
|
||||||
# If using Unraid or another platform that doesn't allow multiple Compose files,
|
|
||||||
# you can inline the config for a backend by copying its contents
|
|
||||||
# into the immich-machine-learning service in the docker-compose.yml file.
|
|
||||||
|
|
||||||
# See https://docs.immich.app/features/ml-hardware-acceleration for info on usage.
|
|
||||||
|
|
||||||
services:
|
|
||||||
armnn:
|
|
||||||
devices:
|
|
||||||
- /dev/mali0:/dev/mali0
|
|
||||||
volumes:
|
|
||||||
- /lib/firmware/mali_csffw.bin:/lib/firmware/mali_csffw.bin:ro # Mali firmware for your chipset (not always required depending on the driver)
|
|
||||||
- /usr/lib/libmali.so:/usr/lib/libmali.so:ro # Mali driver for your chipset (always required)
|
|
||||||
|
|
||||||
rknn:
|
|
||||||
security_opt:
|
|
||||||
- systempaths=unconfined
|
|
||||||
- apparmor=unconfined
|
|
||||||
devices:
|
|
||||||
- /dev/dri:/dev/dri
|
|
||||||
|
|
||||||
cpu: {}
|
|
||||||
|
|
||||||
cuda:
|
|
||||||
deploy:
|
|
||||||
resources:
|
|
||||||
reservations:
|
|
||||||
devices:
|
|
||||||
- driver: nvidia
|
|
||||||
count: 1
|
|
||||||
capabilities:
|
|
||||||
- gpu
|
|
||||||
|
|
||||||
rocm:
|
|
||||||
group_add:
|
|
||||||
- video
|
|
||||||
devices:
|
|
||||||
- /dev/dri:/dev/dri
|
|
||||||
- /dev/kfd:/dev/kfd
|
|
||||||
|
|
||||||
openvino:
|
|
||||||
device_cgroup_rules:
|
|
||||||
- 'c 189:* rmw'
|
|
||||||
devices:
|
|
||||||
- /dev/dri:/dev/dri
|
|
||||||
volumes:
|
|
||||||
- /dev/bus/usb:/dev/bus/usb
|
|
||||||
|
|
||||||
openvino-wsl:
|
|
||||||
devices:
|
|
||||||
- /dev/dri:/dev/dri
|
|
||||||
- /dev/dxg:/dev/dxg
|
|
||||||
volumes:
|
|
||||||
- /dev/bus/usb:/dev/bus/usb
|
|
||||||
- /usr/lib/wsl:/usr/lib/wsl
|
|
||||||
|
|
@ -1,59 +0,0 @@
|
||||||
# This is a sample docker-compose file that can be used to run Joplin Server
|
|
||||||
# along with a PostgreSQL server.
|
|
||||||
#
|
|
||||||
# Update the following fields in the stanza below:
|
|
||||||
#
|
|
||||||
# POSTGRES_USER
|
|
||||||
# POSTGRES_PASSWORD
|
|
||||||
# APP_BASE_URL
|
|
||||||
#
|
|
||||||
# APP_BASE_URL: This is the base public URL where the service will be running.
|
|
||||||
# - If Joplin Server needs to be accessible over the internet, configure APP_BASE_URL as follows: https://example.com/joplin.
|
|
||||||
# - If Joplin Server does not need to be accessible over the internet, set the APP_BASE_URL to your server's hostname.
|
|
||||||
# For Example: http://[hostname]:22300. The base URL can include the port.
|
|
||||||
# APP_PORT: The local port on which the Docker container will listen.
|
|
||||||
# - This would typically be mapped to port to 443 (TLS) with a reverse proxy.
|
|
||||||
# - If Joplin Server does not need to be accessible over the internet, the port can be mapped to 22300.
|
|
||||||
|
|
||||||
|
|
||||||
services:
|
|
||||||
|
|
||||||
app:
|
|
||||||
image: joplin/server:latest
|
|
||||||
container_name: joplin_app
|
|
||||||
restart: always
|
|
||||||
depends_on:
|
|
||||||
- db
|
|
||||||
ports:
|
|
||||||
- "22300:22300"
|
|
||||||
environment:
|
|
||||||
- APP_PORT=22300
|
|
||||||
- APP_BASE_URL=https://notes.aggtaa.com
|
|
||||||
- DB_CLIENT=pg
|
|
||||||
- POSTGRES_DATABASE=joplin
|
|
||||||
- POSTGRES_USER=joplin
|
|
||||||
- POSTGRES_PASSWORD=VZa8MBSuJHkzcGoowmJH1gzO8fCOU3zY
|
|
||||||
# - POSTGRES_PORT=${POSTGRES_PORT}
|
|
||||||
- POSTGRES_HOST=db
|
|
||||||
networks:
|
|
||||||
- net
|
|
||||||
- caddy_default
|
|
||||||
|
|
||||||
db:
|
|
||||||
image: postgres:16
|
|
||||||
container_name: joplin_db
|
|
||||||
restart: always
|
|
||||||
volumes:
|
|
||||||
- /docker/data/joplin/db:/var/lib/postgresql/data
|
|
||||||
environment:
|
|
||||||
- POSTGRES_DB=joplin
|
|
||||||
- POSTGRES_USER=joplin
|
|
||||||
- POSTGRES_PASSWORD=VZa8MBSuJHkzcGoowmJH1gzO8fCOU3zY
|
|
||||||
networks:
|
|
||||||
- net
|
|
||||||
|
|
||||||
networks:
|
|
||||||
net:
|
|
||||||
internal: true
|
|
||||||
caddy_default:
|
|
||||||
external: true
|
|
||||||
|
|
@ -1,84 +0,0 @@
|
||||||
services:
|
|
||||||
opencloud:
|
|
||||||
image: ${OC_DOCKER_IMAGE:-opencloudeu/opencloud-rolling}:${OC_DOCKER_TAG:-6.0.0}
|
|
||||||
container_name: opencloud
|
|
||||||
restart: always
|
|
||||||
# user: ${OC_CONTAINER_UID_GID:-1000:1000}
|
|
||||||
user: root
|
|
||||||
ports:
|
|
||||||
- 9200:9200
|
|
||||||
environment:
|
|
||||||
# enable services that are not started automatically
|
|
||||||
# OC_ADD_RUN_SERVICES: ""
|
|
||||||
|
|
||||||
OC_URL: https://files.aggtaa.com
|
|
||||||
|
|
||||||
OC_LOG_LEVEL: "debug"
|
|
||||||
OC_LOG_COLOR: "false"
|
|
||||||
OC_LOG_PRETTY: "true"
|
|
||||||
|
|
||||||
# do not use SSL between the reverse proxy and OpenCloud
|
|
||||||
PROXY_TLS: "false"
|
|
||||||
# INSECURE: needed if OpenCloud / reverse proxy is using self generated certificates
|
|
||||||
OC_INSECURE: "true"
|
|
||||||
# basic auth (not recommended, but needed for eg. WebDav clients that do not support OpenID Connect)
|
|
||||||
PROXY_ENABLE_BASIC_AUTH: "false"
|
|
||||||
IDM_CREATE_DEMO_USERS: "false"
|
|
||||||
IDM_ADMIN_PASSWORD: "admin" # initial password
|
|
||||||
# smtp
|
|
||||||
NOTIFICATIONS_SMTP_HOST: "${SMTP_HOST}"
|
|
||||||
NOTIFICATIONS_SMTP_PORT: "${SMTP_PORT}"
|
|
||||||
NOTIFICATIONS_SMTP_SENDER: "${SMTP_SENDER:-OpenCloud Notifications <notifications@cloud.opencloud.test>}"
|
|
||||||
NOTIFICATIONS_SMTP_USERNAME: "${SMTP_USERNAME}"
|
|
||||||
NOTIFICATIONS_SMTP_PASSWORD: "${SMTP_PASSWORD}"
|
|
||||||
NOTIFICATIONS_SMTP_INSECURE: "${SMTP_INSECURE:-false}"
|
|
||||||
NOTIFICATIONS_SMTP_AUTHENTICATION: "${SMTP_AUTHENTICATION}"
|
|
||||||
NOTIFICATIONS_SMTP_ENCRYPTION: "${SMTP_TRANSPORT_ENCRYPTION:-none}"
|
|
||||||
# ?
|
|
||||||
FRONTEND_ARCHIVER_MAX_SIZE: "10000000000"
|
|
||||||
FRONTEND_CHECK_FOR_UPDATES: "true"
|
|
||||||
PROXY_CSP_CONFIG_FILE_LOCATION: /etc/opencloud/csp.yaml
|
|
||||||
# password policy
|
|
||||||
OC_PASSWORD_POLICY_BANNED_PASSWORDS_LIST: /etc/opencloud/banned-password-list.txt
|
|
||||||
OC_SHARING_PUBLIC_SHARE_MUST_HAVE_PASSWORD: "false"
|
|
||||||
OC_SHARING_PUBLIC_WRITEABLE_SHARE_MUST_HAVE_PASSWORD: "true"
|
|
||||||
OC_PASSWORD_POLICY_DISABLED: "false"
|
|
||||||
OC_PASSWORD_POLICY_MIN_CHARACTERS: "8"
|
|
||||||
OC_PASSWORD_POLICY_MIN_LOWERCASE_CHARACTERS: "1"
|
|
||||||
OC_PASSWORD_POLICY_MIN_UPPERCASE_CHARACTERS: "1"
|
|
||||||
OC_PASSWORD_POLICY_MIN_DIGITS: "1"
|
|
||||||
OC_PASSWORD_POLICY_MIN_SPECIAL_CHARACTERS: "0"
|
|
||||||
OC_DEFAULT_LANGUAGE: ru
|
|
||||||
# oidc
|
|
||||||
OC_OIDC_CLIENT_ID: opencloud
|
|
||||||
IDP_DOMAIN: "auth"
|
|
||||||
OC_OIDC_ISSUER: https://auth.aggtaa.com
|
|
||||||
PROXY_OIDC_ACCESS_TOKEN_VERIFY_METHOD: none # disable, as authelia uses plain string tokens, opencloud expects jwt
|
|
||||||
OC_EXCLUDE_RUN_SERVICES: idp # disable internal lico idp, as external authelia is used
|
|
||||||
PROXY_AUTOPROVISION_ACCOUNTS: true # autocreate local accounts on oidc login
|
|
||||||
GRAPH_USERNAME_MATCH: none # does it need this?
|
|
||||||
PROXY_USER_OIDC_CLAIM: preferred_username
|
|
||||||
PROXY_USER_CS3_CLAIM: username
|
|
||||||
# PROXY_ROLE_ASSIGNMENT_DRIVER: default # all new users are of 'user' role
|
|
||||||
PROXY_ROLE_ASSIGNMENT_DRIVER: oidc
|
|
||||||
GRAPH_ASSIGN_DEFAULT_USER_ROLE: false
|
|
||||||
PROXY_ROLE_ASSIGNMENT_OIDC_CLAIM: groups
|
|
||||||
WEB_OIDC_SCOPE: openid profile email groups
|
|
||||||
volumes:
|
|
||||||
- ./config/csp.yaml:/etc/opencloud/csp.yaml:ro
|
|
||||||
- ./config/banned-password-list.txt:/etc/opencloud/banned-password-list.txt:ro
|
|
||||||
- ./config/proxy.yaml:/etc/opencloud/proxy.yaml:ro
|
|
||||||
- /docker/data/opencloud/etc:/etc/opencloud
|
|
||||||
- /docker/data/opencloud/data:/var/lib/opencloud
|
|
||||||
- /docker/data/opencloud/apps:/var/lib/opencloud/web/assets/apps
|
|
||||||
entrypoint:
|
|
||||||
- /bin/sh
|
|
||||||
# run opencloud init to initialize a configuration file with random secrets
|
|
||||||
# it will fail on subsequent runs, because the config file already exists
|
|
||||||
# therefore we ignore the error and then start the opencloud server
|
|
||||||
command: ["-c", "opencloud init || true; opencloud server"]
|
|
||||||
networks:
|
|
||||||
- caddy_default
|
|
||||||
networks:
|
|
||||||
caddy_default:
|
|
||||||
external: true
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
password
|
|
||||||
12345678
|
|
||||||
123
|
|
||||||
OpenCloud
|
|
||||||
OpenCloud-1
|
|
||||||
|
|
@ -1,47 +0,0 @@
|
||||||
directives:
|
|
||||||
child-src:
|
|
||||||
- '''self'''
|
|
||||||
connect-src:
|
|
||||||
- '''self'''
|
|
||||||
- 'blob:'
|
|
||||||
- 'https://auth.aggtaa.com/'
|
|
||||||
# - 'https://${COMPANION_DOMAIN|companion.opencloud.test}${TRAEFIK_PORT_HTTPS}/'
|
|
||||||
# - 'wss://${COMPANION_DOMAIN|companion.opencloud.test}${TRAEFIK_PORT_HTTPS}/'
|
|
||||||
# - 'https://raw.githubusercontent.com/opencloud-eu/awesome-apps/'
|
|
||||||
# - 'https://${IDP_DOMAIN|keycloak.opencloud.test}${TRAEFIK_PORT_HTTPS}/'
|
|
||||||
# - 'https://update.opencloud.eu/'
|
|
||||||
default-src:
|
|
||||||
- '''none'''
|
|
||||||
font-src:
|
|
||||||
- '''self'''
|
|
||||||
frame-ancestors:
|
|
||||||
- '''self'''
|
|
||||||
frame-src:
|
|
||||||
- '''self'''
|
|
||||||
- 'blob:'
|
|
||||||
- 'https://embed.diagrams.net/'
|
|
||||||
# In contrary to bash and docker the default is given after the | character
|
|
||||||
- 'https://${COLLABORA_DOMAIN|collabora.opencloud.test}${TRAEFIK_PORT_HTTPS}/'
|
|
||||||
# This is needed for the external-sites web extension when embedding sites
|
|
||||||
- 'https://docs.opencloud.eu'
|
|
||||||
img-src:
|
|
||||||
- '''self'''
|
|
||||||
- 'data:'
|
|
||||||
- 'blob:'
|
|
||||||
- 'https://raw.githubusercontent.com/opencloud-eu/awesome-apps/'
|
|
||||||
- 'https://tile.openstreetmap.org/'
|
|
||||||
# In contrary to bash and docker the default is given after the | character
|
|
||||||
- 'https://${COLLABORA_DOMAIN|collabora.opencloud.test}${TRAEFIK_PORT_HTTPS}/'
|
|
||||||
manifest-src:
|
|
||||||
- '''self'''
|
|
||||||
media-src:
|
|
||||||
- '''self'''
|
|
||||||
object-src:
|
|
||||||
- '''self'''
|
|
||||||
- 'blob:'
|
|
||||||
script-src:
|
|
||||||
- '''self'''
|
|
||||||
- '''unsafe-inline'''
|
|
||||||
style-src:
|
|
||||||
- '''self'''
|
|
||||||
- '''unsafe-inline'''
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
role_assignment:
|
|
||||||
driver: oidc
|
|
||||||
oidc_role_mapper:
|
|
||||||
role_claim: groups
|
|
||||||
role_mapping:
|
|
||||||
- role_name: admin
|
|
||||||
claim_value: files-admin # authelia group name
|
|
||||||
- role_name: user
|
|
||||||
claim_value: files-user # authelia group name
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
services:
|
services:
|
||||||
radicale:
|
docker-radicale:
|
||||||
image: tomsquest/docker-radicale
|
image: tomsquest/docker-radicale
|
||||||
container_name: radicale
|
container_name: radicale
|
||||||
restart: always
|
restart: always
|
||||||
|
|
@ -10,36 +10,13 @@ services:
|
||||||
- TAKE_FILE_OWNERSHIP=false
|
- TAKE_FILE_OWNERSHIP=false
|
||||||
volumes:
|
volumes:
|
||||||
- /docker/data/radicale/data:/data
|
- /docker/data/radicale/data:/data
|
||||||
- ./config:/config:ro
|
- /docker/data/radicale/config:/config:ro
|
||||||
networks:
|
networks:
|
||||||
- caddy_default
|
- caddy_default
|
||||||
- net
|
# command: "id"
|
||||||
ui:
|
|
||||||
image: ghcr.io/nagimov/agendav-docker:latest
|
|
||||||
container_name: radicale_ui
|
|
||||||
restart: always
|
|
||||||
ports:
|
|
||||||
- 19999:8080
|
|
||||||
user: "root:root"
|
|
||||||
environment:
|
|
||||||
- AGENDAV_SERVER_NAME=127.0.0.1
|
|
||||||
- AGENDAV_TITLE=
|
|
||||||
- AGENDAV_FOOTER=
|
|
||||||
- AGENDAV_CALDAV_SERVER=http://radicale:5232/%u
|
|
||||||
- AGENDAV_CALDAV_PUBLIC_URL=https://c.aggtaa.com
|
|
||||||
- AGENDAV_TIMEZONE=Europe/Moscow
|
|
||||||
- AGENDAV_WEEKSTART=1 # monday
|
|
||||||
- AGENDAV_LANG=en
|
|
||||||
- AGENDAV_LOG_DIR=/tmp/
|
|
||||||
# - AGENDAV_ENVIRONMENT=dev
|
|
||||||
networks:
|
|
||||||
- caddy_default
|
|
||||||
- net
|
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
caddy_default:
|
caddy_default:
|
||||||
external: true
|
external: true
|
||||||
net:
|
|
||||||
internal: true
|
|
||||||
|
|
||||||
# also see https://github.com/iBigQ/radicale-birthday-calendar
|
# also see https://github.com/iBigQ/radicale-birthday-calendar
|
||||||
|
|
@ -1,163 +0,0 @@
|
||||||
# -*- mode: conf -*-
|
|
||||||
# vim:ft=cfg
|
|
||||||
|
|
||||||
# Config file for Radicale - A simple calendar server
|
|
||||||
#
|
|
||||||
# Place it into /etc/radicale/config (global)
|
|
||||||
# or ~/.config/radicale/config (user)
|
|
||||||
#
|
|
||||||
# The current values are the default ones
|
|
||||||
|
|
||||||
|
|
||||||
[server]
|
|
||||||
|
|
||||||
# CalDAV server hostnames separated by a comma
|
|
||||||
# IPv4 syntax: address:port
|
|
||||||
# IPv6 syntax: [address]:port
|
|
||||||
# For example: 0.0.0.0:9999, [::]:9999
|
|
||||||
#hosts = 127.0.0.1:5232
|
|
||||||
hosts = 0.0.0.0:5232
|
|
||||||
|
|
||||||
# Daemon flag
|
|
||||||
#daemon = False
|
|
||||||
|
|
||||||
# File storing the PID in daemon mode
|
|
||||||
#pid =
|
|
||||||
|
|
||||||
# Max parallel connections
|
|
||||||
#max_connections = 20
|
|
||||||
|
|
||||||
# Max size of request body (bytes)
|
|
||||||
#max_content_length = 100000000
|
|
||||||
|
|
||||||
# Socket timeout (seconds)
|
|
||||||
#timeout = 30
|
|
||||||
|
|
||||||
# SSL flag, enable HTTPS protocol
|
|
||||||
ssl = False
|
|
||||||
|
|
||||||
# SSL certificate path
|
|
||||||
# certificate = /etc/ssl/certs/ssl-cert-snakeoil.pem
|
|
||||||
|
|
||||||
# SSL private key
|
|
||||||
# key = /etc/ssl/private/ssl-cert-snakeoil.key
|
|
||||||
|
|
||||||
# CA certificate for validating clients. This can be used to secure
|
|
||||||
# TCP traffic between Radicale and a reverse proxy
|
|
||||||
#certificate_authority =
|
|
||||||
|
|
||||||
# SSL Protocol used. See python's ssl module for available values
|
|
||||||
#protocol = PROTOCOL_TLSv1_2
|
|
||||||
|
|
||||||
# Available ciphers. See python's ssl module for available ciphers
|
|
||||||
#ciphers =
|
|
||||||
|
|
||||||
# Reverse DNS to resolve client address in logs
|
|
||||||
#dns_lookup = True
|
|
||||||
|
|
||||||
# Message displayed in the client when a password is needed
|
|
||||||
#realm = Radicale - Password Required
|
|
||||||
|
|
||||||
|
|
||||||
[encoding]
|
|
||||||
|
|
||||||
# Encoding for responding requests
|
|
||||||
#request = utf-8
|
|
||||||
|
|
||||||
# Encoding for storing local collections
|
|
||||||
#stock = utf-8
|
|
||||||
|
|
||||||
|
|
||||||
[auth]
|
|
||||||
|
|
||||||
# Authentication method
|
|
||||||
# Value: none | htpasswd | remote_user | http_x_remote_user
|
|
||||||
#type = remote_user
|
|
||||||
type = htpasswd
|
|
||||||
|
|
||||||
# Htpasswd filename
|
|
||||||
htpasswd_filename = /config/users
|
|
||||||
|
|
||||||
# Htpasswd encryption method
|
|
||||||
# Value: plain | sha1 | ssha | crypt | bcrypt | md5
|
|
||||||
# Only bcrypt can be considered secure.
|
|
||||||
# bcrypt and md5 require the passlib library to be installed.
|
|
||||||
htpasswd_encryption = bcrypt
|
|
||||||
|
|
||||||
# Incorrect authentication delay (seconds)
|
|
||||||
#delay = 1
|
|
||||||
|
|
||||||
|
|
||||||
[rights]
|
|
||||||
|
|
||||||
# Rights backend
|
|
||||||
# Value: none | authenticated | owner_only | owner_write | from_file
|
|
||||||
type = owner_only
|
|
||||||
|
|
||||||
# File for rights management from_file
|
|
||||||
# file = /config/rights
|
|
||||||
|
|
||||||
|
|
||||||
[storage]
|
|
||||||
|
|
||||||
# Storage backend
|
|
||||||
# Value: multifilesystem
|
|
||||||
type = multifilesystem
|
|
||||||
|
|
||||||
# Folder for storing local collections, created if not present
|
|
||||||
filesystem_folder = /data/collections
|
|
||||||
|
|
||||||
# Lock the storage. Never start multiple instances of Radicale or edit the
|
|
||||||
# storage externally while Radicale is running if disabled.
|
|
||||||
# filesystem_locking = True
|
|
||||||
|
|
||||||
# Sync all changes to disk during requests. (This can impair performance.)
|
|
||||||
# Disabling it increases the risk of data loss, when the system crashes or
|
|
||||||
# power fails!
|
|
||||||
# filesystem_fsync = True
|
|
||||||
|
|
||||||
# Delete sync token that are older (seconds)
|
|
||||||
#max_sync_token_age = 2592000
|
|
||||||
|
|
||||||
# Close the lock file when no more clients are waiting.
|
|
||||||
# This option is not very useful in general, but on Windows files that are
|
|
||||||
# opened cannot be deleted.
|
|
||||||
#filesystem_close_lock_file = False
|
|
||||||
|
|
||||||
# Command that is run after changes to storage
|
|
||||||
# Example: ([ -d .git ] || git init) && ([ -e .gitignore ] || printf '.Radicale.cache\n.Radicale.lock\n.Radicale.tmp-*\n' > .gitignore) && git add -A && (git diff --cached --quiet || git commit -m "Changes by "%(user)s)
|
|
||||||
# hook = (/bin/sh /webhooks/notify.sh "%(user)s" "%(path)s" "%(request)s" || true)
|
|
||||||
|
|
||||||
|
|
||||||
[web]
|
|
||||||
|
|
||||||
# Web interface backend
|
|
||||||
# Value: none | internal
|
|
||||||
#type = internal
|
|
||||||
|
|
||||||
|
|
||||||
[logging]
|
|
||||||
|
|
||||||
# Logging configuration file
|
|
||||||
# If no config is given, simple information is printed on the standard output
|
|
||||||
# For more information about the syntax of the configuration file, see:
|
|
||||||
# http://docs.python.org/library/logging.config.html
|
|
||||||
# config = /config/logging
|
|
||||||
|
|
||||||
# Set the default logging level to debug
|
|
||||||
#debug = False
|
|
||||||
|
|
||||||
# Store all environment variables (including those set in the shell)
|
|
||||||
#full_environment = False
|
|
||||||
|
|
||||||
# Don't include passwords in logs
|
|
||||||
#mask_passwords = True
|
|
||||||
|
|
||||||
|
|
||||||
[headers]
|
|
||||||
|
|
||||||
# Additional HTTP headers
|
|
||||||
Access-Control-Allow-Origin = *
|
|
||||||
Access-Control-Allow-Methods: GET, POST, OPTIONS, PROPFIND, PROPPATCH, REPORT, PUT, MOVE, DELETE, LOCK, UNLOCK
|
|
||||||
Access-Control-Allow-Headers: User-Agent, Authorization, Content-type, Depth, If-match, If-None-Match, Lock-Token, Timeout, Destination, Overwrite, Prefer, X-client, X-Requested-With
|
|
||||||
Access-Control-Expose-Headers: Etag, Preference-Applied
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
# read access to root collection for all users
|
|
||||||
[all-read-root-collection]
|
|
||||||
user = .+
|
|
||||||
collection =
|
|
||||||
permission = r
|
|
||||||
|
|
||||||
# write access to own files for each user
|
|
||||||
[owner-write]
|
|
||||||
user: .+
|
|
||||||
collection: ^%(login)s(/.+)?$
|
|
||||||
permission: rw
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
anton:$2y$05$4e0H5UK4Q5p7.FNiY36NTuCYJWXPp4M5HOTLFL/HUP58EaCyNRwGa
|
|
||||||
jintara:$2y$05$ZXu4ILSNwUKgRh2vQdoB3elQ52/KA3NznMIEX3q5y1reXszN3PW7m
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
|
|
||||||
# USER="jintara"
|
|
||||||
# PATH_="/data/collections/collection-root/jintara/bda0aa8d-8db8-1362-ee05-64d9cf7c8271/560b559b-130a-490e-acf8-1aa4e5ac4752.vcf" # дарья кухно
|
|
||||||
# PATH_="/data/collections/collection-root/jintara/bda0aa8d-8db8-1362-ee05-64d9cf7c8271/8fb3b96b-0b08-4606-95e9-fec553f8dc5e.vcf" # ольга кузина
|
|
||||||
|
|
||||||
USER="anton"
|
|
||||||
#PATH_="/data/collections/collection-root/anton/c9cc103a-bb2f-cde2-0138-25126ce8ab3f/708ac282-53b5-4044-9352-55883fcc6013.vcf" # test birthday
|
|
||||||
PATH_="/data/collections/collection-root/anton/c9cc103a-bb2f-cde2-0138-25126ce8ab3f/3f38ee57-74c8-4c65-99c7-ca6159e8bd7a.vcf" # Александр Кухно
|
|
||||||
|
|
||||||
|
|
||||||
REQUEST="PUT"
|
|
||||||
|
|
||||||
curl -s -X POST http://localhost:19000/hooks/on-change \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-H "X-Secret: RUCVDCATrwtYlCT680FSW4IT1PrvkLB9" \
|
|
||||||
-d "{\"user\":\"$USER\",\"path\":\"$PATH_\",\"request\":\"$REQUEST\"}"
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
- id: on-change
|
|
||||||
execute-command: /usr/local/bin/python3
|
|
||||||
command-working-directory: /etc/webhook/scripts
|
|
||||||
include-command-output-in-response: true
|
|
||||||
pass-arguments-to-command:
|
|
||||||
- source: string
|
|
||||||
name: "/etc/webhook/scripts/on-change.py"
|
|
||||||
- source: payload
|
|
||||||
name: user
|
|
||||||
- source: payload
|
|
||||||
name: path
|
|
||||||
- source: payload
|
|
||||||
name: request
|
|
||||||
trigger-rule:
|
|
||||||
match:
|
|
||||||
type: value
|
|
||||||
value: RUCVDCATrwtYlCT680FSW4IT1PrvkLB9
|
|
||||||
parameter:
|
|
||||||
source: header
|
|
||||||
name: X-Secret
|
|
||||||
- id: rescan
|
|
||||||
execute-command: /usr/local/bin/python3
|
|
||||||
command-working-directory: /etc/webhook/scripts
|
|
||||||
include-command-output-in-response: true
|
|
||||||
pass-arguments-to-command:
|
|
||||||
- source: string
|
|
||||||
name: "/etc/webhook/scripts/rescan.py"
|
|
||||||
- source: url
|
|
||||||
name: user
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
USER="$1"
|
|
||||||
PATH_="$2"
|
|
||||||
REQUEST="$3"
|
|
||||||
|
|
||||||
# Only fire on actual writes
|
|
||||||
case "$REQUEST" in
|
|
||||||
PUT|DELETE) ;;
|
|
||||||
*) exit 0 ;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
curl -s -X POST $WEBHOOKS_URI/on-change \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-H "X-Secret: RUCVDCATrwtYlCT680FSW4IT1PrvkLB9" \
|
|
||||||
-d "{\"user\":\"$USER\",\"path\":\"$PATH_\",\"request\":\"$REQUEST\"}"
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
calendars:
|
|
||||||
anton:
|
|
||||||
base:
|
|
||||||
dir: /data/collections/collection-root/anton
|
|
||||||
dates_target: 7c3bf078-e287-ed95-f9f6-016fc84663df
|
|
||||||
rescan:
|
|
||||||
- path: c9cc103a-bb2f-cde2-0138-25126ce8ab3f
|
|
||||||
jintara:
|
|
||||||
base:
|
|
||||||
dir: /data/collections/collection-root/jintara
|
|
||||||
dates_target: ebbbb557-5ce9-60cf-7a58-f25ace327913
|
|
||||||
rescan:
|
|
||||||
- path: bda0aa8d-8db8-1362-ee05-64d9cf7c8271
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,65 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
from pprint import pprint
|
|
||||||
import vobject
|
|
||||||
|
|
||||||
from .types import Event
|
|
||||||
from .utils import parse_date
|
|
||||||
|
|
||||||
def read_contact_date_field(card, target: list[Event], field_name: str, target_field_name: str):
|
|
||||||
if field_name in card.contents:
|
|
||||||
for idx, component in enumerate(card.contents[field_name]): # datetime.date or string depending on format
|
|
||||||
field_id = f"{field_name}-{idx}"
|
|
||||||
omit_year = component.params.get("X-APPLE-OMIT-YEAR")
|
|
||||||
dt = parse_date(field_id, target_field_name, component.value, omit_year[0] if omit_year else False)
|
|
||||||
target.append(dt)
|
|
||||||
|
|
||||||
def read_contact_abdate_field(card, target: list[Event]):
|
|
||||||
grouped_fields = {}
|
|
||||||
|
|
||||||
field_name = "x-abdate"
|
|
||||||
field_name_2 = "x-ablabel"
|
|
||||||
if field_name in card.contents:
|
|
||||||
for component in card.contents[field_name]: # datetime.date or string depending on format
|
|
||||||
if component.group not in grouped_fields:
|
|
||||||
grouped_fields[component.group] = {}
|
|
||||||
grouped_fields[component.group]["value"] = component.value
|
|
||||||
|
|
||||||
if field_name_2 in card.contents:
|
|
||||||
for component in card.contents[field_name_2]: # datetime.date or string depending on format
|
|
||||||
if component.group not in grouped_fields:
|
|
||||||
grouped_fields[component.group] = {}
|
|
||||||
grouped_fields[component.group]["name"] = component.value
|
|
||||||
|
|
||||||
# if len(grouped_fields):
|
|
||||||
# pprint(card)
|
|
||||||
|
|
||||||
for group in grouped_fields:
|
|
||||||
f = grouped_fields[group]
|
|
||||||
# pprint(group)
|
|
||||||
# pprint(f)
|
|
||||||
if "name" in f and "value" in f:
|
|
||||||
target.append(parse_date(group, f["name"], f["value"]))
|
|
||||||
|
|
||||||
def read_contact(path: str) -> tuple[vobject.vCard, list[Event]]:
|
|
||||||
|
|
||||||
# print("---")
|
|
||||||
|
|
||||||
# with open(path, 'r') as file:
|
|
||||||
# print(file.read())
|
|
||||||
|
|
||||||
# print("---")
|
|
||||||
|
|
||||||
with open(path) as f:
|
|
||||||
card = vobject.readOne(f)
|
|
||||||
|
|
||||||
result = []
|
|
||||||
|
|
||||||
read_contact_date_field(card, result, "bday", "birthday")
|
|
||||||
read_contact_date_field(card, result, "anniversary", "anniversary")
|
|
||||||
read_contact_date_field(card, result, "x-anniversary", "anniversary")
|
|
||||||
# result["x-abdate"] = read_contact_date_field(card, "x-abdate") # need specific parsing. also see X-ABDATE;X-ABLABEL
|
|
||||||
read_contact_date_field(card, result, "x-anniversary", "anniversary")
|
|
||||||
read_contact_date_field(card, result, "x-evolution-anniversary", "anniversary") # also see X-EVOLUTION-SPOUSE
|
|
||||||
read_contact_abdate_field(card, result) # also see X-EVOLUTION-SPOUSE
|
|
||||||
|
|
||||||
return card, result
|
|
||||||
|
|
@ -1,41 +0,0 @@
|
||||||
import vobject
|
|
||||||
|
|
||||||
def get_contact_name(card: vobject.vCard) -> str:
|
|
||||||
"""Extract best available display name from a vCard, never raises."""
|
|
||||||
|
|
||||||
# 1. FN — formatted name, most reliable
|
|
||||||
try:
|
|
||||||
fn = card.contents["fn"][0].value.strip()
|
|
||||||
if fn:
|
|
||||||
return fn
|
|
||||||
except (KeyError, IndexError, AttributeError):
|
|
||||||
pass
|
|
||||||
|
|
||||||
# 2. N — structured name: last, first, middle, prefix, suffix
|
|
||||||
try:
|
|
||||||
n = card.contents["n"][0].value
|
|
||||||
parts = [n.prefix, n.given, n.additional, n.family, n.suffix]
|
|
||||||
name = " ".join(p for p in parts if p and p.strip())
|
|
||||||
if name.strip():
|
|
||||||
return name.strip()
|
|
||||||
except (KeyError, IndexError, AttributeError):
|
|
||||||
pass
|
|
||||||
|
|
||||||
# 3. ORG — organization name as last resort
|
|
||||||
try:
|
|
||||||
org = card.contents["org"][0].value
|
|
||||||
# org can be a list ["Company", "Department"] or a string
|
|
||||||
if isinstance(org, list):
|
|
||||||
org = " / ".join(p for p in org if p and p.strip())
|
|
||||||
if org and org.strip():
|
|
||||||
return org.strip()
|
|
||||||
except (KeyError, IndexError, AttributeError):
|
|
||||||
pass
|
|
||||||
|
|
||||||
# 4. EMAIL
|
|
||||||
try:
|
|
||||||
return card.contents["email"][0].value.strip()
|
|
||||||
except (KeyError, IndexError, AttributeError):
|
|
||||||
pass
|
|
||||||
|
|
||||||
return "Unknown"
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
import vobject
|
|
||||||
from datetime import date, datetime
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
from .types import Event
|
|
||||||
|
|
||||||
def create_event(name: str, base_id: str, eventDate: Event) -> tuple[str, str]:
|
|
||||||
|
|
||||||
year = eventDate.year or datetime.now().year
|
|
||||||
dt = date(year, eventDate.month, eventDate.day)
|
|
||||||
|
|
||||||
id = base_id
|
|
||||||
|
|
||||||
cal = vobject.iCalendar()
|
|
||||||
event = cal.add('vevent')
|
|
||||||
event.add('uid').value = id
|
|
||||||
event.add('summary').value = f"{name}, {eventDate.name}"
|
|
||||||
event.add('description').value = (f"{eventDate.year}-" if eventDate.year is not None else "") + f"{eventDate.month:02}-{eventDate.day:02}"
|
|
||||||
event.add('dtstart').value = dt
|
|
||||||
event.add('rrule').value = 'FREQ=YEARLY'
|
|
||||||
|
|
||||||
return id, cal.serialize()
|
|
||||||
|
|
||||||
def write_calendar_event(prefix: str, event: Event, dir: Path):
|
|
||||||
evtid, ics = create_event(prefix, f"{id}-{event.id}", event)
|
|
||||||
with open(dir / f"{evtid}.ics", 'w') as f:
|
|
||||||
f.write(ics)
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
from typing import NamedTuple, Optional
|
|
||||||
|
|
||||||
class Event(NamedTuple):
|
|
||||||
id: str
|
|
||||||
name: str
|
|
||||||
day: int
|
|
||||||
month: int
|
|
||||||
year: Optional[int] = None
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
import re
|
|
||||||
from datetime import date, datetime
|
|
||||||
|
|
||||||
from .types import Event
|
|
||||||
|
|
||||||
def parse_date(id: str, name: str, input: str, apple_omit_year: str | None = None) -> Event:
|
|
||||||
if isinstance(input, date):
|
|
||||||
return Event(id, name, input.day, input.month, input.year)
|
|
||||||
|
|
||||||
match = re.match(r"^(-|NaN)-(\d{2})-?(\d{2})$", input) # no-year format: --MMDD or --MM-DD. Also NaN-MM-DD spotted
|
|
||||||
if match:
|
|
||||||
return Event(id, name, int(match.group(3)), int(match.group(2)))
|
|
||||||
|
|
||||||
for fmt in ("%Y-%m-%d", "%Y%m%d"):
|
|
||||||
try:
|
|
||||||
dt = datetime.strptime(input, fmt).date()
|
|
||||||
if apple_omit_year == f"{dt.year}":
|
|
||||||
return Event(id, name, dt.day, dt.month)
|
|
||||||
else:
|
|
||||||
return Event(id, name, dt.day, dt.month, dt.year)
|
|
||||||
except ValueError:
|
|
||||||
continue
|
|
||||||
|
|
||||||
return None # cannot parse
|
|
||||||
|
|
@ -1,61 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
from pprint import pprint
|
|
||||||
import sys
|
|
||||||
import yaml
|
|
||||||
from box import Box
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
from contactdates import calendar, addressbook, addressbook_utils
|
|
||||||
|
|
||||||
def load_config():
|
|
||||||
CONFIG_PATH = Path(__file__).parent / "config.yaml"
|
|
||||||
|
|
||||||
with open(CONFIG_PATH) as f:
|
|
||||||
return Box(yaml.safe_load(f))
|
|
||||||
|
|
||||||
def handle_contact_change(action: str, path: str, user: str):
|
|
||||||
config = load_config()
|
|
||||||
|
|
||||||
base_path = Path(config.calendars[user].base.dir)
|
|
||||||
target_calendar = config.calendars[user].dates_target
|
|
||||||
|
|
||||||
dir = base_path / target_calendar
|
|
||||||
|
|
||||||
print(f"Reading contact {path}")
|
|
||||||
|
|
||||||
card, events = addressbook.read_contact(path)
|
|
||||||
name = addressbook_utils.get_contact_name(card)
|
|
||||||
id = card.contents["uid"][0].value.strip()
|
|
||||||
|
|
||||||
|
|
||||||
print(f"Writing contact \"{name}\" {len(events)} events to {dir}")
|
|
||||||
|
|
||||||
for event in events:
|
|
||||||
evtid, evt = calendar.create_event(name, f"{id}-{event.id}", event)
|
|
||||||
print(f"Writing contact \"{name}\" event {evtid}")
|
|
||||||
with open(dir / f"{evtid}.ics", 'w') as f:
|
|
||||||
f.write(evt)
|
|
||||||
|
|
||||||
def handle_change():
|
|
||||||
user = sys.argv[1] if len(sys.argv) > 1 else "?"
|
|
||||||
path = sys.argv[2] if len(sys.argv) > 2 else "?"
|
|
||||||
request = sys.argv[3] if len(sys.argv) > 3 else "?"
|
|
||||||
|
|
||||||
# Distinguish contacts (CardDAV) vs calendar (CalDAV) by path
|
|
||||||
if ".vcf" in path:
|
|
||||||
kind = "CONTACT"
|
|
||||||
elif ".ics" in path:
|
|
||||||
kind = "EVENT"
|
|
||||||
else:
|
|
||||||
kind = "COLLECTION"
|
|
||||||
|
|
||||||
print(f"{request} {user} {kind} {path}")
|
|
||||||
|
|
||||||
if kind == "CONTACT":
|
|
||||||
handle_contact_change(request, path, user)
|
|
||||||
|
|
||||||
def main():
|
|
||||||
handle_change()
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
|
|
@ -1,65 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
from pprint import pprint
|
|
||||||
import sys
|
|
||||||
import yaml
|
|
||||||
import glob
|
|
||||||
from box import Box
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
from contactdates import calendar, addressbook, addressbook_utils
|
|
||||||
|
|
||||||
def load_config():
|
|
||||||
CONFIG_PATH = Path(__file__).parent / "config.yaml"
|
|
||||||
|
|
||||||
with open(CONFIG_PATH) as f:
|
|
||||||
return Box(yaml.safe_load(f))
|
|
||||||
|
|
||||||
def process_contact(file: Path, target_dir: Path, user: str):
|
|
||||||
# print(f"Reading {user} contact {file.name}")
|
|
||||||
|
|
||||||
card, events = addressbook.read_contact(str(file))
|
|
||||||
name = addressbook_utils.get_contact_name(card)
|
|
||||||
id = card.contents["uid"][0].value.strip()
|
|
||||||
|
|
||||||
if len(events):
|
|
||||||
print(f"Reading {user} contact {file.name}")
|
|
||||||
# pprint(card)
|
|
||||||
print(f"Writing contact \"{name}\" {len(events)} events to {target_dir}")
|
|
||||||
|
|
||||||
for file_to_delete in target_dir.glob(f"{id}-*.ics"):
|
|
||||||
print(f"Deleting old contact event {file_to_delete.name}")
|
|
||||||
file_to_delete.unlink()
|
|
||||||
|
|
||||||
for event in events:
|
|
||||||
evtid, evt = calendar.create_event(name, f"{id}-{event.id}", event)
|
|
||||||
print(f"Writing contact \"{name}\" event {evtid}")
|
|
||||||
with open(target_dir / f"{evtid}.ics", 'w') as f:
|
|
||||||
f.write(evt)
|
|
||||||
|
|
||||||
def handle_rescan(user: str):
|
|
||||||
print(f"Rescanning {user} addressbooks")
|
|
||||||
|
|
||||||
config = load_config()
|
|
||||||
|
|
||||||
user_config = config.calendars[user]
|
|
||||||
|
|
||||||
pprint(user_config)
|
|
||||||
|
|
||||||
base_path = Path(config.calendars[user].base.dir)
|
|
||||||
target_calendar = base_path / config.calendars[user].dates_target
|
|
||||||
addressbooks = user_config.rescan
|
|
||||||
|
|
||||||
for ab in addressbooks:
|
|
||||||
dir = base_path / ab.path
|
|
||||||
print(f"Rescanning {user} addressbook {dir}")
|
|
||||||
|
|
||||||
files = dir.glob("*.vcf")
|
|
||||||
for file in files:
|
|
||||||
process_contact(file, target_calendar, user)
|
|
||||||
|
|
||||||
def main():
|
|
||||||
user = sys.argv[1] if len(sys.argv) > 1 else "?"
|
|
||||||
handle_rescan(user)
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
FROM python:3.13-alpine
|
|
||||||
COPY --from=almir/webhook /usr/local/bin/webhook /usr/local/bin/webhook
|
|
||||||
USER root
|
|
||||||
RUN pip install --no-cache-dir pyyaml python-box vobject
|
|
||||||
EXPOSE 9000
|
|
||||||
ENTRYPOINT ["webhook"]
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
# VARIABLE=value #comment
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
services:
|
|
||||||
syncthing:
|
|
||||||
image: syncthing/syncthing
|
|
||||||
container_name: syncthing
|
|
||||||
restart: always
|
|
||||||
hostname: docker1
|
|
||||||
ports:
|
|
||||||
- 8384:8384
|
|
||||||
volumes:
|
|
||||||
- /docker/data/syncthing/data:/var/syncthing
|
|
||||||
healthcheck:
|
|
||||||
test: curl -fkLsS -m 2 127.0.0.1:8384/rest/noauth/health | grep -o --color=never
|
|
||||||
OK || exit 1
|
|
||||||
interval: 1m
|
|
||||||
timeout: 10s
|
|
||||||
retries: 3
|
|
||||||
networks:
|
|
||||||
- caddy_default
|
|
||||||
networks:
|
|
||||||
caddy_default:
|
|
||||||
external: true
|
|
||||||
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
# VARIABLE=value #comment
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
services:
|
|
||||||
tidyquest:
|
|
||||||
image: mellowfox/tidyquest:latest
|
|
||||||
container_name: tidyquest
|
|
||||||
restart: always
|
|
||||||
ports:
|
|
||||||
- 3020:3000
|
|
||||||
environment:
|
|
||||||
- NODE_ENV=production
|
|
||||||
- JWT_SECRET=YMuTbK7kAw1Iv6qVp1465eLxSQ4w6fTQ
|
|
||||||
- TZ=Europe/Moscow
|
|
||||||
volumes:
|
|
||||||
- /docker/data/tidyquest/data:/app/data
|
|
||||||
networks:
|
|
||||||
- caddy_default
|
|
||||||
networks:
|
|
||||||
caddy_default:
|
|
||||||
external: true
|
|
||||||
Loading…
Reference in New Issue