19 Commits

Author SHA1 Message Date
Scott Idem
ef249b1745 Fix Bitbucket auth migration in deploy workflow 2026-06-09 08:32:57 -04:00
Scott Idem
6c6de37419 fix: restrict Dozzle to localhost-only binding
Bind Dozzle to 127.0.0.1 to prevent exposure on external/LAN interfaces.
Previously bound to 0.0.0.0, allowing unauthenticated access to container
logs from any network-reachable host.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-29 14:02:48 -04:00
Scott Idem
47fe502dc1 Minor clean up 2026-04-19 15:24:28 -04:00
Scott Idem
a56213569a docs: expand .env.default comments for API and DB tuning settings
Updated AE_API_GUNICORN_WORKERS default from 2 → 4 based on stress
testing (nearly 2x throughput improvement confirmed). Added detailed
comments to Gunicorn, DB pool, and connection tuning settings explaining
what each parameter does, how they interact, and capacity planning math.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 18:55:28 -04:00
Scott Idem
8d1c27471f feat: expose DB pool_size and max_overflow as env vars
Documents AE_DB_POOL_SIZE and AE_DB_POOL_MAX_OVERFLOW in .env.default
with per-replica connection math comment for capacity planning.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 18:06:19 -04:00
Scott Idem
d1ed06a4c4 fix: resolve logrotate permission issues in maintenance container 2026-04-03 17:10:22 -04:00
Scott Idem
3c6b67b149 chore: unify timezone and implement containerized log rotation 2026-04-03 17:06:34 -04:00
Scott Idem
75fc650ba8 docs(cheatsheet): update multi-stack isolation section with full container name var list
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 19:20:52 -04:00
Scott Idem
c136c2e50c chore(env): clean up .env.default and parameterize container names
- Remove 16 dead variables (OSIT_ENV, AE_API_ENV, AE_APP_ENV, AE_FLASK_APP_SRC,
  AE_DB_ROOT_PASSWORD, OSIT_WEB_HTTPS_PORT, 5x DOCKER_AE_*_EXTRA_HOST,
  CONTAINER_AE_API/APP/MARIADB/PMA)
- Add missing vars: AE_NETWORK_NAME, CONTAINER_DOZZLE, AE_DOZZLE_PORT
- Parameterize hardcoded container names in compose: CONTAINER_MARIADB,
  CONTAINER_PMA, CONTAINER_AE_OPS (all with :-default fallbacks)
- Fix AE_DB_EXTERNAL_PORT default: 3306 → 32768 (avoids host MariaDB conflict)
- Reorganize: AE_APP_GATEWAY_PORT moved next to AE_API_GATEWAY_PORT

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 19:02:46 -04:00
Scott Idem
4f15386d93 docs: update CHEATSHEET and README for new build/deploy commands
Replace AE_APP_BUILD_MODE=staging references and old docker compose
build-ui instructions with current build-docker-* and deploy-remote-*
Makefile targets.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 18:05:43 -04:00
Scott Idem
352cca8a27 fix(compose): update BUILD_MODE fallback from staging to dev
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 17:28:10 -04:00
Scott Idem
dbfa9754d9 chore(deploy): add deploy.sh remote script, update Makefile
- deploy.sh: SSH-triggered deploy for prod and test environments on
  srv-nyx (linode.oneskyit.com). Pulls repos, builds ae_app container
  with correct BUILD_MODE, restarts ae_api.
- Makefile: rename build-ui → build-docker-dev/test/prod to match new
  naming convention; add deploy-remote-test and deploy-remote-prod targets
- .env.default: AE_APP_BUILD_MODE staging → dev (from prior session)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 17:18:52 -04:00
Scott Idem
bb437ce5cb chore(env): add .env.default template and track it in .gitignore
The Docker env project had no committed .env template — new contributors
had to reverse-engineer the required variables from the compose files.

Added .env.default with all required variables, secrets replaced with XXXX,
and comments explaining each section. Notable: AE_API_GUNICORN_TIMEOUT is
documented as 900 (needed for long ffmpeg video jobs like clip_video).

Updated .gitignore to whitelist .env.default via !.env.default.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 14:08:11 -04:00
Scott Idem
bd035f8c17 fix(nginx,gunicorn): raise send_timeout and proxy_send_timeout for long-running endpoints
Nginx was closing the client connection after exactly 60 seconds on requests
like clip_video (ffmpeg, 5-40 min) because send_timeout and proxy_send_timeout
both default to 60s. proxy_read_timeout was already 2100s but the other two
timeouts were still at defaults.

With proxy_buffering off, Nginx holds the write path to the client open as soon
as the upstream connection is established. If the upstream sends no data for 60s
(e.g. ffmpeg processing), Nginx treats the idle write path as stalled and closes
the client connection, logging 499 (Client Closed Request).

Fixed: raise proxy_send_timeout and send_timeout to 2100s to match
proxy_read_timeout in the main location block.

Also raised the Gunicorn default timeout from 30s to 120s in gunicorn_conf.py
as a belt-and-suspenders measure (AE_API_GUNICORN_TIMEOUT env var takes precedence).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 14:05:22 -04:00
Scott Idem
6fd6899879 fix(gunicorn): set control_socket to /dev/shm path
Newer gunicorn (post-23.0.0) added _get_control_socket_path() which
calls os.path.isabs() on the value — crashing when it is None.
Point the socket to /dev/shm (already used for worker_tmp_dir) so it
is writable inside the container and satisfies the new gunicorn code.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 20:24:12 -04:00
Scott Idem
cd208ef25c Clean up of old stuff 2026-03-24 17:07:19 -04:00
Scott Idem
0d81958bfc Cleaning up old legacy files. Sorry, no more Flask. 2026-03-24 15:51:44 -04:00
Scott Idem
8c9d263afb Disable Gunicorn control_socket in FastAPI configs
Gunicorn 25.1.0+ enables a 'control_socket' by default, which creates
a root-owned 'gunicorn.ctl' file in the chdir directory. When this
directory is a volume mount (as in our dev/test setups), it causes
permission errors during Docker build context gathering.

This change explicitly sets 'control_socket = None' to prevent the
creation of this file.
2026-03-24 15:41:54 -04:00
Scott Idem
facf453991 Added .dockerignore to this file to help with a build issue. 2026-03-24 15:10:29 -04:00
37 changed files with 380 additions and 1631 deletions

View File

@@ -20,3 +20,4 @@ backups/
# Ignore miscellaneous
README.md
gunicorn.ctl

201
.env.default Normal file
View File

@@ -0,0 +1,201 @@
# ------------------------------------------------------------------------------
# AETHER FRAMEWORK - DOCKER ENVIRONMENT CONFIGURATION TEMPLATE
# ------------------------------------------------------------------------------
# Copy this file to .env and fill in real values.
# .env is gitignored — never commit the live file.
# ------------------------------------------------------------------------------
# ------------------------------------------------------------------------------
# SYSTEM SETTINGS
# ------------------------------------------------------------------------------
# System timezone for all containers
TZ=US/Eastern
# Logging level for the API and background workers (debug, info, warning, error)
AE_LOG_LVL=debug
# Docker Compose Profiles
# 'database' includes: mariadb, phpmyadmin, ae_ops
# Comment out or leave empty for "app-only" nodes that connect to a remote DB
# COMPOSE_PROFILES=database
# ------------------------------------------------------------------------------
# STACK ISOLATION
# ------------------------------------------------------------------------------
# Unique Docker network name per stack (prevents collisions when running test/prod on same host)
AE_NETWORK_NAME=ae_dev_net
# Internal Docker container names (must be unique per stack)
# Note: ae_api and ae_app are scaled services — Docker does not allow container_name on those.
CONTAINER_WEB=ae_web_dev
CONTAINER_REDIS=ae_redis_dev
CONTAINER_DOZZLE=ae_dozzle_dev
CONTAINER_MARIADB=ae_mariadb_dev
CONTAINER_PMA=ae_pma_dev
CONTAINER_AE_OPS=ae_ops_dev
# ------------------------------------------------------------------------------
# NETWORK & PROXY SETTINGS
# ------------------------------------------------------------------------------
# Local Nginx listener ports on the host system
OSIT_WEB_HTTP_PORT=8888
# Maximum allowed file upload size (Global for Nginx)
OSIT_WEB_MAX_BODY_SIZE=5120M
# Gateway ports for external reverse proxy (Home Server → this node → Docker Nginx)
AE_APP_GATEWAY_PORT=3001
AE_API_GATEWAY_PORT=5060
# Dozzle log viewer port
AE_DOZZLE_PORT=8881
# Nginx Server Names (used in vhost config templates)
DOCKER_AE_API_SERVER_NAME=dev-api.oneskyit.com
DOCKER_AE_APP_SERVER_NAME=dev-example.oneskyit.com
DOCKER_PHPMYADMIN_SERVER_NAME=dev-phpmyadmin.oneskyit.com
DOCKER_OSIT_SERVER_NAME=dev.oneskyit.com
# ------------------------------------------------------------------------------
# DATABASE SETTINGS (MariaDB)
# ------------------------------------------------------------------------------
# To use an EXTERNAL database:
# 1. Set COMPOSE_PROFILES= (empty) above to disable local DB containers.
# 2. Set AE_DB_SERVER to the external IP or hostname.
# 3. Ensure the external DB allows connections from this host's IP.
# DB Hostname (use 'mariadb' for the local container, or a remote IP/FQDN)
AE_DB_SERVER=vpn-db.oneskyit.com
# AE_DB_SERVER=mariadb
AE_DB_PORT=3306
# Port to expose on the host system if running a local MariaDB container
AE_DB_EXTERNAL_PORT=32768
# Database credentials
AE_DB_NAME=aether_dev
AE_DB_USERNAME=aether_dev
AE_DB_PASSWORD=XXXX
# Connection Tuning
# Seconds to wait when establishing a new connection before giving up.
# Lower values fail fast on DB outage rather than hanging requests.
AE_DB_CONNECTION_TIMEOUT=7
# Seconds before a pooled connection is recycled (closed and reopened).
# Prevents "MySQL server has gone away" errors from MariaDB's wait_timeout.
# Must be less than MariaDB's wait_timeout (default 28800s / 8 hours).
# 900s (15 min) is a safe conservative value for active workloads.
AE_DB_POOL_RECYCLE=900
# Connections held open per API replica at idle (the "warm" pool).
# Each replica maintains this many persistent connections to MariaDB.
AE_DB_POOL_SIZE=10
# Additional connections a replica can open beyond AE_DB_POOL_SIZE under burst load.
# These are created on demand and closed when the burst subsides.
# Max connections per replica = AE_DB_POOL_SIZE + AE_DB_POOL_MAX_OVERFLOW.
# Total max DB connections across all replicas = AE_API_REPLICAS × (AE_DB_POOL_SIZE + AE_DB_POOL_MAX_OVERFLOW).
# Example: 3 replicas × (10 + 20) = 90 max connections. MARIADB_MAX_CONNECTIONS must exceed this.
AE_DB_POOL_MAX_OVERFLOW=20
# ------------------------------------------------------------------------------
# REDIS SETTINGS
# ------------------------------------------------------------------------------
# Redis is used for caching, ID resolution, and messaging
AE_REDIS_SERVER=redis
AE_REDIS_PORT=6379
# ------------------------------------------------------------------------------
# API SETTINGS (FastAPI)
# ------------------------------------------------------------------------------
# Number of API container instances (Docker Compose replica scaling).
# Each replica is an independent container with its own Gunicorn process and
# connection pool. Total DB connections = AE_API_REPLICAS × (AE_DB_POOL_SIZE + AE_DB_POOL_MAX_OVERFLOW).
# Increase for horizontal scaling across CPU cores. On a single-node Linode,
# 2-4 replicas is typical; more replicas won't help if the DB is the bottleneck.
AE_API_REPLICAS=3
# --- Gunicorn / Uvicorn Tuning ---
# Internal port Gunicorn listens on inside the container. Nginx proxies to this.
# Each replica uses this same port within its own network namespace.
AE_API_GUNICORN_PORT=5065
# Worker timeout in seconds. A request that takes longer than this causes Gunicorn
# to kill and restart the worker. Default in gunicorn_conf.py is 120s.
# Raise for endpoints that run long ffmpeg operations (clip_video, convert_file, etc.).
# Dev typically uses 900s to accommodate 5-15 min video jobs.
AE_API_GUNICORN_TIMEOUT=900
# Uvicorn worker processes per replica. Each worker handles requests independently
# using async I/O, but SQLAlchemy DB calls are synchronous and block the worker.
# More workers = more parallel DB queries. Recommended: 2-4 per replica.
# Total parallel DB query capacity ≈ AE_API_REPLICAS × AE_API_GUNICORN_WORKERS.
# Stress testing at 4 workers/replica yielded ~2x throughput vs 2 workers (14 req/s vs 7.5 req/s).
# Rule of thumb: (2 × CPU cores) + 1 per replica, but DB throughput caps before CPU becomes the limit.
AE_API_GUNICORN_WORKERS=4
# Threads per Gunicorn worker. Uvicorn workers use async I/O, so threading provides
# minimal benefit here. Leave at 1 unless explicitly benchmarked otherwise.
AE_API_GUNICORN_THREADS=1
# Security & CORS
# JWT_KEY should be a 22+ character secret string. Rotate if compromised.
AE_API_JWT_KEY=XXXX
# Regex for allowed CORS origins. Requests from non-matching origins are blocked.
# Extend the pattern if adding new domains or local dev ports.
AE_API_ORIGINS_REGEX="(https://.*\.oneskyit\.com)|(http://.*\.oneskyit\.com)|(http://.*.localhost)|(http://.*.localhost:5173)"
# ------------------------------------------------------------------------------
# SMTP SETTINGS (Email)
# ------------------------------------------------------------------------------
# Core SMTP configuration for system notifications and user emails
AE_SMTP_SERVER=linode.oneskyit.com
AE_SMTP_PORT=465
AE_SMTP_USERNAME=send_mail
AE_SMTP_PASSWORD=XXXX
# ------------------------------------------------------------------------------
# APP SETTINGS (SvelteKit)
# ------------------------------------------------------------------------------
# Build mode baked into the Docker image at build time (dev, test, prod)
AE_APP_BUILD_MODE=dev
AE_APP_REPLICAS=2
# ------------------------------------------------------------------------------
# SOURCE PATHS (Absolute paths on Host Machine)
# ------------------------------------------------------------------------------
# IMPORTANT: These paths must exist on the machine running Docker.
# They are mounted into containers as volumes for real-time development.
AE_API_SRC=/home/scott/OSIT_dev/aether_api_fastapi
AE_APP_SRC=/home/scott/OSIT_dev/aether_app_sveltekit
# Physical File Storage (Images, Documents, etc.)
# NOTE: Shared between environments to ensure binary availability
HOSTED_FILES_SRC=/home/scott/OSIT/hosted_files
HOSTED_TMP_SRC=/home/scott/OSIT/hosted_tmp
# ------------------------------------------------------------------------------
# SERVICE TUNING & PERFORMANCE
# ------------------------------------------------------------------------------
# phpMyAdmin Host Port
AE_PMA_PORT=8081
# MariaDB Performance (Injected via Docker Compose command flags)
MARIADB_MAX_CONNECTIONS=500
MARIADB_INNODB_BUFFER_POOL_SIZE=512M
MARIADB_QUERY_CACHE_SIZE=32M
MARIADB_TMP_TABLE_SIZE=384M
MARIADB_TABLE_OPEN_CACHE=4000
# ------------------------------------------------------------------------------
# AETHER SHARED CONFIG (DB Driven)
# ------------------------------------------------------------------------------
# Specifies which record from the 'cfg' table to use for shared settings
# (SMTP, API routes, and external service keys)
# common options: 1=Default, 5=Home Dev, 7=Live Test
AE_CFG_ID=5

1
.gitignore vendored
View File

@@ -57,6 +57,7 @@ Thumbs.db
.env
.venv
*.env
!.env.default
env/
venv/
ENV/

View File

@@ -4,7 +4,8 @@
- **Full Rebuild:** `docker compose up -d --build`
- **Rebuild SvelteKit only:** `docker compose up -d --build ae_app`
- **Restart API (pick up Python changes):** `docker compose restart ae_api`
- **Switch Build Mode:** Edit `.env``AE_APP_BUILD_MODE=prod` `docker compose up -d --build ae_app`
- **Rebuild SvelteKit (local):** `make build-docker-dev` / `build-docker-test` / `build-docker-prod`
- **Deploy to remote:** `make deploy-remote-test` / `deploy-remote-prod` (SSH → linode.oneskyit.com)
- **Shut everything down:** `npm run compose:down` (from `aether_app_sveltekit/`)
## 🛠️ Management Links
@@ -26,7 +27,10 @@ To run multiple stacks (`test`, `bak`, `prod`) on one host, you **must** assign
## 🏗️ Multi-Stack Isolation
1. **Network Name:** Set `AE_NETWORK_NAME=ae_test_net` (etc) to prevent Docker network name collisions.
2. **Container Names:** Set `CONTAINER_` variables (e.g., `CONTAINER_WEB=ae_web_test`) to prevent Docker from refusing to start "conflicting" containers.
2. **Container Names:** All service container names are now `.env` variables with `:-default` fallbacks. Set unique values per stack:
- `CONTAINER_WEB`, `CONTAINER_REDIS`, `CONTAINER_DOZZLE`
- `CONTAINER_MARIADB`, `CONTAINER_PMA`, `CONTAINER_AE_OPS` (database profile only)
- Note: `ae_api` and `ae_app` use `scale` — Docker does not allow `container_name` on scaled services.
3. **Internal Shared Net:** All stacks must connect to `aether_shared_net` to reach a shared MariaDB/Redis.
## 💾 Database Operations

View File

@@ -1,7 +1,7 @@
# Aether Platform - Operations Makefile
# Use these shortcuts for faster development and deployment.
.PHONY: up down restart-api build-api build-ui logs ps
.PHONY: up down restart-api build-api build-docker-dev build-docker-test build-docker-prod logs ps deploy-remote-test deploy-remote-prod
# Start the entire stack
up:
@@ -21,10 +21,18 @@ restart-api:
build-api:
docker compose up -d --build ae_api
# REBUILD UI: Standard autonomous build for SvelteKit
build-ui:
# BUILD DOCKER UI: Build the SvelteKit container for the given mode.
# Use 'npm run dev' for active development (Vite HMR, no Docker).
# Use these only when testing the production-like Docker build locally.
build-docker-dev:
docker compose build ae_app && docker compose up -d ae_app
build-docker-test:
docker compose build --build-arg BUILD_MODE=test ae_app && docker compose up -d ae_app
build-docker-prod:
docker compose build --build-arg BUILD_MODE=prod ae_app && docker compose up -d --remove-orphans ae_app
# View combined logs
logs:
docker compose logs -f --tail=100
@@ -32,3 +40,11 @@ logs:
# Check service status
ps:
docker compose ps
# Remote deploy (SSH to linode.oneskyit.com, run deploy.sh)
# Requires key-based SSH and deploy.sh committed + pulled on the server.
deploy-remote-test:
ssh linode.oneskyit.com 'bash /srv/env/test_aether/deploy.sh test'
deploy-remote-prod:
ssh linode.oneskyit.com 'bash /srv/env/prod_aether/deploy.sh prod'

View File

@@ -23,6 +23,7 @@ workstation:3001 workstation:5060
```
**Key Improvements:**
- **Timezone Support:** All containers use the `TZ` variable from `.env` for consistent logging and database timestamps.
- **Scalable Routing:** Nginx uses Regex (`~^(dev|test|bak|sr|prod)?-?...`) to automatically handle any environment prefix without configuration changes.
- **Isolated Stacks:** Each deployment uses a unique `AE_NETWORK_NAME` and `CONTAINER_` prefix to prevent collisions.
- **Shared Services:** Core infrastructure (DB/Redis) resides on the `aether_shared_net` which must be created manually once.
@@ -42,7 +43,7 @@ Create the base directory and clone this environment:
```bash
sudo mkdir -p /srv/env/aether
sudo chown -R $USER:$USER /srv/env/aether
git clone https://bitbucket.org/oneskyit/one-sky-it-container-environment.git /srv/env/aether/container_env
git clone git@bitbucket.org:oneskyit/one-sky-it-container-environment.git /srv/env/aether/container_env
```
### 3. Configure Environment Settings
@@ -78,13 +79,24 @@ docker compose restart ae_api # Restart the FastAPI Backend
```
### Deployment Workflow
The SvelteKit application is built **inside** the container. You can control the build mode (which bakes in the correct `PUBLIC_` variables) via the `.env` file:
- Set `AE_APP_BUILD_MODE=staging` for development/testing.
- Set `AE_APP_BUILD_MODE=prod` for production.
The SvelteKit application is built **inside** the container using `vite build --mode <env>`, which reads the corresponding `.env.<env>` file for `PUBLIC_` variables.
Then run:
From `aether_app_sveltekit/`:
```bash
docker compose up -d --build ae_app
# Build Docker image locally
npm run build:docker:dev # uses .env.dev
npm run build:docker:test # uses .env.test
npm run build:docker:prod # uses .env.prod
# Deploy to remote server (linode.oneskyit.com)
npm run deploy:remote:test
npm run deploy:remote:prod
```
Or via Makefile targets in this directory:
```bash
make build-docker-dev
make deploy-remote-prod
```
---
@@ -108,7 +120,7 @@ These scripts are located in the root directory:
## 📂 Directory Map
* **`conf/`**: Configuration templates for Nginx and Gunicorn. API config now lives in the `aether_api_fastapi` repo as `app/config.py` and reads settings directly from env vars.
* **`logs/`**: Centralized logging for all containers.
* **`logs/`**: Centralized logging for all containers. Automatic rotation is managed by the `ae_ops` service (7-day retention).
* **`srv/`**: Mount points for data and source code (managed via symlinks).
* **`scripts/`**: Internal automation logic.
* **`backups/`**: Storage for MariaDB snapshots.

View File

@@ -1,105 +0,0 @@
# Configuration file for this FastAPI app.
import os
from pydantic import AnyHttpUrl, BaseSettings, EmailStr, HttpUrl, PostgresDsn, validator
from typing import Any, Dict, List, Optional, Union
# ### ### #
class Settings(BaseSettings):
AETHER_CFG = {}
AETHER_CFG['id'] = os.getenv('AE_CFG_ID', None)
# AETHER_CFG['api_id'] = os.getenv('AE_API_CFG_ID', None) # NOT CURRENTLY NEED OR USED
JWT_KEY = 'EHmSXZFKfMEW65E8kxCKmQ' # 22 characters; super secret Aether JWT signing key
# APP_NAME: str = "Aether API (FastAPI)"
# SUPER_EMAIL: EmailStr = 'Aether.Super@oneskyit.com'
# Database Connection
DB = {}
DB['server'] = os.getenv('AE_DB_V5_SERVER', 'mariadb') # 'linode.oneskyit.com' # linode.oneskyit.com, vpn-linode linode.oneskyit.local
DB['port'] = os.getenv('AE_DB_V5_PORT', '3306') # default = 3306
DB['name'] = os.getenv('AE_DB_V5_NAME', None) # 'aether_dev' #onesky_ams_dev
DB['username'] = os.getenv('AE_DB_V5_USERNAME', None) # 'osit_aether' # 'onesky_aether'
DB['password'] = os.getenv('AE_DB_V5_PASSWORD', None) #
SQLALCHEMY_DB_URI = 'mysql://'+DB['username']+':'+DB['password']+'@'+DB['server']+'/'+DB['name']
# Aether API log files paths
LOG_PATH = {}
LOG_PATH['app'] = os.getenv('AE_API_V5_LOG_PATH', 'admin/log/app.log') # 'admin/log/app.log', '../../logs/aether_api.log'
# LOG_PATH['app_warning'] = '/logs/aether_api_warning.log' # 'admin/log/app_warning.log' '../../logs/aether_api_warning.log'
# Redis
REDIS = {}
REDIS['server'] = os.getenv('AE_REDIS_SERVER', 'redis') # 'localhost' 'redis'
REDIS['port'] = os.getenv('AE_REDIS_PORT', '6379') # '6379'
# Send SMTP Email
SMTP = {}
# server
# port
# username
# password
# Server Hosted File Paths
FILES_PATH = {}
# hosted_files_root
# hosted_tmp_root
# CORS Origins
ORIGINS_REGEX = '(https://.*\.oneskyit\.com)|(http://.*\.oneskyit\.com)|(https://.*\.oneskyit\.com:4443)|(http://.*\.oneskyit\.com:8080)|(http://.*\.oneskyit\.com:8181)|(https://.*\.oneskyit\.com:8443)|(http://.*\.oneskyit\.local)|(http://.*\.oneskyit\.local:5000)|(http://.*.localhost)|(http://.*.localhost:5000)|(http://.*.localhost:8181)'
# A reasonable, but fairly open example regular expression for the CORS origins:
# '(https://.*\.oneskyit\.com)|(http://.*\.oneskyit\.com)|(http://.*\.oneskyit\.com:8181)|(https://.*\.oneskyit\.com:8443)|(http://.*\.oneskyit\.local)|(http://.*\.oneskyit\.local:5000)|(http://.*.localhost)|(http://.*.localhost:5000)|(http://.*.localhost:8181)'
ORIGINS = [
'https://oneskyit.com',
# 'http://app-local.oneskyit.com',
'http://192.168.32.20:3000',
'http://192.168.32.20:8080',
'http://localhost',
'http://localhost:3000',
# 'http://localhost:5000',
'http://localhost:7800',
# 'http://localhost:8080',
# 'http://localhost:8888',
# 'http://fastapi.localhost',
'http://svelte.oneskyit.local:5555',
# 'http://connect.localhost:5000', # Using localhost
# 'http://dev-svelte.oneskyit.local:5555',
# 'http://lci.internal:5000', # Using internal; just in case guess before LCI
# 'http://lci.oneskyit.internal:5000', # Using internal; just in case guess before LCI
# 'http://lci.oneskyit.internal', # Using internal; just in case guess before LCI
]
# HTTP Status Dict List
HTTP_STATUS_LI = {}
# HTTP_STATUS_LI[200] = { 'name': 'OK', 'message': 'The request has succeeded.' }
# HTTP_STATUS_LI[400] = { 'name': 'Bad Request', 'message': 'The request could not be understood by the server due to malformed syntax. The client SHOULD NOT repeat the request without modifications.' }
# HTTP_STATUS_LI[401] = { 'name': 'Unauthorized', 'message': 'The server could not verify that you are authorized to access the URL requested. You either supplied the wrong credentials (e.g. a bad password), or your browser does not understand how to supply the credentials required.' }
# HTTP_STATUS_LI[402] = { 'name': '?Request Failed?', 'message': '??The parameters were valid but the request failed.??' }
# HTTP_STATUS_LI[403] = { 'name': 'Forbidden', 'message': 'The server understood the request, but is refusing to fulfill it. Authorization will not help and the request SHOULD NOT be repeated. If the request method was not HEAD and the server wishes to make public why the request has not been fulfilled, it SHOULD describe the reason for the refusal in the entity. If the server does not wish to make this information available to the client, the status code 404 (Not Found) can be used instead.' }
# HTTP_STATUS_LI[404] = { 'name': 'Not Found', 'message': 'The requested resource does not exist.' }
# HTTP_STATUS_LI[409] = { 'name': 'Conflict', 'message': 'The request conflicts with another request (perhaps due to using the same idempotent key).' }
# HTTP_STATUS_LI[429] = { 'name': 'Too Many Requests', 'message': 'Too many requests hit the API too quickly. We recommend an exponential backoff of your requests.' }
# HTTP_STATUS_LI[500] = { 'name': 'Internal Server Error', 'message': 'The server encountered an unexpected condition which prevented it from fulfilling the request.' }
# HTTP_STATUS_LI[501] = { 'name': 'Not Implemented', 'message': 'The server does not support the functionality required to fulfill the request. This is the appropriate response when the server does not recognize the request method and is not capable of supporting it for any resource.' }
# HTTP_STATUS_LI[502] = { 'name': 'Bad Gateway', 'message': 'The server, while acting as a gateway or proxy, received an invalid response from the upstream server it accessed in attempting to fulfill the request.' }
# HTTP_STATUS_LI[503] = { 'name': 'Service Unavailable', 'message': 'The server is currently unable to handle the request due to a temporary overloading or maintenance of the server. The implication is that this is a temporary condition which will be alleviated after some delay. If known, the length of the delay MAY be indicated in a Retry-After header. If no Retry-After is given, the client SHOULD handle the response as it would for a 500 response.' }
# HTTP_STATUS_LI[504] = { 'name': 'Gateway Timeout', 'message': 'The server, while acting as a gateway or proxy, did not receive a timely response from the upstream server specified by the URI (e.g. HTTP, FTP, LDAP) or some other auxiliary server (e.g. DNS) it needed to access in attempting to complete the request.' }
settings = Settings()

View File

@@ -1,43 +0,0 @@
import os
# Gunicorn config variables
loglevel = "debug"
accesslog = "/logs/gunicorn_access.log" # "-" # stdout
errorlog = "/logs/gunicorn_error.log" # "-" # stderr
# "logfile" does not seem to actually do anything
# logfile = "/logs/gunicorn.log" # "-" # stderr
bind = "0.0.0.0:5005"
# bind = "unix:/tmp/gunicorn.sock"
worker_tmp_dir = "/dev/shm"
chdir = "/srv/aether_api"
# home = /path/to/environment
wsgi_app = "app.main:app"
# module = "run_server"
# callable = "app"
# plugins = "python"
# default_proc_name = "app.main:app"
# Setting a long timeout since some FastAPI API requests may take a while
timeout = 2100 # default 30; 1200 is NOT enough; worker process silent then kill and restart
graceful_timeout = 10 # default 30; timeout after restart signal
keepalive = 2 # default 2; setting higher because behind load balancer (nginx)
# Reload does not work correctly with UvicornWorker
# https://github.com/benoitc/gunicorn/issues/2339
# Disable reload if using more than one thread
##### reload = True
# reload_engine = "poll"
worker_class = "uvicorn.workers.UvicornWorker" # default "sync"
# Works are processes, not threads
# workers = 9 # default 1; use 10ish for production; 2 to 4 times the number of cores
# threads = 1 # default 1; only affects Gthread worker type
workers = os.getenv('AE_API_WORKERS', 2)
threads = os.getenv('AE_API_THREADS', 2)
# umask = '007'

View File

@@ -1,63 +0,0 @@
# aioredis # BAD! Not maintained!
anyio
argon2-cffi
argon2-cffi-bindings
asgiref
async-timeout
certifi
cffi
charset-normalizer
click
Deprecated
dnspython
email-validator
et-xmlfile
fastapi
greenlet
gunicorn
h11
html2text
httpcore
httptools
httpx
idna
itsdangerous
Jinja2
MarkupSafe
mysqlclient
numpy
openpyxl
orjson
packaging
pandas
passlib
pdf2image
Pillow
pycparser
pydantic
PyJWT
pyparsing
python-dateutil
python-dotenv
python-multipart
pytz
PyYAML
qrcode
redis[hiredis]
requests
rfc3986
six
sniffio
SQLAlchemy==1.4.47 # 1.4.47 is the newest I am working with
starlette
stripe
typing_extensions
ujson
urllib3
uvicorn
uvloop
watchfiles
watchgod
websockets
wrapt
xlrd

View File

@@ -6,6 +6,9 @@ loglevel = os.getenv('AE_LOG_LVL', 'warning')
accesslog = "-" # stdout
errorlog = "-" # stderr
# Use /dev/shm for the control socket to avoid permission issues on volume mounts
control_socket = "/dev/shm/gunicorn.ctl"
# ... (existing bind/chdir) ...
bind = "0.0.0.0:5005"
worker_tmp_dir = "/dev/shm"
@@ -13,7 +16,7 @@ chdir = "/srv/aether_api"
wsgi_app = "app.main:app"
# Numeric variables must be integers
timeout = int(os.getenv('AE_API_GUNICORN_TIMEOUT', 30))
timeout = int(os.getenv('AE_API_GUNICORN_TIMEOUT', 120))
graceful_timeout = int(os.getenv('AE_API_GUNICORN_GRACEFUL_TIMEOUT', 30))
keepalive = int(os.getenv('AE_API_GUNICORN_KEEPALIVE', 4))

View File

@@ -1,67 +0,0 @@
aiofiles==23.2.1
anyio==3.7.1
argon2-cffi==23.1.0
argon2-cffi-bindings==21.2.0
async-timeout==4.0.3
baize==0.20.8
certifi==2024.2.2
cffi==1.16.0
charset-normalizer==3.3.2
click==8.1.7
Deprecated==1.2.14
dnspython==2.6.1
email_validator==2.1.1
et-xmlfile==1.1.0
fastapi==0.110.2
greenlet==3.0.3
gunicorn==21.2.0
h11==0.14.0
hiredis==2.3.2
html2text==2024.2.26
httpcore==1.0.5
httptools==0.6.1
httpx==0.27.0
idna==3.7
itsdangerous==2.2.0
Jinja2==3.1.3
MarkupSafe==2.1.5
mysqlclient==2.2.4
numpy==1.26.4
openpyxl==3.1.2
orjson==3.10.1
packaging==24.0
pandas==2.2.2
passlib==1.7.4
pdf2image==1.17.0
pillow==10.3.0
pycparser==2.22
pydantic==1.10.15
PyJWT==2.8.0
pyparsing==3.1.2
pypng==0.20220715.0
python-dateutil==2.9.0.post0
python-dotenv==1.0.1
python-multipart==0.0.9
pytz==2024.1
PyYAML==6.0.1
qrcode==7.4.2
redis==5.0.4
requests==2.31.0
rfc3986==2.0.0
six==1.16.0
sniffio==1.3.1
SQLAlchemy==1.4.52
starlette==0.37.2
stripe==9.4.0
typing_extensions==4.11.0
tzdata==2024.1
ujson==5.9.0
urllib3==2.2.1
uvicorn==0.20.0
uvloop==0.19.0
Wand==0.6.13
watchfiles==0.21.0
watchgod==0.8.2
websockets==12.0
wrapt==1.16.0
xlrd==2.0.1

View File

@@ -1,59 +0,0 @@
import os
# Gunicorn config variables
loglevel = os.getenv('AE_LOG_LVL', 'warning')
# accesslog = "/logs/gunicorn_access.log" # "-" # stdout
errorlog = "/logs/gunicorn_error.log" # "-" # stderr
# "logfile" does not seem to actually do anything
# logfile = "/logs/gunicorn.log" # "-" # stderr
bind = "0.0.0.0:5005"
# bind = "unix:/tmp/gunicorn.sock"
worker_tmp_dir = "/dev/shm"
chdir = "/srv/aether_app"
# home = /path/to/environment
wsgi_app = "run_server:app"
# module = "run_server"
# callable = "app"
# plugins = "python"
# default_proc_name = "run_server:app"
# Setting a longer timeout since some Flask app requests may take a while
timeout = os.getenv('AE_APP_GUNICORN_TIMEOUT', 1200) # default 30; worker process silent then kill and restart
graceful_timeout = os.getenv('AE_APP_GUNICORN_GRACEFUL_TIMEOUT', 20)
keepalive = os.getenv('AE_APP_GUNICORN_KEEPALIVE', 300) # default 2; setting higher because behind load balancer (nginx)
# Disable reload if using more than one thread
reload = True
# worker_class = "sync" # default "sync"
# Works are processes, not threads
# workers = 9 # default 1; use 10ish for production; 2 to 4 times the number of cores
# threads = 1 # default 1; only affects Gthread worker type
workers = os.getenv('AE_APP_GUNICORN_WORKERS', 2)
threads = os.getenv('AE_APP_GUNICORN_THREADS', 2)
# umask = '007'
# capture_output = True
# default_proc_name = "run_server:app"
# worker_class = "worker_class"
## From FastAPI for reference
# --bind unix:/home/scott/OSIT_dev/aether_api_fastapi/gunicorn.sock
# --umask 007 app.main:app
# --workers 2
# --worker-class uvicorn.workers.UvicornWorker
# --log-level debug
# --access-logfile admin/log/access.log
# --error-logfile admin/log/error.log
# --log-file admin/log/log.log
# --capture-output
# --keep-alive 5
# --reload

View File

@@ -1,41 +0,0 @@
# Updated manually 2023-09-12 with a lot of trial and error.
# A few are commented out even though they are actually used and required. Other packages already pull them in.
argon2-cffi>=23.1.0 # Must keep
click>=8.1.7 # Must keep???
Deprecated>=1.2.14
# Flask 2.0.3 works; 2.1.3 works except for changes related to send_file and send_directory; 2.2.5 does not yet work
Flask==2.1.3 # Must keep; 2.2.5 seems ok as of 2023-10-19 but needs more testing
Flask-Caching>=2.0.2
Flask-Cors>=4.0.0
Flask-MySQLdb>=1.0.1
Flask-SocketIO>=5.3.6
Flask-SQLAlchemy>=2.5.1
gunicorn>=21.2.0
html2text>=2020.1.16
# itsdangerous>=2.1.2
Jinja2>=3.1.2
# MarkupSafe>=2.1.3
# mysqlclient>=2.2.0
numpy>=1.25.2
pandas>=2.1.0
passlib>=1.7.4
Pillow>=10.0.0
pydantic>=2.3.0
python-dateutil>=2.8.2
python-engineio>=4.3.0
python-socketio>=5.5.0
pytz>=2023.3.post1
qrcode>=7.4.2
redis>=5.0.0
requests==2.28.1 # version 2.31.0 does not work?
# six>=1.16.0
SQLAlchemy>=2.0.20 # Must keep
stripe==5.0.0 # version 6.4.0 exists 2023-09-12
types-pytz>=2023.3.0.1
types-requests>=2.31.0.2
types-urllib3>=1.26.25.14
typing_extensions>=4.7.1
# urllib3>=1.26.16
Werkzeug==2.3.7 # hold off on upgrading to 3.x versions; Werkzeug 2.3.7 works with Flask 2.1.3; 2.3.8 should be out late 2023
xmltodict>=0.13.0 # Must keep

View File

@@ -1,40 +0,0 @@
# Updated manually 2023-09-12 with a lot of trial and error.
# A few are commented out even though they are actually used and required. Other packages already pull them in.
argon2-cffi>=23.1.0 # Must keep
click>=8.1.7 # Must keep???
Deprecated>=1.2.14
Flask==2.0.3 # Must keep
Flask-Caching>=2.0.2
Flask-Cors>=4.0.0
Flask-MySQLdb>=1.0.1
Flask-SocketIO>=5.3.6
Flask-SQLAlchemy>=2.5.1
gunicorn>=21.2.0
html2text>=2020.1.16
# itsdangerous>=2.1.2
Jinja2>=3.1.2
# MarkupSafe>=2.1.3
# mysqlclient>=2.2.0
numpy>=1.25.2
pandas>=2.1.0
passlib>=1.7.4
Pillow>=10.0.0
pydantic>=2.3.0
python-dateutil>=2.8.2
python-engineio>=4.3.0
python-socketio>=5.5.0
pytz>=2023.3.post1
qrcode>=7.4.2
redis>=5.0.0
requests==2.28.1 # version 2.31.0 does not work?
# six>=1.16.0
SQLAlchemy>=2.0.20 # Must keep
stripe==5.0.0 # version 6.4.0 exists 2023-09-12
types-pytz>=2023.3.0.1
types-requests>=2.31.0.2
types-urllib3>=1.26.25.14
typing_extensions>=4.7.1
# urllib3>=1.26.16
Werkzeug>=2.3.7
xmltodict>=0.13.0 # Must keep

View File

@@ -1 +1,2 @@
55 * * * * bash /scripts/backup_internal.sh >> /logs/backup_cron.log 2>&1
0 0 * * * /usr/sbin/logrotate /etc/logrotate.internal.conf

View File

@@ -1,7 +1,7 @@
# Logrotate configuration for Aether Docker Logs
# To use: sudo ln -s /home/scott/OSIT_dev/aether_container_env/conf/logrotate.conf /etc/logrotate.d/aether
# Logrotate configuration for Aether Docker Logs (Internal container version)
/home/scott/OSIT_dev/aether_container_env/logs/*/*.log {
/logs/*/*.log /logs/web/*/*.log {
su aether aether
daily
rotate 7
missingok

View File

@@ -1,151 +0,0 @@
# DO NOT EDIT: created by update.sh from Dockerfile-debian.template
FROM php:8.1-fpm-bullseye
# entrypoint.sh and cron.sh dependencies
RUN set -ex; \
\
apt-get update; \
apt-get install -y --no-install-recommends \
rsync \
bzip2 \
busybox-static \
libldap-common \
libmagickcore-6.q16-6-extra \
; \
rm -rf /var/lib/apt/lists/*; \
\
mkdir -p /var/spool/cron/crontabs; \
echo '*/5 * * * * php -f /var/www/html/cron.php' > /var/spool/cron/crontabs/www-data
# install the PHP extensions we need
# see https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html
ENV PHP_MEMORY_LIMIT 512M
ENV PHP_UPLOAD_LIMIT 512M
RUN set -ex; \
\
savedAptMark="$(apt-mark showmanual)"; \
\
apt-get update; \
apt-get install -y --no-install-recommends \
libcurl4-openssl-dev \
libevent-dev \
libfreetype6-dev \
libicu-dev \
libjpeg-dev \
libldap2-dev \
libmcrypt-dev \
libmemcached-dev \
libpng-dev \
libpq-dev \
libxml2-dev \
libmagickwand-dev \
libzip-dev \
libwebp-dev \
libgmp-dev \
; \
\
debMultiarch="$(dpkg-architecture --query DEB_BUILD_MULTIARCH)"; \
docker-php-ext-configure gd --with-freetype --with-jpeg --with-webp; \
docker-php-ext-configure ldap --with-libdir="lib/$debMultiarch"; \
docker-php-ext-install -j "$(nproc)" \
bcmath \
exif \
gd \
intl \
ldap \
opcache \
pcntl \
pdo_mysql \
pdo_pgsql \
zip \
gmp \
; \
\
# pecl will claim success even if one install fails, so we need to perform each install separately
pecl install APCu-5.1.22; \
pecl install memcached-3.2.0; \
pecl install redis-5.3.7; \
pecl install imagick-3.7.0; \
\
docker-php-ext-enable \
apcu \
memcached \
redis \
imagick \
; \
rm -r /tmp/pear; \
\
# reset apt-mark's "manual" list so that "purge --auto-remove" will remove all build dependencies
apt-mark auto '.*' > /dev/null; \
apt-mark manual $savedAptMark; \
ldd "$(php -r 'echo ini_get("extension_dir");')"/*.so \
| awk '/=>/ { print $3 }' \
| sort -u \
| xargs -r dpkg-query -S \
| cut -d: -f1 \
| sort -u \
| xargs -rt apt-mark manual; \
\
apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \
rm -rf /var/lib/apt/lists/*
# set recommended PHP.ini settings
# see https://docs.nextcloud.com/server/latest/admin_manual/installation/server_tuning.html#enable-php-opcache
RUN { \
echo 'opcache.enable=1'; \
echo 'opcache.interned_strings_buffer=16'; \
echo 'opcache.max_accelerated_files=10000'; \
echo 'opcache.memory_consumption=128'; \
echo 'opcache.save_comments=1'; \
echo 'opcache.revalidate_freq=60'; \
} > "${PHP_INI_DIR}/conf.d/opcache-recommended.ini"; \
\
echo 'apc.enable_cli=1' >> "${PHP_INI_DIR}/conf.d/docker-php-ext-apcu.ini"; \
\
{ \
echo 'memory_limit=${PHP_MEMORY_LIMIT}'; \
echo 'upload_max_filesize=${PHP_UPLOAD_LIMIT}'; \
echo 'post_max_size=${PHP_UPLOAD_LIMIT}'; \
} > "${PHP_INI_DIR}/conf.d/nextcloud.ini"; \
\
mkdir /var/www/data; \
chown -R www-data:root /var/www; \
chmod -R g=u /var/www
VOLUME /var/www/html
ENV NEXTCLOUD_VERSION 25.0.2
RUN set -ex; \
fetchDeps=" \
gnupg \
dirmngr \
"; \
apt-get update; \
apt-get install -y --no-install-recommends $fetchDeps; \
\
curl -fsSL -o nextcloud.tar.bz2 \
"https://download.nextcloud.com/server/releases/nextcloud-${NEXTCLOUD_VERSION}.tar.bz2"; \
curl -fsSL -o nextcloud.tar.bz2.asc \
"https://download.nextcloud.com/server/releases/nextcloud-${NEXTCLOUD_VERSION}.tar.bz2.asc"; \
export GNUPGHOME="$(mktemp -d)"; \
# gpg key from https://nextcloud.com/nextcloud.asc
gpg --batch --keyserver keyserver.ubuntu.com --recv-keys 28806A878AE423A28372792ED75899B9A724937A; \
gpg --batch --verify nextcloud.tar.bz2.asc nextcloud.tar.bz2; \
tar -xjf nextcloud.tar.bz2 -C /usr/src/; \
gpgconf --kill all; \
rm nextcloud.tar.bz2.asc nextcloud.tar.bz2; \
rm -rf "$GNUPGHOME" /usr/src/nextcloud/updater; \
mkdir -p /usr/src/nextcloud/data; \
mkdir -p /usr/src/nextcloud/custom_apps; \
chmod +x /usr/src/nextcloud/occ; \
\
apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false $fetchDeps; \
rm -rf /var/lib/apt/lists/*
COPY *.sh upgrade.exclude /
COPY config/* /usr/src/nextcloud/config/
ENTRYPOINT ["/entrypoint.sh"]
CMD ["php-fpm"]

View File

@@ -1,4 +0,0 @@
<?php
$CONFIG = array (
'memcache.local' => '\OC\Memcache\APCu',
);

View File

@@ -1,15 +0,0 @@
<?php
$CONFIG = array (
'apps_paths' => array (
0 => array (
'path' => OC::$SERVERROOT.'/apps',
'url' => '/apps',
'writable' => false,
),
1 => array (
'path' => OC::$SERVERROOT.'/custom_apps',
'url' => '/custom_apps',
'writable' => true,
),
),
);

View File

@@ -1,41 +0,0 @@
<?php
$autoconfig_enabled = false;
if (getenv('SQLITE_DATABASE')) {
$AUTOCONFIG['dbtype'] = 'sqlite';
$AUTOCONFIG['dbname'] = getenv('SQLITE_DATABASE');
$autoconfig_enabled = true;
} elseif (getenv('MYSQL_DATABASE_FILE') && getenv('MYSQL_USER_FILE') && getenv('MYSQL_PASSWORD_FILE') && getenv('MYSQL_HOST')) {
$AUTOCONFIG['dbtype'] = 'mysql';
$AUTOCONFIG['dbname'] = trim(file_get_contents(getenv('MYSQL_DATABASE_FILE')));
$AUTOCONFIG['dbuser'] = trim(file_get_contents(getenv('MYSQL_USER_FILE')));
$AUTOCONFIG['dbpass'] = trim(file_get_contents(getenv('MYSQL_PASSWORD_FILE')));
$AUTOCONFIG['dbhost'] = getenv('MYSQL_HOST');
$autoconfig_enabled = true;
} elseif (getenv('MYSQL_DATABASE') && getenv('MYSQL_USER') && getenv('MYSQL_PASSWORD') && getenv('MYSQL_HOST')) {
$AUTOCONFIG['dbtype'] = 'mysql';
$AUTOCONFIG['dbname'] = getenv('MYSQL_DATABASE');
$AUTOCONFIG['dbuser'] = getenv('MYSQL_USER');
$AUTOCONFIG['dbpass'] = getenv('MYSQL_PASSWORD');
$AUTOCONFIG['dbhost'] = getenv('MYSQL_HOST');
$autoconfig_enabled = true;
} elseif (getenv('POSTGRES_DB_FILE') && getenv('POSTGRES_USER_FILE') && getenv('POSTGRES_PASSWORD_FILE') && getenv('POSTGRES_HOST')) {
$AUTOCONFIG['dbtype'] = 'pgsql';
$AUTOCONFIG['dbname'] = trim(file_get_contents(getenv('POSTGRES_DB_FILE')));
$AUTOCONFIG['dbuser'] = trim(file_get_contents(getenv('POSTGRES_USER_FILE')));
$AUTOCONFIG['dbpass'] = trim(file_get_contents(getenv('POSTGRES_PASSWORD_FILE')));
$AUTOCONFIG['dbhost'] = getenv('POSTGRES_HOST');
$autoconfig_enabled = true;
} elseif (getenv('POSTGRES_DB') && getenv('POSTGRES_USER') && getenv('POSTGRES_PASSWORD') && getenv('POSTGRES_HOST')) {
$AUTOCONFIG['dbtype'] = 'pgsql';
$AUTOCONFIG['dbname'] = getenv('POSTGRES_DB');
$AUTOCONFIG['dbuser'] = getenv('POSTGRES_USER');
$AUTOCONFIG['dbpass'] = getenv('POSTGRES_PASSWORD');
$AUTOCONFIG['dbhost'] = getenv('POSTGRES_HOST');
$autoconfig_enabled = true;
}
if ($autoconfig_enabled) {
$AUTOCONFIG['directory'] = getenv('NEXTCLOUD_DATA_DIR') ?: '/var/www/html/data';
}

View File

@@ -1,17 +0,0 @@
<?php
if (getenv('REDIS_HOST')) {
$CONFIG = array(
'memcache.distributed' => '\OC\Memcache\Redis',
'memcache.locking' => '\OC\Memcache\Redis',
'redis' => array(
'host' => getenv('REDIS_HOST'),
'password' => (string) getenv('REDIS_HOST_PASSWORD'),
),
);
if (getenv('REDIS_HOST_PORT') !== false) {
$CONFIG['redis']['port'] = (int) getenv('REDIS_HOST_PORT');
} elseif (getenv('REDIS_HOST')[0] != '/') {
$CONFIG['redis']['port'] = 6379;
}
}

View File

@@ -1,30 +0,0 @@
<?php
$overwriteHost = getenv('OVERWRITEHOST');
if ($overwriteHost) {
$CONFIG['overwritehost'] = $overwriteHost;
}
$overwriteProtocol = getenv('OVERWRITEPROTOCOL');
if ($overwriteProtocol) {
$CONFIG['overwriteprotocol'] = $overwriteProtocol;
}
$overwriteCliUrl = getenv('OVERWRITECLIURL');
if ($overwriteCliUrl) {
$CONFIG['overwrite.cli.url'] = $overwriteCliUrl;
}
$overwriteWebRoot = getenv('OVERWRITEWEBROOT');
if ($overwriteWebRoot) {
$CONFIG['overwritewebroot'] = $overwriteWebRoot;
}
$overwriteCondAddr = getenv('OVERWRITECONDADDR');
if ($overwriteCondAddr) {
$CONFIG['overwritecondaddr'] = $overwriteCondAddr;
}
$trustedProxies = getenv('TRUSTED_PROXIES');
if ($trustedProxies) {
$CONFIG['trusted_proxies'] = array_filter(array_map('trim', explode(' ', $trustedProxies)));
}

View File

@@ -1,27 +0,0 @@
<?php
if (getenv('OBJECTSTORE_S3_BUCKET')) {
$use_ssl = getenv('OBJECTSTORE_S3_SSL');
$use_path = getenv('OBJECTSTORE_S3_USEPATH_STYLE');
$use_legacyauth = getenv('OBJECTSTORE_S3_LEGACYAUTH');
$autocreate = getenv('OBJECTSTORE_S3_AUTOCREATE');
$CONFIG = array(
'objectstore' => array(
'class' => '\OC\Files\ObjectStore\S3',
'arguments' => array(
'bucket' => getenv('OBJECTSTORE_S3_BUCKET'),
'key' => getenv('OBJECTSTORE_S3_KEY') ?: '',
'secret' => getenv('OBJECTSTORE_S3_SECRET') ?: '',
'region' => getenv('OBJECTSTORE_S3_REGION') ?: '',
'hostname' => getenv('OBJECTSTORE_S3_HOST') ?: '',
'port' => getenv('OBJECTSTORE_S3_PORT') ?: '',
'objectPrefix' => getenv("OBJECTSTORE_S3_OBJECT_PREFIX") ? getenv("OBJECTSTORE_S3_OBJECT_PREFIX") : "urn:oid:",
'autocreate' => (strtolower($autocreate) === 'false' || $autocreate == false) ? false : true,
'use_ssl' => (strtolower($use_ssl) === 'false' || $use_ssl == false) ? false : true,
// required for some non Amazon S3 implementations
'use_path_style' => $use_path == true && strtolower($use_path) !== 'false',
// required for older protocol versions
'legacy_auth' => $use_legacyauth == true && strtolower($use_legacyauth) !== 'false'
)
)
);
}

View File

@@ -1,22 +0,0 @@
<?php
if (getenv('SMTP_HOST') && getenv('MAIL_FROM_ADDRESS') && getenv('MAIL_DOMAIN')) {
$CONFIG = array (
'mail_smtpmode' => 'smtp',
'mail_smtphost' => getenv('SMTP_HOST'),
'mail_smtpport' => getenv('SMTP_PORT') ?: (getenv('SMTP_SECURE') ? 465 : 25),
'mail_smtpsecure' => getenv('SMTP_SECURE') ?: '',
'mail_smtpauth' => getenv('SMTP_NAME') && (getenv('SMTP_PASSWORD') || (getenv('SMTP_PASSWORD_FILE') && file_exists(getenv('SMTP_PASSWORD_FILE')))),
'mail_smtpauthtype' => getenv('SMTP_AUTHTYPE') ?: 'LOGIN',
'mail_smtpname' => getenv('SMTP_NAME') ?: '',
'mail_from_address' => getenv('MAIL_FROM_ADDRESS'),
'mail_domain' => getenv('MAIL_DOMAIN'),
);
if (getenv('SMTP_PASSWORD_FILE') && file_exists(getenv('SMTP_PASSWORD_FILE'))) {
$CONFIG['mail_smtppassword'] = trim(file_get_contents(getenv('SMTP_PASSWORD_FILE')));
} elseif (getenv('SMTP_PASSWORD')) {
$CONFIG['mail_smtppassword'] = getenv('SMTP_PASSWORD');
} else {
$CONFIG['mail_smtppassword'] = '';
}
}

View File

@@ -1,31 +0,0 @@
<?php
if (getenv('OBJECTSTORE_SWIFT_URL')) {
$autocreate = getenv('OBJECTSTORE_SWIFT_AUTOCREATE');
$CONFIG = array(
'objectstore' => [
'class' => 'OC\\Files\\ObjectStore\\Swift',
'arguments' => [
'autocreate' => $autocreate == true && strtolower($autocreate) !== 'false',
'user' => [
'name' => getenv('OBJECTSTORE_SWIFT_USER_NAME'),
'password' => getenv('OBJECTSTORE_SWIFT_USER_PASSWORD'),
'domain' => [
'name' => (getenv('OBJECTSTORE_SWIFT_USER_DOMAIN')) ?: 'Default',
],
],
'scope' => [
'project' => [
'name' => getenv('OBJECTSTORE_SWIFT_PROJECT_NAME'),
'domain' => [
'name' => (getenv('OBJECTSTORE_SWIFT_PROJECT_DOMAIN')) ?: 'Default',
],
],
],
'serviceName' => (getenv('OBJECTSTORE_SWIFT_SERVICE_NAME')) ?: 'swift',
'region' => getenv('OBJECTSTORE_SWIFT_REGION'),
'url' => getenv('OBJECTSTORE_SWIFT_URL'),
'bucket' => getenv('OBJECTSTORE_SWIFT_CONTAINER_NAME'),
]
]
);
}

View File

@@ -1,4 +0,0 @@
#!/bin/sh
set -eu
exec busybox crond -f -l 0 -L /dev/stdout

View File

@@ -1,250 +0,0 @@
#!/bin/sh
set -eu
# version_greater A B returns whether A > B
version_greater() {
[ "$(printf '%s\n' "$@" | sort -t '.' -n -k1,1 -k2,2 -k3,3 -k4,4 | head -n 1)" != "$1" ]
}
# return true if specified directory is empty
directory_empty() {
[ -z "$(ls -A "$1/")" ]
}
run_as() {
if [ "$(id -u)" = 0 ]; then
su -p "$user" -s /bin/sh -c "$1"
else
sh -c "$1"
fi
}
# usage: file_env VAR [DEFAULT]
# ie: file_env 'XYZ_DB_PASSWORD' 'example'
# (will allow for "$XYZ_DB_PASSWORD_FILE" to fill in the value of
# "$XYZ_DB_PASSWORD" from a file, especially for Docker's secrets feature)
file_env() {
local var="$1"
local fileVar="${var}_FILE"
local def="${2:-}"
local varValue=$(env | grep -E "^${var}=" | sed -E -e "s/^${var}=//")
local fileVarValue=$(env | grep -E "^${fileVar}=" | sed -E -e "s/^${fileVar}=//")
if [ -n "${varValue}" ] && [ -n "${fileVarValue}" ]; then
echo >&2 "error: both $var and $fileVar are set (but are exclusive)"
exit 1
fi
if [ -n "${varValue}" ]; then
export "$var"="${varValue}"
elif [ -n "${fileVarValue}" ]; then
export "$var"="$(cat "${fileVarValue}")"
elif [ -n "${def}" ]; then
export "$var"="$def"
fi
unset "$fileVar"
}
if expr "$1" : "apache" 1>/dev/null; then
if [ -n "${APACHE_DISABLE_REWRITE_IP+x}" ]; then
a2disconf remoteip
fi
fi
if expr "$1" : "apache" 1>/dev/null || [ "$1" = "php-fpm" ] || [ "${NEXTCLOUD_UPDATE:-0}" -eq 1 ]; then
uid="$(id -u)"
gid="$(id -g)"
if [ "$uid" = '0' ]; then
case "$1" in
apache2*)
user="${APACHE_RUN_USER:-http}"
group="${APACHE_RUN_GROUP:-http}"
# strip off any '#' symbol ('#1000' is valid syntax for Apache)
user="${user#'#'}"
group="${group#'#'}"
;;
*) # php-fpm
user='http'
group='http'
;;
esac
else
user="$uid"
group="$gid"
fi
if [ -n "${REDIS_HOST+x}" ]; then
echo "Configuring Redis as session handler"
{
file_env REDIS_HOST_PASSWORD
echo 'session.save_handler = redis'
# check if redis host is an unix socket path
if [ "$(echo "$REDIS_HOST" | cut -c1-1)" = "/" ]; then
if [ -n "${REDIS_HOST_PASSWORD+x}" ]; then
echo "session.save_path = \"unix://${REDIS_HOST}?auth=${REDIS_HOST_PASSWORD}\""
else
echo "session.save_path = \"unix://${REDIS_HOST}\""
fi
# check if redis password has been set
elif [ -n "${REDIS_HOST_PASSWORD+x}" ]; then
echo "session.save_path = \"tcp://${REDIS_HOST}:${REDIS_HOST_PORT:=6379}?auth=${REDIS_HOST_PASSWORD}\""
else
echo "session.save_path = \"tcp://${REDIS_HOST}:${REDIS_HOST_PORT:=6379}\""
fi
echo "redis.session.locking_enabled = 1"
echo "redis.session.lock_retries = -1"
# redis.session.lock_wait_time is specified in microseconds.
# Wait 10ms before retrying the lock rather than the default 2ms.
echo "redis.session.lock_wait_time = 10000"
} > /usr/local/etc/php/conf.d/redis-session.ini
fi
installed_version="0.0.0.0"
if [ -f /var/www/html/version.php ]; then
# shellcheck disable=SC2016
installed_version="$(php -r 'require "/var/www/html/version.php"; echo implode(".", $OC_Version);')"
fi
# shellcheck disable=SC2016
image_version="$(php -r 'require "/usr/src/nextcloud/version.php"; echo implode(".", $OC_Version);')"
if version_greater "$installed_version" "$image_version"; then
echo "Can't start Nextcloud because the version of the data ($installed_version) is higher than the docker image version ($image_version) and downgrading is not supported. Are you sure you have pulled the newest image version?"
exit 1
fi
if version_greater "$image_version" "$installed_version"; then
echo "Initializing nextcloud $image_version ..."
if [ "$installed_version" != "0.0.0.0" ]; then
echo "Upgrading nextcloud from $installed_version ..."
run_as 'php /var/www/html/occ app:list' | sed -n "/Enabled:/,/Disabled:/p" > /tmp/list_before
fi
if [ "$(id -u)" = 0 ]; then
rsync_options="-rlDog --chown $user:$group"
else
rsync_options="-rlD"
fi
# If another process is syncing the html folder, wait for
# it to be done, then escape initalization.
# You need to define the NEXTCLOUD_INIT_LOCK environment variable
lock=/var/www/html/nextcloud-init-sync.lock
count=0
limit=10
if [ -f "$lock" ] && [ -n "${NEXTCLOUD_INIT_LOCK+x}" ]; then
until [ ! -f "$lock" ] || [ "$count" -gt "$limit" ]
do
count=$((count+1))
wait=$((count*10))
echo "Another process is initializing Nextcloud. Waiting $wait seconds..."
sleep $wait
done
if [ "$count" -gt "$limit" ]; then
echo "Timeout while waiting for an ongoing initialization"
exit 1
fi
echo "The other process is done, assuming complete initialization"
else
# Prevent multiple images syncing simultaneously
touch $lock
rsync $rsync_options --delete --exclude-from=/upgrade.exclude /usr/src/nextcloud/ /var/www/html/
for dir in config data custom_apps themes; do
if [ ! -d "/var/www/html/$dir" ] || directory_empty "/var/www/html/$dir"; then
rsync $rsync_options --include "/$dir/" --exclude '/*' /usr/src/nextcloud/ /var/www/html/
fi
done
rsync $rsync_options --include '/version.php' --exclude '/*' /usr/src/nextcloud/ /var/www/html/
# Install
if [ "$installed_version" = "0.0.0.0" ]; then
echo "New nextcloud instance"
file_env NEXTCLOUD_ADMIN_PASSWORD
file_env NEXTCLOUD_ADMIN_USER
if [ -n "${NEXTCLOUD_ADMIN_USER+x}" ] && [ -n "${NEXTCLOUD_ADMIN_PASSWORD+x}" ]; then
# shellcheck disable=SC2016
install_options='-n --admin-user "$NEXTCLOUD_ADMIN_USER" --admin-pass "$NEXTCLOUD_ADMIN_PASSWORD"'
if [ -n "${NEXTCLOUD_DATA_DIR+x}" ]; then
# shellcheck disable=SC2016
install_options=$install_options' --data-dir "$NEXTCLOUD_DATA_DIR"'
fi
file_env MYSQL_DATABASE
file_env MYSQL_PASSWORD
file_env MYSQL_USER
file_env POSTGRES_DB
file_env POSTGRES_PASSWORD
file_env POSTGRES_USER
install=false
if [ -n "${SQLITE_DATABASE+x}" ]; then
echo "Installing with SQLite database"
# shellcheck disable=SC2016
install_options=$install_options' --database-name "$SQLITE_DATABASE"'
install=true
elif [ -n "${MYSQL_DATABASE+x}" ] && [ -n "${MYSQL_USER+x}" ] && [ -n "${MYSQL_PASSWORD+x}" ] && [ -n "${MYSQL_HOST+x}" ]; then
echo "Installing with MySQL database"
# shellcheck disable=SC2016
install_options=$install_options' --database mysql --database-name "$MYSQL_DATABASE" --database-user "$MYSQL_USER" --database-pass "$MYSQL_PASSWORD" --database-host "$MYSQL_HOST"'
install=true
elif [ -n "${POSTGRES_DB+x}" ] && [ -n "${POSTGRES_USER+x}" ] && [ -n "${POSTGRES_PASSWORD+x}" ] && [ -n "${POSTGRES_HOST+x}" ]; then
echo "Installing with PostgreSQL database"
# shellcheck disable=SC2016
install_options=$install_options' --database pgsql --database-name "$POSTGRES_DB" --database-user "$POSTGRES_USER" --database-pass "$POSTGRES_PASSWORD" --database-host "$POSTGRES_HOST"'
install=true
fi
if [ "$install" = true ]; then
echo "Starting nextcloud installation"
max_retries=10
try=0
until run_as "php /var/www/html/occ maintenance:install $install_options" || [ "$try" -gt "$max_retries" ]
do
echo "Retrying install..."
try=$((try+1))
sleep 10s
done
if [ "$try" -gt "$max_retries" ]; then
echo "Installing of nextcloud failed!"
exit 1
fi
if [ -n "${NEXTCLOUD_TRUSTED_DOMAINS+x}" ]; then
echo "Setting trusted domains…"
NC_TRUSTED_DOMAIN_IDX=1
for DOMAIN in $NEXTCLOUD_TRUSTED_DOMAINS ; do
DOMAIN=$(echo "$DOMAIN" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')
run_as "php /var/www/html/occ config:system:set trusted_domains $NC_TRUSTED_DOMAIN_IDX --value=$DOMAIN"
NC_TRUSTED_DOMAIN_IDX=$((NC_TRUSTED_DOMAIN_IDX+1))
done
fi
else
echo "Please run the web-based installer on first connect!"
fi
fi
# Upgrade
else
run_as 'php /var/www/html/occ upgrade'
run_as 'php /var/www/html/occ app:list' | sed -n "/Enabled:/,/Disabled:/p" > /tmp/list_after
echo "The following apps have been disabled:"
diff /tmp/list_before /tmp/list_after | grep '<' | cut -d- -f2 | cut -d: -f1
rm -f /tmp/list_before /tmp/list_after
fi
# Initialization done, reset lock
rm $lock
echo "Initializing finished"
fi
fi
# Update htaccess after init if requested
if [ -n "${NEXTCLOUD_INIT_HTACCESS+x}" ] && [ "$installed_version" != "0.0.0.0" ]; then
run_as 'php /var/www/html/occ maintenance:update:htaccess'
fi
fi
exec "$@"

View File

@@ -1,6 +0,0 @@
/config/
/data/
/custom_apps/
/themes/
/version.php
/nextcloud-init-sync.lock

View File

@@ -1,20 +0,0 @@
server {
listen 80;
listen [::]:80;
server_name mailman2-oneskyit.localhost mailman2.oneskyit.com;
access_log /logs/nginx/access_oneskyit_mailman2.log;
index index.php;
location / {
proxy_pass http://mailman2:80;
proxy_pass_header Content-Type;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade; # allow websockets
proxy_pass_header Connection;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $host;
}
}

View File

@@ -1,196 +0,0 @@
upstream php-handler {
server nextcloud25:9000;
# server unix:/var/run/php/php7.4-fpm.sock;
}
# Set the `immutable` cache control options only for assets with a cache busting `v` argument
map $arg_v $asset_immutable {
"" "";
default "immutable";
}
server {
listen 80;
listen [::]:80;
server_name nextcloud.oneskyit.com;
# Prevent nginx HTTP Server Detection
server_tokens off;
# Enforce HTTPS
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name nextcloud.oneskyit.com;
access_log /logs/nginx/access_oneskyit_nextcloud.log;
# Path to the root of your installation
# root /srv/nextcloud;
root /var/www/html;
# Use Mozilla's guidelines for SSL/TLS settings
# https://mozilla.github.io/server-side-tls/ssl-config-generator/
# ssl_certificate /etc/ssl/nginx/nextcloud.oneskyit.com.crt;
# ssl_certificate_key /etc/ssl/nginx/nextcloud.oneskyit.com.key;
include /etc/nginx/options-ssl-nginx.conf;
ssl_certificate /etc/certs/fullchain.pem;
ssl_certificate_key /etc/certs/privkey.pem;
ssl_dhparam /etc/certs/ssl-dhparams.pem;
# Prevent nginx HTTP Server Detection
server_tokens off;
# HSTS settings
# WARNING: Only add the preload option once you read about
# the consequences in https://hstspreload.org/. This option
# will add the domain to a hardcoded list that is shipped
# in all major browsers and getting removed from this list
# could take several months.
#add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload" always;
# set max upload size and increase upload timeout:
client_max_body_size 512M;
client_body_timeout 300s;
fastcgi_buffers 64 4K;
# Enable gzip but do not remove ETag headers
gzip on;
gzip_vary on;
gzip_comp_level 4;
gzip_min_length 256;
gzip_proxied expired no-cache no-store private no_last_modified no_etag auth;
gzip_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/wasm application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy;
# Pagespeed is not supported by Nextcloud, so if your server is built
# with the `ngx_pagespeed` module, uncomment this line to disable it.
#pagespeed off;
# The settings allows you to optimize the HTTP2 bandwitdth.
# See https://blog.cloudflare.com/delivering-http-2-upload-speed-improvements/
# for tunning hints
client_body_buffer_size 512k;
# HTTP response headers borrowed from Nextcloud `.htaccess`
add_header Referrer-Policy "no-referrer" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Download-Options "noopen" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Permitted-Cross-Domain-Policies "none" always;
add_header X-Robots-Tag "none" always;
add_header X-XSS-Protection "1; mode=block" always;
# Remove X-Powered-By, which is an information leak
fastcgi_hide_header X-Powered-By;
# Specify how to handle directories -- specifying `/index.php$request_uri`
# here as the fallback means that Nginx always exhibits the desired behaviour
# when a client requests a path that corresponds to a directory that exists
# on the server. In particular, if that directory contains an index.php file,
# that file is correctly served; if it doesn't, then the request is passed to
# the front-end controller. This consistent behaviour means that we don't need
# to specify custom rules for certain paths (e.g. images and other assets,
# `/updater`, `/ocm-provider`, `/ocs-provider`), and thus
# `try_files $uri $uri/ /index.php$request_uri`
# always provides the desired behaviour.
index index.php index.html /index.php$request_uri;
# Rule borrowed from `.htaccess` to handle Microsoft DAV clients
location = / {
if ( $http_user_agent ~ ^DavClnt ) {
return 302 /remote.php/webdav/$is_args$args;
}
}
location = /robots.txt {
allow all;
log_not_found off;
access_log off;
}
# Make a regex exception for `/.well-known` so that clients can still
# access it despite the existence of the regex rule
# `location ~ /(\.|autotest|...)` which would otherwise handle requests
# for `/.well-known`.
location ^~ /.well-known {
# The rules in this block are an adaptation of the rules
# in `.htaccess` that concern `/.well-known`.
location = /.well-known/carddav { return 301 /remote.php/dav/; }
location = /.well-known/caldav { return 301 /remote.php/dav/; }
location /.well-known/acme-challenge { try_files $uri $uri/ =404; }
location /.well-known/pki-validation { try_files $uri $uri/ =404; }
# Let Nextcloud's API for `/.well-known` URIs handle all other
# requests by passing them to the front-end controller.
return 301 /index.php$request_uri;
}
# Rules borrowed from `.htaccess` to hide certain paths from clients
location ~ ^/(?:build|tests|config|lib|3rdparty|templates|data)(?:$|/) { return 404; }
location ~ ^/(?:\.|autotest|occ|issue|indie|db_|console) { return 404; }
# Ensure this block, which passes PHP files to the PHP process, is above the blocks
# which handle static assets (as seen below). If this block is not declared first,
# then Nginx will encounter an infinite rewriting loop when it prepends `/index.php`
# to the URI, resulting in a HTTP 500 error response.
location ~ \.php(?:$|/) {
# Required for legacy support
rewrite ^/(?!index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|oc[ms]-provider\/.+|.+\/richdocumentscode\/proxy) /index.php$request_uri;
fastcgi_split_path_info ^(.+?\.php)(/.*)$;
set $path_info $fastcgi_path_info;
try_files $fastcgi_script_name =404;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
# fastcgi_param SCRIPT_FILENAME /var/www/html/$fastcgi_script_name;
fastcgi_param PATH_INFO $path_info;
fastcgi_param HTTPS on;
fastcgi_param modHeadersAvailable true; # Avoid sending the security headers twice
fastcgi_param front_controller_active true; # Enable pretty urls
fastcgi_pass php-handler;
fastcgi_intercept_errors on;
fastcgi_request_buffering off;
fastcgi_max_temp_file_size 0;
}
location ~ \.(?:css|js|svg|gif|png|jpg|ico|wasm|tflite|map)$ {
try_files $uri /index.php$request_uri;
add_header Cache-Control "public, max-age=15778463, $asset_immutable";
access_log off; # Optional: Don't log access to assets
location ~ \.wasm$ {
default_type application/wasm;
}
}
location ~ \.woff2?$ {
try_files $uri /index.php$request_uri;
expires 7d; # Cache-Control policy borrowed from `.htaccess`
access_log off; # Optional: Don't log access to assets
}
# Rule borrowed from `.htaccess`
location /remote {
return 301 /remote.php$request_uri;
}
location / {
try_files $uri $uri/ /index.php$request_uri;
}
}

View File

@@ -1,160 +0,0 @@
server {
listen 80;
listen [::]:80;
server_name ${DOCKER_AE_API_V5_SERVER_NAME} v5-fastapi.localhost api-v5.localhost;
# server_name
# fastapi_gunicorn.localhost
# dev-api.localhost
# dev-api.oneskyit.com
# test-api.oneskyit.com
# ;
access_log /logs/nginx/access_fastapi_gunicorn.log;
error_log /logs/nginx/error_fastapi_gunicorn.log;
client_max_body_size ${OSIT_WEB_MAX_BODY_SIZE}; # 5120M; #4096M or 4G; 5120M or 5G;
location / {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_redirect off;
proxy_buffering off;
# This is needed for long running Python code. Default is 60 seconds
# Increased from 1200 to 1500 on 2022-04-17
# Increased from 1500 to 2000 on 2023-03-15
# Increased proxy read timeout to 2100 and decreased fastcgi options to 35s on 2023-03-16
fastcgi_connect_timeout 4s;
fastcgi_send_timeout 5s;
fastcgi_read_timeout 5s;
# proxy read timeout being too low will cause 504 Gateway Time-out on the client browser
proxy_read_timeout 2100s;
proxy_pass http://v5_fastapi_backend;
}
location /ws {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_http_version 1.1;
proxy_redirect off;
proxy_buffering off;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# proxy_read_timeout 600;
# proxy_headers_hash_max_size 1024;
proxy_pass http://v5_fastapi_backend;
access_log /logs/nginx/access_fastapi_gunicorn_ws.log;
error_log /logs/nginx/error_fastapi_gunicorn_ws.log;
}
}
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name ${DOCKER_AE_API_V5_SERVER_NAME} v5-fastapi.localhost api-v5.localhost;
# server_name
# fastapi_gunicorn.localhost
# dev-api.localhost
# dev-api.oneskyit.com
# test-api.oneskyit.com
# ;
access_log /logs/nginx/access_fastapi_gunicorn.log;
error_log /logs/nginx/error_fastapi_gunicorn.log;
include /etc/nginx/options-ssl-nginx.conf;
ssl_certificate /etc/certs/fullchain.pem;
ssl_certificate_key /etc/certs/privkey.pem;
ssl_dhparam /etc/certs/ssl-dhparams.pem;
# include brotli.conf;
# include gzip.conf;
client_max_body_size ${OSIT_WEB_MAX_BODY_SIZE}; # 5120M; #4096M or 4G; 5120M or 5G;
location / {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_redirect off;
proxy_buffering off;
# This is needed for long running Python code. Default is 60 seconds
# Increased from 1200 to 1500 on 2022-04-17
# Increased from 1500 to 2000 on 2023-03-15
# Increased proxy read timeout to 2100 and decreased fastcgi options to 35s on 2023-03-16
fastcgi_connect_timeout 35s;
fastcgi_send_timeout 35s;
fastcgi_read_timeout 35s;
# proxy read timeout being too low will cause 504 Gateway Time-out on the client browser
proxy_read_timeout 2100s;
proxy_pass http://v5_fastapi_backend;
}
location /ws {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_http_version 1.1;
proxy_redirect off;
proxy_buffering off;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# proxy_read_timeout 600;
# proxy_headers_hash_max_size 1024;
proxy_pass http://v5_fastapi_backend;
access_log /logs/nginx/access_fastapi_gunicorn_ws.log;
error_log /logs/nginx/error_fastapi_gunicorn_ws.log;
}
}
upstream v5_fastapi_backend {
# sticky sessions
ip_hash;
# enable least connections balancing method
# least_conn;
# zone backend 64k; # Use NGINX Plus' shared memory
# server webserver1 weight=1;
# server webserver2 weight=4;
# larger number will recieve more requests
# Example of 20 vs 10: 20 will recieve twice as many requests as 10
server aether_api_v5_gunicorn:5005 weight=20 max_fails=3 fail_timeout=30s;
# server aether_api_gunicorn_bak:5005 weight=10 max_fails=1 fail_timeout=30s;
# maintain up to 20 idle connections to the group of upstream servers
# keepalive 20;
}

View File

@@ -4,7 +4,7 @@ server {
server_name
${DOCKER_AE_APP_SERVER_NAME}
~^(dev|test|bak|sk|sr|prod)?-?(app|demo|connect|aacc|aapor|ascm|bgh|businessgroup|chow|cmsc|idaa|ishlt|lci|ncsd|npa|rli|scott|dgr)\.oneskyit\.com$
~^(dev|test|bak|sk|sr|prod)?-?(app|demo|connect|aacc|aapor|ascm|axonius|bgh|businessgroup|chow|cmsc|idaa|ishlt|lci|ncsd|npa|rli|scott|dgr)\.oneskyit\.com$
app.localhost
demo.localhost
connect.localhost

View File

@@ -50,6 +50,10 @@ server {
# proxy read timeout being too low will cause 504 Gateway Time-out on the client browser
proxy_read_timeout 2100s;
# proxy_send_timeout and send_timeout default to 60s. For long-running endpoints
# (clip_video, ffmpeg operations that take 5-40 min), raise to match proxy_read_timeout.
proxy_send_timeout 2100s;
send_timeout 2100s;
proxy_pass http://fastapi_backend;
}

View File

@@ -1,167 +0,0 @@
server {
listen 80;
listen [::]:80;
# server_name
# ${NGINX_SERVER_NAMES}
# ;
server_name
${DOCKER_AE_APP_SERVER_NAME}
flask_gunicorn.localhost demo.localhost dev.localhost
bak-app.oneskyit.com
bak-connect.oneskyit.com *.bak-connect.oneskyit.com
bak-demo.oneskyit.com *.bak-demo.oneskyit.com
bak-businessgroup.oneskyit.com *.bak-businessgroup.oneskyit.com
bak-ishlt.oneskyit.com *.bak-ishlt.oneskyit.com
dev-app.oneskyit.com
dev-connect.oneskyit.com *.dev-connect.oneskyit.com
dev-demo.oneskyit.com *.dev-demo.oneskyit.com
dev-aacc.oneskyit.com *.dev-aacc.oneskyit.com
dev-aapor.oneskyit.com *.dev-aapor.oneskyit.com
dev-businessgroup.oneskyit.com *.dev-businessgroup.oneskyit.com
dev-chow.oneskyit.com *.dev-chow.oneskyit.com
dev-cmsc.oneskyit.com *.dev-cmsc.oneskyit.com
dev-idaa.oneskyit.com *.dev-idaa.oneskyit.com
dev-ishlt.oneskyit.com *.dev-ishlt.oneskyit.com
dev-lci.oneskyit.com *.dev-lci.oneskyit.com
dev-ncsd.oneskyit.com *.dev-ncsd.oneskyit.com
dev-npa.oneskyit.com *.dev-npa.oneskyit.com
dev-rli.oneskyit.com *.dev-rli.oneskyit.com
sr-app.oneskyit.com
sr-connect.oneskyit.com *.sr-connect.oneskyit.com
sr-demo.oneskyit.com *.sr-demo.oneskyit.com
sr-aacc.oneskyit.com *.sr-aacc.oneskyit.com
sr-aapor.oneskyit.com *.sr-aapor.oneskyit.com
sr-businessgroup.oneskyit.com *.sr-businessgroup.oneskyit.com
sr-cmsc.oneskyit.com *.sr-cmsc.oneskyit.com
sr-lci.oneskyit.com *.sr-lci.oneskyit.com
sr-ncsd.oneskyit.com *.sr-ncsd.oneskyit.com
test-app.oneskyit.com
# test-idaa.oneskyit.com *.test-idaa.oneskyit.com
# test-ishlt.oneskyit.com *.test-ishlt.oneskyit.com
;
access_log /logs/nginx/access_flask_gunicorn.log;
error_log /logs/nginx/error_flask_gunicorn.log;
client_max_body_size ${OSIT_WEB_MAX_BODY_SIZE}; # 5120M; #4096M or 4G; 5120M or 5G;
location / {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_redirect off;
proxy_buffering off;
# This is needed for long running Python code. Default is 60 seconds
# Increased from 1200 to 1500 on 2022-04-17
fastcgi_connect_timeout 1500s;
fastcgi_send_timeout 1500s;
fastcgi_read_timeout 1500s;
proxy_read_timeout 1500s;
proxy_pass http://flask_backend;
}
}
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
# The Docker nginx envsubst with templates does not work for multiple server names.
# server_name
# ${NGINX_SERVER_NAMES}
# ;
server_name
${DOCKER_AE_APP_SERVER_NAME}
flask_gunicorn.localhost demo.localhost dev.localhost
bak-app.oneskyit.com
bak-connect.oneskyit.com *.bak-connect.oneskyit.com
bak-demo.oneskyit.com *.bak-demo.oneskyit.com
bak-businessgroup.oneskyit.com *.bak-businessgroup.oneskyit.com
bak-ishlt.oneskyit.com *.bak-ishlt.oneskyit.com
dev-app.oneskyit.com
dev-connect.oneskyit.com *.dev-connect.oneskyit.com
dev-demo.oneskyit.com *.dev-demo.oneskyit.com
dev-aacc.oneskyit.com *.dev-aacc.oneskyit.com
dev-aapor.oneskyit.com *.dev-aapor.oneskyit.com
dev-businessgroup.oneskyit.com *.dev-businessgroup.oneskyit.com
dev-chow.oneskyit.com *.dev-chow.oneskyit.com
# dev-cmsc.oneskyit.com *.dev-cmsc.oneskyit.com
dev-idaa.oneskyit.com *.dev-idaa.oneskyit.com
dev-ishlt.oneskyit.com *.dev-ishlt.oneskyit.com
dev-lci.oneskyit.com *.dev-lci.oneskyit.com
# dev-ncsd.oneskyit.com *.dev-ncsd.oneskyit.com
dev-npa.oneskyit.com *.dev-npa.oneskyit.com
dev-rli.oneskyit.com *.dev-rli.oneskyit.com
sr-app.oneskyit.com
sr-connect.oneskyit.com *.sr-connect.oneskyit.com
sr-demo.oneskyit.com *.sr-demo.oneskyit.com
sr-aacc.oneskyit.com *.sr-aacc.oneskyit.com
sr-aapor.oneskyit.com *.sr-aapor.oneskyit.com
sr-businessgroup.oneskyit.com *.sr-businessgroup.oneskyit.com
# sr-cmsc.oneskyit.com *.sr-cmsc.oneskyit.com
sr-lci.oneskyit.com *.sr-lci.oneskyit.com
# sr-ncsd.oneskyit.com *.sr-ncsd.oneskyit.com
test-app.oneskyit.com
# test-idaa.oneskyit.com *.test-idaa.oneskyit.com
# test-ishlt.oneskyit.com *.test-ishlt.oneskyit.com
;
access_log /logs/nginx/access_flask_gunicorn.log;
error_log /logs/nginx/error_flask_gunicorn.log;
include /etc/nginx/options-ssl-nginx.conf;
ssl_certificate /etc/certs/fullchain_wild.pem;
ssl_certificate_key /etc/certs/privkey_wild.pem;
ssl_dhparam /etc/certs/ssl-dhparams.pem;
# include brotli.conf;
# include gzip.conf;
client_max_body_size ${OSIT_WEB_MAX_BODY_SIZE}; # 5120M; #4096M or 4G; 5120M or 5G;
location / {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_redirect off;
proxy_buffering off;
# This is needed for long running Python code. Default is 60 seconds
# Increased from 1200 to 1500 on 2022-04-17
fastcgi_connect_timeout 1500s;
fastcgi_send_timeout 1500s;
fastcgi_read_timeout 1500s;
proxy_read_timeout 1500s;
proxy_pass http://flask_backend;
}
}
upstream flask_backend {
ip_hash;
server app-node:3000 weight=20 max_fails=3 fail_timeout=30s;
}

103
deploy.sh Executable file
View File

@@ -0,0 +1,103 @@
#!/bin/bash
# deploy.sh — Remote deploy for Aether Platform
# Run on srv-nyx directly, or triggered via SSH from the workstation.
#
# Usage: ./deploy.sh <prod|test> [app_branch] [api_branch]
# Example: ./deploy.sh prod
# ./deploy.sh test ae_app_3x_llm development
#
# From workstation (npm run deploy:remote:prod / deploy:remote:test):
# ssh linode.oneskyit.com 'bash /srv/env/prod_aether/deploy.sh prod'
# ssh linode.oneskyit.com 'bash /srv/env/test_aether/deploy.sh test'
#
# NOTE: bak_aether shares the same app/api dirs as prod.
# After a prod deploy, restart bak containers manually if running:
# cd /srv/env/bak_aether && docker compose restart ae_app ae_api
set -euo pipefail
ensure_bitbucket_ssh_remote() {
local repo_path=$1
local remote_url
local remote_path
remote_url=$(git -C "$repo_path" remote get-url origin)
if [[ "$remote_url" =~ ^https://([^@/]+@)?bitbucket\.org/(.+)$ ]]; then
remote_path=${BASH_REMATCH[2]}
git -C "$repo_path" remote set-url origin "git@bitbucket.org:${remote_path}"
fi
}
ENV=${1:-}
if [ -z "$ENV" ]; then
echo "Usage: $0 <prod|test> [app_branch] [api_branch]"
exit 1
fi
# --- Environment config ---
# TODO: Update default branches once prod/test branch strategy is finalized.
# Currently both envs pull from the same working branches.
if [ "$ENV" = "prod" ]; then
APP_DIR=/srv/apps/prod_aether_app_sveltekit
API_DIR=/srv/apps/prod_aether_api_fastapi
COMPOSE_DIR=/srv/env/prod_aether
BUILD_MODE=prod
APP_BRANCH=${2:-ae_app_3x_llm}
API_BRANCH=${3:-development}
elif [ "$ENV" = "test" ]; then
APP_DIR=/srv/apps/test_aether_app_sveltekit
API_DIR=/srv/apps/test_aether_api_fastapi
COMPOSE_DIR=/srv/env/test_aether
BUILD_MODE=test
APP_BRANCH=${2:-ae_app_3x_llm}
API_BRANCH=${3:-development}
else
echo "Unknown environment: '$ENV' (expected: prod or test)"
exit 1
fi
echo ""
echo "========================================"
echo " Aether Deploy: $ENV"
echo " App: $APP_DIR [$APP_BRANCH]"
echo " API: $API_DIR [$API_BRANCH]"
echo " Mode: $BUILD_MODE"
echo "========================================"
echo ""
# --- Pull repos ---
echo "[1/4] Pulling container env..."
ensure_bitbucket_ssh_remote "$COMPOSE_DIR"
git -C "$COMPOSE_DIR" pull --ff-only
echo ""
echo "[2/4] Pulling app ($APP_BRANCH)..."
ensure_bitbucket_ssh_remote "$APP_DIR"
git -C "$APP_DIR" pull --ff-only origin "$APP_BRANCH"
echo ""
echo "[3/4] Pulling API ($API_BRANCH)..."
ensure_bitbucket_ssh_remote "$API_DIR"
git -C "$API_DIR" pull --ff-only origin "$API_BRANCH"
# --- Build and deploy ---
echo ""
echo "[4/4] Building and deploying..."
cd "$COMPOSE_DIR"
docker compose build --build-arg BUILD_MODE="$BUILD_MODE" ae_app
docker compose up -d ae_app
docker compose restart ae_api
echo ""
echo "========================================"
echo " Done: $ENV deployed successfully"
echo "========================================"
if [ "$ENV" = "prod" ]; then
echo ""
echo " bak_aether uses the same code dirs — if its containers"
echo " are running, restart them:"
echo " cd /srv/env/bak_aether && docker compose restart ae_app ae_api"
fi
echo ""

View File

@@ -13,9 +13,9 @@ services:
environment:
- PUID=1000
- PGID=1000
- TZ=US/Eastern
- TZ=${TZ}
- NGINX_SERVER_NAMES="flask_gunicorn.localhost demo.localhost dev.localhost dev.oneskyit.com dev-app.oneskyit.com dev-connect.oneskyit.com dev-demo.oneskyit.com dev-aacc.oneskyit.com dev-aapor.oneskyit.com dev-ascm.oneskyit.com dev-businessgroup.oneskyt.com dev-chow.oneskyit.com dev-cmsc.oneskyit.com dev-idaa.oneskyit.com dev-ishlt.oneskyit.com dev-lci.oneskyit.com dev-ncsd.oneskyit.com dev-npa.oneskyit.com dev-rli.oneskyit.com test-app.oneskyit.com test-api.oneskyit.com test-demo.oneskyit.com test-lci.oneskyit.com test-idaa.oneskyit.com scott.oneskyit.com dgr.oneskyit.com"
- NGINX_SERVER_NAMES="demo.localhost dev.localhost dev.oneskyit.com dev-app.oneskyit.com dev-connect.oneskyit.com dev-demo.oneskyit.com dev-aacc.oneskyit.com dev-aapor.oneskyit.com dev-ascm.oneskyit.com dev-businessgroup.oneskyt.com dev-chow.oneskyit.com dev-cmsc.oneskyit.com dev-idaa.oneskyit.com dev-ishlt.oneskyit.com dev-lci.oneskyit.com dev-ncsd.oneskyit.com dev-npa.oneskyit.com dev-rli.oneskyit.com test-app.oneskyit.com test-api.oneskyit.com test-demo.oneskyit.com test-lci.oneskyit.com test-idaa.oneskyit.com scott.oneskyit.com dgr.oneskyit.com"
ports:
- "${OSIT_WEB_HTTP_PORT}:80" # LAN HTTP (local access without SSL)
# - "${OSIT_WEB_HTTPS_PORT}:443" # HTTPS — not needed internally; terminate SSL at home server
@@ -30,7 +30,6 @@ services:
- ./conf/nginx/site.conf:/etc/nginx/conf.d/0_site.conf
- ./conf/nginx/site-enabled_aether_fastapi_gunicorn.conf:/etc/nginx/templates/site-enabled_aether_fastapi_gunicorn.conf.template
- ./conf/nginx/site-enabled_aether_app_svelte_node.conf:/etc/nginx/templates/site-enabled_aether_app_svelte_node.conf.template
# - ./conf/nginx/site-enabled_aether_flask_gunicorn.conf:/etc/nginx/templates/site-enabled_aether_flask_gunicorn.conf.template
- ./conf/certs/oneskyit_wild_fullchain.pem:/etc/certs/fullchain_wild.pem
- ./conf/certs/oneskyit_wild_privkey.pem:/etc/certs/privkey_wild.pem
- ./conf/certs/oneskyit.com_fullchain.pem:/etc/certs/fullchain.pem
@@ -40,7 +39,6 @@ services:
depends_on:
- ae_api
- ae_app
# - aether_app_gunicorn
logging:
driver: "json-file"
options:
@@ -54,6 +52,8 @@ services:
networks:
- default
- shared
environment:
- TZ=${TZ}
command: redis-server --save "" --loglevel warning
logging:
driver: "json-file"
@@ -64,7 +64,7 @@ services:
mariadb:
restart: always
image: mariadb:10.11
container_name: ae_mariadb_dev
container_name: ${CONTAINER_MARIADB:-ae_mariadb_dev}
profiles: ["database"]
networks:
- shared
@@ -82,6 +82,7 @@ services:
MYSQL_DATABASE: ${AE_DB_NAME}
MYSQL_USER: ${AE_DB_USERNAME}
MYSQL_PASSWORD: ${AE_DB_PASSWORD}
TZ: ${TZ}
ports:
- "${AE_DB_EXTERNAL_PORT}:3306"
volumes:
@@ -96,13 +97,14 @@ services:
phpmyadmin:
restart: always
image: phpmyadmin/phpmyadmin
container_name: ae_pma_dev
container_name: ${CONTAINER_PMA:-ae_pma_dev}
profiles: ["database"]
networks:
- shared
environment:
PMA_HOST: mariadb
UPLOAD_LIMIT: 64M
TZ: ${TZ}
ports:
- "${AE_PMA_PORT}:80"
depends_on:
@@ -161,7 +163,7 @@ services:
dockerfile: Dockerfile
target: deploy-node
args:
BUILD_MODE: ${AE_APP_BUILD_MODE:-staging}
BUILD_MODE: ${AE_APP_BUILD_MODE:-dev}
scale: ${AE_APP_REPLICAS:-1}
networks:
- default
@@ -180,10 +182,8 @@ services:
home.oneskyit.com: "71.126.159.102"
static.oneskyit.com: "104.237.143.4"
dev.oneskyit.com: "192.168.32.7"
# volumes:
# # In production, the build happens INSIDE the container.
# # Mounting the host source here would override the internal build.
# # - ${AE_APP_SRC}:/app
volumes:
- ./logs/ae_app:/logs
depends_on:
- ae_api
- redis
@@ -194,49 +194,15 @@ services:
max-size: "10m"
max-file: "3"
# *Legacy* Aether Flask Application served with Gunicorn
# *NOTE:* This legacy frontend using Flask is being replaced by the new one using SvelteKit.
# aether_app_gunicorn:
# # ... (same as before) ...
# restart: always
# container_name: ${CONTAINER_AE_APP}
# build:
# context: ./
# dockerfile: aether_flask_gunicorn.Dockerfile
# env_file:
# - ./.env
# ports:
# - "${AE_APP_GUNICORN_PORT}:5005"
# extra_hosts:
# - "${DOCKER_AE_SERVER_EXTRA_HOST}"
# - "${DOCKER_AE_API_SERVER_EXTRA_HOST}"
# - "${DOCKER_AE_API_BAK_SERVER_EXTRA_HOST}"
# volumes:
# - ./conf/aether_flask_gunicorn_conf.py:/conf/gunicorn_flask_conf.py
# - ./conf/aether_flask_requirements_current.txt:/requirements_current.txt
# - ./conf/aether_app_config.py:/srv/aether_app/flask_config_v2.py
# - ./logs/ae_app:/logs
# - ${AE_APP_SRC}:/srv/aether_app
# - ${HOSTED_FILES_SRC}:/srv/hosted_files
# - ${HOSTED_TMP_SRC}:/srv/hosted_tmp
# - ./tmp/ae_app:/tmp
# depends_on:
# - ae_api
# stdin_open: true
# tty: true
# logging:
# driver: "json-file"
# options:
# max-size: "10m"
# max-file: "3"
dozzle:
container_name: ${CONTAINER_DOZZLE:-ae_dozzle_dev}
image: amir20/dozzle:latest
environment:
- TZ=${TZ}
volumes:
- /var/run/docker.sock:/var/run/docker.sock
ports:
- "${AE_DOZZLE_PORT:-8881}:8080"
- "127.0.0.1:${AE_DOZZLE_PORT:-8881}:8080"
restart: unless-stopped
logging:
driver: "json-file"
@@ -245,8 +211,7 @@ services:
max-file: "3"
ae_ops:
# ... (same as before) ...
container_name: ae_ops_dev
container_name: ${CONTAINER_AE_OPS:-ae_ops_dev}
image: alpine:latest
restart: always
profiles: ["database"]
@@ -260,7 +225,8 @@ services:
- ./scripts:/scripts
- ./logs:/logs
- ./conf/crontab:/etc/crontabs/root
command: sh -c "apk add --no-cache docker-cli bash && crond -f -l 2"
- ./conf/logrotate.conf:/etc/logrotate.conf
command: sh -c "apk add --no-cache docker-cli bash logrotate && adduser -u 1000 -D aether && cp /etc/logrotate.conf /etc/logrotate.internal.conf && chown root:root /etc/logrotate.internal.conf && crond -f -l 2"
depends_on:
- mariadb
logging:

View File

@@ -1,4 +0,0 @@
# Ignore everything in this directory
*
# Except for this file
!.gitignore