www in docker support
This commit is contained in:
parent
539a848e95
commit
c227fce036
2145 changed files with 399596 additions and 58 deletions
54
local-jsp-docker/README.md
Normal file
54
local-jsp-docker/README.md
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
# Local JSP Docker Stack
|
||||
|
||||
This stack boots the `www` Tomcat webapp locally, seeds MySQL from the checked-in `pg` dump, overrides environment-sensitive `PARM` rows, and captures outgoing email into files.
|
||||
|
||||
The default local model seed is `db/pg-model-seed-trimmed-20260421.sql`.
|
||||
|
||||
## Services
|
||||
|
||||
- `tomcat-www`: Tomcat 9 on Java 11 serving a runtime copy of `www`
|
||||
- `mysql`: MySQL 8 with first-boot import of `db/pg-model-seed-trimmed-20260421.sql` by default
|
||||
- `maildump`: local SMTP sink writing `.eml`, `.txt`, and `.html` payloads to disk
|
||||
|
||||
## What Gets Mocked
|
||||
|
||||
- `PARM.DOCBASE` points to `/data/docbase/`
|
||||
- temp, help, and mailer paths are redirected under `/data/docbase/`
|
||||
- SMTP points to the local `maildump` service
|
||||
- common external-storage style paths are mapped to local fixture directories from `test_pkl`
|
||||
|
||||
## Start
|
||||
|
||||
From this folder:
|
||||
|
||||
```powershell
|
||||
docker compose up --build
|
||||
```
|
||||
|
||||
The public site is exposed on `http://localhost:8080`.
|
||||
|
||||
Mail output is written under `local-jsp-docker/runtime/maildump/out`.
|
||||
|
||||
## First Boot
|
||||
|
||||
The first MySQL start imports the file named by `LOCAL_DB_SEED_DUMP`, which defaults to `db/pg-model-seed-trimmed-20260421.sql`.
|
||||
|
||||
After the import finishes, the local override script updates `PARM` with local-safe values.
|
||||
|
||||
The Tomcat runtime also patches the deployed `WEB-INF/web.xml` so the `instance` context-param is `local-model` instead of `main`. That keeps the startup DB updater servlets from mutating the model database during local boot.
|
||||
|
||||
## Restart Workflow
|
||||
|
||||
JSP edits are picked up by restarting `tomcat-www`:
|
||||
|
||||
```powershell
|
||||
docker compose restart tomcat-www
|
||||
```
|
||||
|
||||
The Tomcat container copies `www` into a runtime directory on each start, patches the deployed `WEB-INF/web.xml`, and then runs Tomcat.
|
||||
|
||||
## Notes
|
||||
|
||||
- This stack currently targets the `www` runtime only.
|
||||
- PHP files in `www` are not executed by this stack.
|
||||
- If the app still references production-only paths from DB data, add them to `mysql/init/20-local-overrides.sh` and `tomcat/bootstrap-docbase.sh`.
|
||||
91
local-jsp-docker/docker-compose.yml
Normal file
91
local-jsp-docker/docker-compose.yml
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
services:
|
||||
tomcat-www:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: tomcat/Dockerfile
|
||||
container_name: regalami-local-jsp-tomcat
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
LOCAL_DB_HOST: mysql
|
||||
LOCAL_DB_PORT: 3306
|
||||
LOCAL_DB_NAME: pg
|
||||
LOCAL_DB_USER: root
|
||||
LOCAL_DB_PASSWORD: root
|
||||
FACEAI_HANDOFF_URL: ${FACEAI_HANDOFF_URL:-http://localhost:8081/faceai_handoff.php}
|
||||
FACEAI_DEV_USER_ID: ${FACEAI_DEV_USER_ID:-1}
|
||||
FACEAI_DEV_DISPLAY_NAME: ${FACEAI_DEV_DISPLAY_NAME:-Local Model User}
|
||||
FACEAI_DEV_EMAIL: ${FACEAI_DEV_EMAIL:-local.model.user@example.invalid}
|
||||
FACEAI_DEV_MEMBERSHIP_STATUS: ${FACEAI_DEV_MEMBERSHIP_STATUS:-active}
|
||||
LOCAL_APP_INSTANCE: ${LOCAL_APP_INSTANCE:-local-model}
|
||||
LOCAL_DOCBASE: /data/docbase/
|
||||
FACEAI_FEATURE_ENABLED: ${FACEAI_FEATURE_ENABLED:-1}
|
||||
FACEAI_FRONTEND_URL: ${FACEAI_FRONTEND_URL:-http://localhost:3001}
|
||||
FACEAI_BACKEND_INTERNAL_URL: ${FACEAI_BACKEND_INTERNAL_URL:-http://host.docker.internal:3001}
|
||||
FACEAI_SHARED_SECRET: ${FACEAI_SHARED_SECRET:-change-me}
|
||||
FACEAI_ALLOW_DEV_HANDOFF: ${FACEAI_ALLOW_DEV_HANDOFF:-1}
|
||||
FACEAI_IDENTITY_COOKIE: ${FACEAI_IDENTITY_COOKIE:-rus_faceai_identity}
|
||||
volumes:
|
||||
- ../www:/workspace/www:ro
|
||||
- ../test_pkl:/workspace/test_pkl:ro
|
||||
- ./runtime/docbase:/data/docbase
|
||||
- ./runtime/tomcat-work:/usr/local/tomcat/work
|
||||
- ./runtime/tomcat-logs:/usr/local/tomcat/logs
|
||||
ports:
|
||||
- "8080:8080"
|
||||
depends_on:
|
||||
mysql:
|
||||
condition: service_healthy
|
||||
maildump:
|
||||
condition: service_started
|
||||
|
||||
mysql:
|
||||
image: mysql:8.4
|
||||
container_name: regalami-local-jsp-mysql
|
||||
restart: unless-stopped
|
||||
command:
|
||||
- --max_allowed_packet=1G
|
||||
- --net_read_timeout=600
|
||||
- --net_write_timeout=600
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: root
|
||||
MYSQL_DATABASE: pg
|
||||
MYSQL_ROOT_HOST: '%'
|
||||
LOCAL_DB_SEED_DUMP: ${LOCAL_DB_SEED_DUMP:-pg-model-seed-trimmed-20260421.sql}
|
||||
LOCAL_DOCBASE: /data/docbase/
|
||||
LOCAL_MAIL_SMTP_HOST: maildump
|
||||
LOCAL_MAIL_SMTP_PORT: 1025
|
||||
LOCAL_SOURCE_DIR: /workspace/www/
|
||||
volumes:
|
||||
- mysql-data:/var/lib/mysql
|
||||
- ../db:/seed:ro
|
||||
- ../www:/workspace/www:ro
|
||||
- ./mysql/init:/docker-entrypoint-initdb.d:ro
|
||||
healthcheck:
|
||||
test:
|
||||
- CMD
|
||||
- mysqladmin
|
||||
- ping
|
||||
- -h
|
||||
- 127.0.0.1
|
||||
- -proot
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 30
|
||||
start_period: 60s
|
||||
|
||||
maildump:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: maildump/Dockerfile
|
||||
container_name: regalami-local-jsp-maildump
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
MAILDUMP_OUTPUT_DIR: /out
|
||||
MAILDUMP_SMTP_PORT: 1025
|
||||
volumes:
|
||||
- ./runtime/maildump/out:/out
|
||||
ports:
|
||||
- "8025:8025"
|
||||
|
||||
volumes:
|
||||
mysql-data:
|
||||
11
local-jsp-docker/maildump/Dockerfile
Normal file
11
local-jsp-docker/maildump/Dockerfile
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
FROM python:3.12-slim
|
||||
|
||||
RUN pip install --no-cache-dir aiosmtpd
|
||||
|
||||
COPY maildump/maildump.py /app/maildump.py
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
EXPOSE 1025 8025
|
||||
|
||||
CMD ["python", "/app/maildump.py"]
|
||||
101
local-jsp-docker/maildump/maildump.py
Normal file
101
local-jsp-docker/maildump/maildump.py
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import email
|
||||
import os
|
||||
import re
|
||||
from datetime import datetime, timezone
|
||||
from email import policy
|
||||
from email.message import Message
|
||||
from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer
|
||||
from pathlib import Path
|
||||
from threading import Thread
|
||||
|
||||
from aiosmtpd.controller import Controller
|
||||
|
||||
|
||||
OUTPUT_DIR = Path(os.environ.get("MAILDUMP_OUTPUT_DIR", "/out"))
|
||||
SMTP_PORT = int(os.environ.get("MAILDUMP_SMTP_PORT", "1025"))
|
||||
HTTP_PORT = int(os.environ.get("MAILDUMP_HTTP_PORT", "8025"))
|
||||
|
||||
|
||||
def safe_name(value: str) -> str:
|
||||
cleaned = re.sub(r"[^A-Za-z0-9._-]+", "-", value.strip())
|
||||
return cleaned.strip("-.") or "message"
|
||||
|
||||
|
||||
def extract_part(message: Message, wanted: str) -> str:
|
||||
if message.is_multipart():
|
||||
for part in message.walk():
|
||||
if part.get_content_type() == wanted:
|
||||
payload = part.get_payload(decode=True) or b""
|
||||
charset = part.get_content_charset() or "utf-8"
|
||||
return payload.decode(charset, errors="replace")
|
||||
return ""
|
||||
|
||||
if message.get_content_type() != wanted:
|
||||
return ""
|
||||
|
||||
payload = message.get_payload(decode=True) or b""
|
||||
charset = message.get_content_charset() or "utf-8"
|
||||
return payload.decode(charset, errors="replace")
|
||||
|
||||
|
||||
class MailDumpHandler:
|
||||
async def handle_DATA(self, server, session, envelope):
|
||||
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
parsed = email.message_from_bytes(envelope.content, policy=policy.default)
|
||||
timestamp = datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%S.%fZ")
|
||||
subject = safe_name(parsed.get("subject", "no-subject"))
|
||||
message_root = OUTPUT_DIR / f"{timestamp}-{subject}"
|
||||
message_root.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
(message_root / "message.eml").write_bytes(envelope.content)
|
||||
(message_root / "metadata.txt").write_text(
|
||||
"\n".join(
|
||||
[
|
||||
f"mail_from: {envelope.mail_from}",
|
||||
f"rcpt_tos: {', '.join(envelope.rcpt_tos)}",
|
||||
f"subject: {parsed.get('subject', '')}",
|
||||
f"date: {parsed.get('date', '')}",
|
||||
]
|
||||
)
|
||||
+ "\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
text_body = extract_part(parsed, "text/plain")
|
||||
html_body = extract_part(parsed, "text/html")
|
||||
|
||||
if text_body:
|
||||
(message_root / "body.txt").write_text(text_body, encoding="utf-8")
|
||||
if html_body:
|
||||
(message_root / "body.html").write_text(html_body, encoding="utf-8")
|
||||
|
||||
return "250 OK"
|
||||
|
||||
|
||||
def serve_http() -> None:
|
||||
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
|
||||
os.chdir(OUTPUT_DIR)
|
||||
server = ThreadingHTTPServer(("0.0.0.0", HTTP_PORT), SimpleHTTPRequestHandler)
|
||||
server.serve_forever()
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
http_thread = Thread(target=serve_http, daemon=True)
|
||||
http_thread.start()
|
||||
|
||||
controller = Controller(MailDumpHandler(), hostname="0.0.0.0", port=SMTP_PORT)
|
||||
controller.start()
|
||||
|
||||
try:
|
||||
while True:
|
||||
await asyncio.sleep(3600)
|
||||
finally:
|
||||
controller.stop()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
12
local-jsp-docker/mysql/init/10-import-pg.sh
Normal file
12
local-jsp-docker/mysql/init/10-import-pg.sh
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
seed_dump="${LOCAL_DB_SEED_DUMP:-pg-model-seed-trimmed-20260421.sql}"
|
||||
|
||||
if [ ! -f "/seed/${seed_dump}" ]; then
|
||||
echo "Seed dump not found: /seed/${seed_dump}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Importing seed dump: ${seed_dump}"
|
||||
mysql -uroot -p"${MYSQL_ROOT_PASSWORD}" "${MYSQL_DATABASE}" < "/seed/${seed_dump}"
|
||||
33
local-jsp-docker/mysql/init/20-local-overrides.sh
Normal file
33
local-jsp-docker/mysql/init/20-local-overrides.sh
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
mysql -uroot -p"${MYSQL_ROOT_PASSWORD}" "${MYSQL_DATABASE}" <<SQL
|
||||
INSERT INTO parm (codice, descrizione, testo, numero, tipoParm, flgTipo, flgAdmin, createTmst, lastUpdTmst)
|
||||
VALUES
|
||||
('DOCBASE', 'Local Docker docbase root', '${LOCAL_DOCBASE}', NULL, 'STRING', 0, 1, NOW(), NOW()),
|
||||
('PATH_TMP', 'Local Docker tmp path', '_tmp/', NULL, 'STRING', 0, 1, NOW(), NOW()),
|
||||
('MAIL_MSG_PATH_MAILER', 'Mail template directory', 'mailMessage/', NULL, 'STRING', 0, 1, NOW(), NOW()),
|
||||
('SMTP', 'Local Docker SMTP host', '${LOCAL_MAIL_SMTP_HOST}', NULL, 'STRING', 0, 1, NOW(), NOW()),
|
||||
('SMTP_PORT', 'Local Docker SMTP port', '${LOCAL_MAIL_SMTP_PORT}', ${LOCAL_MAIL_SMTP_PORT}, 'NUMBER', 0, 1, NOW(), NOW()),
|
||||
('SMTP_USE_AUTH', 'Disable SMTP auth locally', '0', 0, 'NUMBER', 0, 1, NOW(), NOW()),
|
||||
('SMTP_STARTTLS', 'Disable STARTTLS locally', '0', 0, 'NUMBER', 0, 1, NOW(), NOW()),
|
||||
('FROM', 'Local Docker sender', 'local-dev@regalamiunsorriso.test', NULL, 'STRING', 0, 1, NOW(), NOW()),
|
||||
('TO_TEST', 'Force all mail to local test inbox', 'mail-capture@regalamiunsorriso.test', NULL, 'STRING', 0, 1, NOW(), NOW()),
|
||||
('BCC', 'Disable default BCC locally', '', NULL, 'STRING', 0, 1, NOW(), NOW()),
|
||||
('CC', 'Disable default CC locally', '', NULL, 'STRING', 0, 1, NOW(), NOW()),
|
||||
('PATH_IMG_ART', 'Local image path', '_img/', NULL, 'STRING', 0, 1, NOW(), NOW()),
|
||||
('LOG_MAIL_ATTACH', 'Local mail attachment log path', '_logs/mail-attach/', NULL, 'STRING', 0, 1, NOW(), NOW()),
|
||||
('DAILY_CRONTAB_MAIN_LOG_FILE', 'Local crontab log file', '${LOCAL_DOCBASE}_logs/daily-crontab.log', NULL, 'STRING', 0, 1, NOW(), NOW()),
|
||||
('HELP_ATTACH_PATH', 'Local help attachment path', '_help/attach/', NULL, 'STRING', 0, 1, NOW(), NOW()),
|
||||
('HELP_EXPORT_PATH', 'Local help export path', '_help/export/', NULL, 'STRING', 0, 1, NOW(), NOW()),
|
||||
('HELP_IMG_PATH', 'Local help image path', '_help/img/', NULL, 'STRING', 0, 1, NOW(), NOW()),
|
||||
('SOURCE_DIR', 'Workspace source path', '${LOCAL_SOURCE_DIR}', NULL, 'STRING', 0, 1, NOW(), NOW())
|
||||
ON DUPLICATE KEY UPDATE
|
||||
descrizione = VALUES(descrizione),
|
||||
testo = VALUES(testo),
|
||||
numero = VALUES(numero),
|
||||
tipoParm = VALUES(tipoParm),
|
||||
flgTipo = VALUES(flgTipo),
|
||||
flgAdmin = VALUES(flgAdmin),
|
||||
lastUpdTmst = NOW();
|
||||
SQL
|
||||
14
local-jsp-docker/tomcat/Dockerfile
Normal file
14
local-jsp-docker/tomcat/Dockerfile
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
FROM tomcat:9.0.102-jdk11-temurin
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends ca-certificates bash perl rsync \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
COPY tomcat/bootstrap-docbase.sh /usr/local/bin/bootstrap-docbase.sh
|
||||
COPY tomcat/entrypoint.sh /usr/local/bin/local-jsp-entrypoint.sh
|
||||
|
||||
RUN chmod +x /usr/local/bin/bootstrap-docbase.sh /usr/local/bin/local-jsp-entrypoint.sh
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
ENTRYPOINT ["/usr/local/bin/local-jsp-entrypoint.sh"]
|
||||
48
local-jsp-docker/tomcat/bootstrap-docbase.sh
Normal file
48
local-jsp-docker/tomcat/bootstrap-docbase.sh
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
docbase="${LOCAL_DOCBASE:-/data/docbase/}"
|
||||
workspace_www="/workspace/www"
|
||||
workspace_pkl="/workspace/test_pkl"
|
||||
|
||||
mkdir -p \
|
||||
"${docbase}" \
|
||||
"${docbase}_tmp" \
|
||||
"${docbase}_logs" \
|
||||
"${docbase}admin/csv" \
|
||||
"${docbase}_help/attach" \
|
||||
"${docbase}_help/export" \
|
||||
"${docbase}_help/img"
|
||||
|
||||
link_into_docbase() {
|
||||
local source_path="$1"
|
||||
local target_path="$2"
|
||||
|
||||
rm -rf "$target_path"
|
||||
ln -s "$source_path" "$target_path"
|
||||
}
|
||||
|
||||
if [ -d "${workspace_www}/mailMessage" ]; then
|
||||
link_into_docbase "${workspace_www}/mailMessage" "${docbase}mailMessage"
|
||||
fi
|
||||
|
||||
for subdir in _attach _csv _docs _img _imgMsg csv pics images pdf Templates tmp; do
|
||||
if [ -e "${workspace_www}/${subdir}" ]; then
|
||||
link_into_docbase "${workspace_www}/${subdir}" "${docbase}${subdir}"
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -d "${workspace_pkl}" ]; then
|
||||
link_into_docbase "${workspace_pkl}" "${docbase}test_pkl"
|
||||
|
||||
for subdir in 2026 live test_images; do
|
||||
if [ -e "${workspace_pkl}/${subdir}" ]; then
|
||||
link_into_docbase "${workspace_pkl}/${subdir}" "${docbase}${subdir}"
|
||||
fi
|
||||
done
|
||||
|
||||
# Common generic aliases for code that expects a mounted external storage root.
|
||||
for alias_name in storage nas archive pkl RUS; do
|
||||
link_into_docbase "${workspace_pkl}" "${docbase}${alias_name}"
|
||||
done
|
||||
fi
|
||||
47
local-jsp-docker/tomcat/entrypoint.sh
Normal file
47
local-jsp-docker/tomcat/entrypoint.sh
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
export CATALINA_TMPDIR=/usr/local/tomcat/temp
|
||||
|
||||
runtime_root="/usr/local/tomcat/webapps/ROOT"
|
||||
workspace_www="/workspace/www"
|
||||
|
||||
rm -rf "$runtime_root"
|
||||
mkdir -p "$runtime_root"
|
||||
|
||||
shopt -s dotglob nullglob
|
||||
|
||||
for source_path in "$workspace_www"/*; do
|
||||
entry_name="$(basename "$source_path")"
|
||||
if [ "$entry_name" = "WEB-INF" ]; then
|
||||
continue
|
||||
fi
|
||||
ln -s "$source_path" "$runtime_root/$entry_name"
|
||||
done
|
||||
|
||||
mkdir -p "$runtime_root/WEB-INF"
|
||||
cp -a "$workspace_www/WEB-INF/." "$runtime_root/WEB-INF/"
|
||||
|
||||
mkdir -p /usr/local/tomcat/conf/Catalina/localhost
|
||||
cat > /usr/local/tomcat/conf/Catalina/localhost/ROOT.xml <<'EOF'
|
||||
<Context>
|
||||
<Resources allowLinking="true" />
|
||||
</Context>
|
||||
EOF
|
||||
|
||||
/usr/local/bin/bootstrap-docbase.sh
|
||||
|
||||
patch_context_param() {
|
||||
local param_name="$1"
|
||||
local param_value="$2"
|
||||
|
||||
PARAM_NAME="$param_name" PARAM_VALUE="$param_value" perl -0pi -e 's#(<param-name>\Q$ENV{PARAM_NAME}\E</param-name>\s*<param-value>)[^<]*(</param-value>)#$1 . $ENV{PARAM_VALUE} . $2#egs' "$runtime_root/WEB-INF/web.xml"
|
||||
}
|
||||
|
||||
patch_context_param database "//${LOCAL_DB_HOST:-mysql}/${LOCAL_DB_NAME:-pg}"
|
||||
patch_context_param catalog "${LOCAL_DB_NAME:-pg}"
|
||||
patch_context_param user "${LOCAL_DB_USER:-root}"
|
||||
patch_context_param password "${LOCAL_DB_PASSWORD:-root}"
|
||||
patch_context_param instance "${LOCAL_APP_INSTANCE:-local-model}"
|
||||
|
||||
exec catalina.sh run
|
||||
Loading…
Add table
Add a link
Reference in a new issue