Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
e613138
deps: bump narwhals from 1.32.0 to 2.2.0 (#277)
dependabot[bot] Sep 2, 2025
8695f86
deps: bump fonttools from 4.56.0 to 4.59.1 (#276)
dependabot[bot] Sep 2, 2025
e9a8367
deps: bump protobuf from 5.29.4 to 6.32.0 (#274)
dependabot[bot] Sep 2, 2025
89e26a0
deps: bump packaging from 24.2 to 25.0 (#235)
dependabot[bot] Sep 2, 2025
dbdbb00
move to windows 2022 (#285)
t0mdavid-m Sep 22, 2025
b17c73d
deps: bump narwhals from 2.3.0 to 2.5.0 (#284)
dependabot[bot] Sep 22, 2025
1f212dd
deps: bump streamlit from 1.49.0 to 1.49.1 (#283)
dependabot[bot] Sep 22, 2025
4d3e286
deps: bump gitpython from 3.1.44 to 3.1.45 (#281)
dependabot[bot] Sep 22, 2025
6e266f4
deps: bump charset-normalizer from 3.4.1 to 3.4.3 (#280)
dependabot[bot] Sep 22, 2025
6fd1a91
deps: bump certifi from 2025.1.31 to 2025.8.3 (#279)
dependabot[bot] Sep 22, 2025
8b681f6
Reset CAPTCHA input field after incorrect submission (#296)
Copilot Dec 17, 2025
f2ad2b1
Fix Builds for OpenMS 3.5 (#299)
t0mdavid-m Dec 18, 2025
546e091
enable thirdparty tools for template (#300)
t0mdavid-m Dec 22, 2025
fc4823e
add convenience function for retrieving parameters (#301)
t0mdavid-m Dec 25, 2025
8ab28ad
fix traceback (#297)
t0mdavid-m Dec 25, 2025
5884583
Pin autowrap to <=0.24 to fix Docker build failures (#308)
Copilot Jan 20, 2026
2ee6466
add support for empty list values (#311)
t0mdavid-m Jan 20, 2026
f83ad8c
Redis queue system for online mode (#305)
t0mdavid-m Jan 22, 2026
ec4f41c
Add demo workspace support for online deployments
claude Jan 22, 2026
20c399e
Remove auto_load_for_online demo workspace feature
claude Jan 22, 2026
79ac017
Support multiple source directories for demo workspaces
claude Jan 22, 2026
1699472
Rename default demo workspace to example_demo and remove sample data
claude Jan 22, 2026
8de5612
Add path traversal validation for workspace names
claude Jan 23, 2026
9078bc3
add callable and reactive parameters
t0mdavid-m Jan 23, 2026
ae49ff3
add interactivity to input widget
t0mdavid-m Jan 23, 2026
f62b9f3
improve error handling (#317)
t0mdavid-m Jan 26, 2026
4328fbf
extend advanced section, fix matching (#316)
t0mdavid-m Jan 26, 2026
231df8d
fix list display bug (#314)
t0mdavid-m Jan 26, 2026
ebc7ebb
Merge pull request #315 from OpenMS/add_on_change_reative
t0mdavid-m Jan 26, 2026
3fa4498
Merge pull request #312 from OpenMS/claude/demo-workspace-migration-9…
t0mdavid-m Jan 26, 2026
a2b98f5
Use symlinks for demo workspace files on Linux
claude Jan 26, 2026
98b147f
Merge pull request #318 from OpenMS/claude/symlink-demo-workspace-QWwnW
t0mdavid-m Jan 26, 2026
b9f7a99
Remove Redis queue progress bars from UI
claude Jan 26, 2026
8e405d6
Merge pull request #319 from OpenMS/claude/redis-queue-progress-bar-p…
t0mdavid-m Jan 26, 2026
e97c415
Fix log line limit not being applied in static display
claude Jan 26, 2026
a0b3f7e
Merge pull request #320 from OpenMS/claude/fix-log-line-limit-FPBFB
t0mdavid-m Jan 26, 2026
e5d9480
Use st.multiselect() for list parameters with predefined options
claude Jan 27, 2026
9674791
Merge pull request #322 from OpenMS/claude/streamlit-multiselect-ui-d…
t0mdavid-m Jan 27, 2026
5711f10
Fix multiselect parameter format for TOPP tool compatibility
claude Jan 27, 2026
d9cd52f
Simplify multiselect implementation
claude Jan 27, 2026
7ce5995
Fix _display keys being processed by ParameterManager
claude Jan 27, 2026
22fbf35
Add admin feature to save workspaces as demo workspaces
claude Jan 27, 2026
39d9fa5
Merge pull request #323 from OpenMS/claude/streamlit-multiselect-ui-d…
t0mdavid-m Jan 27, 2026
7c3c91c
Copy params.json instead of symlinking when loading demo data
claude Jan 28, 2026
427acda
Also copy .ini files instead of symlinking when loading demo data
claude Jan 28, 2026
d36bf02
Merge pull request #325 from OpenMS/claude/fix-params-symlink-rKtkv
t0mdavid-m Jan 28, 2026
56d8b46
Fix circular import in admin.py
claude Jan 28, 2026
8e2a34d
Merge pull request #324 from OpenMS/claude/freeze-workshop-demo-featu…
t0mdavid-m Jan 28, 2026
e1df1a7
Propagate workflow stderr to minimal log output (#326)
t0mdavid-m Jan 28, 2026
45518a1
Copy entire .streamlit folder in Docker containers (#329)
t0mdavid-m Jan 29, 2026
ae20b2c
Fix TOPP parameter handling for zero values (#328)
t0mdavid-m Jan 29, 2026
8cb8b57
refactor: extract INI creation into shared ParameterManager.create_in…
t0mdavid-m Jan 29, 2026
b8bd2c2
fix: use system Python with --target for Windows embeddable builds (#…
t0mdavid-m Jan 29, 2026
a784301
Add parameter presets system for workflow optimization (#330)
t0mdavid-m Jan 29, 2026
19efd4f
feat: add configurable max threads for local and online deployments (…
t0mdavid-m Jan 29, 2026
42aabb6
feat: only show stderr in minimal log when process fails (#335)
t0mdavid-m Jan 31, 2026
0820681
fix: Windows executable build CAB file limit (#334)
t0mdavid-m Jan 31, 2026
eb9c205
Fix threads in offline deployment (#333)
t0mdavid-m Jan 31, 2026
126a74b
Add nginx load balancing support for multi-instance Streamlit deploym…
t0mdavid-m Feb 9, 2026
affe68f
Bind nginx load balancer to all interfaces (0.0.0.0) (#11) (#337)
t0mdavid-m Feb 10, 2026
68a9a59
Bind Streamlit to all interfaces (0.0.0.0) in all Docker modes (#338)
t0mdavid-m Feb 10, 2026
1f2267c
Claude/streamlit listen all ips l wgaj (#339)
t0mdavid-m Feb 10, 2026
de0458a
fix cross origin bullshit (#340)
t0mdavid-m Feb 10, 2026
44415ba
Add Matomo analytics integration with GDPR consent support (#341)
t0mdavid-m Feb 20, 2026
c65d503
Remove duplicate `address` key in `.streamlit/config.toml` (#346)
Copilot Mar 3, 2026
bcfb03f
Merge streamlit-template/main into FLASHApp
claude Mar 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 24 additions & 12 deletions .github/workflows/build-windows-executable-app.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,14 @@ jobs:
repository: t0mdavid-m/OpenMS
ref: FVdeploy
path: 'OpenMS'
- name: Install Qt

- name: Install Qt (Windows)
uses: jurplel/install-qt-action@v4
with:
version: '6.8.3'
version: '6.8.3' ## Note this version is build with win64_msvc2022_64 and should always match what we use
arch: 'win64_msvc2022_64'
cache: 'false'
archives: 'qtsvg qtimageformats qtbase'

# https://github.com/marketplace/actions/visual-studio-shell
- name: Set up Visual Studio shell
Expand Down Expand Up @@ -112,6 +114,12 @@ jobs:
rm contrib_build-Windows.tar.gz
ls

- name: Add contrib to PATH
shell: bash
run: |
# Add contrib library path for runtime DLL resolution
echo "${{ github.workspace }}/OpenMS/contrib/lib" >> $GITHUB_PATH

- name: Setup ccache cache
uses: actions/cache@v3
with:
Expand Down Expand Up @@ -145,9 +153,9 @@ jobs:
shell: bash
run: |
mkdir $GITHUB_WORKSPACE/OpenMS/bld/
bash OpenMS/tools/ci/capture-env.sh -v $GITHUB_WORKSPACE/OpenMS/bld/CMakeCache.txt
ctest --output-on-failure -V -S $GITHUB_WORKSPACE/OpenMS/tools/ci/cibuild.cmake
env:
#OS_PREFIX_PATH: "${{ env.Qt5_DIR }}/lib/cmake;${{ env.Qt5_DIR }}"
OPENMS_CONTRIB_LIBS: "${{ github.workspace }}/OpenMS/contrib"
CI_PROVIDER: "GitHub-Actions"
CMAKE_GENERATOR: "Ninja"
Expand All @@ -157,6 +165,7 @@ jobs:
ENABLE_TOPP_TESTING: "ON"
ENABLE_CLASS_TESTING: "ON"
WITH_GUI: "OFF"
WITH_PARQUET: "OFF"
ADDRESS_SANITIZER: "Off"
BUILD_TYPE: "Release"
OPENMP: "On"
Expand Down Expand Up @@ -199,7 +208,7 @@ jobs:
build-executable:
runs-on: windows-2022
needs: [build-openms, build-vue-js-component]

steps:
- name: Checkout
uses: actions/checkout@v3
Expand Down Expand Up @@ -259,12 +268,6 @@ jobs:
cp $PYTHON_DIR/DLLs/tcl86t.dll $EMBED_DIR/
cp $PYTHON_DIR/DLLs/tk86t.dll $EMBED_DIR/

- name: Install pip
run: |
curl -O https://bootstrap.pypa.io/get-pip.py
./python-${{ env.PYTHON_VERSION }}/python get-pip.py --no-warn-script-location
rm get-pip.py

- name: Uncomment 'import site' in python311._pth file
run: |
sed -i 's/#import site/import site/' python-${{ env.PYTHON_VERSION }}/python311._pth
Expand All @@ -284,6 +287,15 @@ jobs:
- name: Create .bat file
run: |
echo '@echo off' > ${{ env.APP_NAME }}.bat
echo 'setlocal EnableDelayedExpansion' > ${{ env.APP_NAME }}.bat
Comment on lines 289 to +290
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Line 290 overwrites the batch-file header.

The second redirection recreates ${{ env.APP_NAME }}.bat, so the @echo off written on the previous line is lost. Use >> here if you want both directives to stay in the file.

💡 Minimal fix
         echo '@echo off' > ${{ env.APP_NAME }}.bat
-        echo 'setlocal EnableDelayedExpansion' > ${{ env.APP_NAME }}.bat
+        echo 'setlocal EnableDelayedExpansion' >> ${{ env.APP_NAME }}.bat
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
echo '@echo off' > ${{ env.APP_NAME }}.bat
echo 'setlocal EnableDelayedExpansion' > ${{ env.APP_NAME }}.bat
echo '@echo off' > ${{ env.APP_NAME }}.bat
echo 'setlocal EnableDelayedExpansion' >> ${{ env.APP_NAME }}.bat
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/build-windows-executable-app.yaml around lines 289 - 290,
The second echo line overwrites the batch file created by the first echo (so
'@echo off' is lost); change the second redirection that writes "setlocal
EnableDelayedExpansion" to append (use >>) to the same file (referencing the
lines that write to ${{ env.APP_NAME }}.bat) so both directives remain in the
.bat header.

echo '' >> ${{ env.APP_NAME }}.bat
echo 'REM Set OpenMS data path for TOPP tools' >> ${{ env.APP_NAME }}.bat
echo 'set OPENMS_DATA_PATH=%~dp0share\OpenMS' >> ${{ env.APP_NAME }}.bat
echo '' >> ${{ env.APP_NAME }}.bat
echo 'REM Add each subfolder in share\OpenMS\THIRDPARTY to PATH' >> ${{ env.APP_NAME }}.bat
echo 'for /D %%D in ("%OPENMS_DATA_PATH%\THIRDPARTY\*") do (' >> ${{ env.APP_NAME }}.bat
echo ' set "PATH=!PATH!;%%D"' >> ${{ env.APP_NAME }}.bat
echo ')' >> ${{ env.APP_NAME }}.bat
echo '' >> ${{ env.APP_NAME }}.bat
echo 'REM Create .streamlit directory in user''s home if it doesn''t exist' >> ${{ env.APP_NAME }}.bat
echo 'if not exist "%USERPROFILE%\.streamlit" mkdir "%USERPROFILE%\.streamlit"' >> ${{ env.APP_NAME }}.bat
Expand Down Expand Up @@ -375,7 +387,7 @@ jobs:
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Id="*" Name="${{ env.APP_NAME }}" Language="1033" Version="1.0.0.0" Codepage="1252" Manufacturer="OpenMS Developer Team" UpgradeCode="${{ env.APP_UpgradeCode }}">
<Package Id="*" InstallerVersion="300" Compressed="yes" InstallPrivileges="elevated" Platform="x64" />
<Media Id="1" Cabinet="streamlit.cab" EmbedCab="yes" />
<MediaTemplate EmbedCab="yes" MaximumUncompressedMediaSize="200" CabinetTemplate="data{0}.cab" />

<!-- Folder structure -->
<Property Id="WIXUI_INSTALLDIR" Value="INSTALLFOLDER" />
Expand Down
18 changes: 10 additions & 8 deletions .github/workflows/test-win-exe-w-embed-py.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,29 +17,31 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Python (regular distribution)
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }} # Use the same version as the embeddable version

- name: Download python embeddable version
run: |
mkdir python-${{ env.PYTHON_VERSION }}
curl -O https://www.python.org/ftp/python/${{ env.PYTHON_VERSION }}/python-${{ env.PYTHON_VERSION }}-embed-amd64.zip
unzip python-${{ env.PYTHON_VERSION }}-embed-amd64.zip -d python-${{ env.PYTHON_VERSION }}
rm python-${{ env.PYTHON_VERSION }}-embed-amd64.zip

- name: Install pip
run: |
curl -O https://bootstrap.pypa.io/get-pip.py
./python-${{ env.PYTHON_VERSION }}/python get-pip.py --no-warn-script-location
rm get-pip.py

- name: Uncomment 'import site' in python311._pth file
run: |
sed -i 's/#import site/import site/' python-${{ env.PYTHON_VERSION }}/python311._pth

- name: Print content of python311._pth file
run: |
cat python-${{ env.PYTHON_VERSION }}/python311._pth

- name: Install Required Packages
run: .\python-${{ env.PYTHON_VERSION }}\python -m pip install -r requirements.txt --no-warn-script-location
# Use system Python (which has development headers) to compile packages,
# installing into the embeddable Python's site-packages directory.
# The embeddable Python lacks Python.h headers needed for native extensions.
run: python -m pip install -r requirements.txt --target python-${{ env.PYTHON_VERSION }}/Lib/site-packages --upgrade --no-warn-script-location

- name: Create .bat file
run: |
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,4 @@ run_app.bat
python*
gdpr_consent/node_modules/
*~
.streamlit/secrets.toml
4 changes: 4 additions & 0 deletions .streamlit/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,12 @@ gatherUsageStats = false
developmentMode = false

[server]
address = "0.0.0.0"
maxUploadSize = 2000 #MB
port = 8501 # should be same as configured in deployment repo
enableCORS = false
enableXsrfProtection = false
Comment on lines +11 to +12
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# First, let's read the .streamlit/config.toml file to see the full context
cat -n .streamlit/config.toml

Repository: OpenMS/FLASHApp

Length of output: 1035


🏁 Script executed:

# Also check if there are any comments or documentation in the file
head -30 .streamlit/config.toml

Repository: OpenMS/FLASHApp

Length of output: 697


🌐 Web query:

Streamlit enableCORS enableXsrfProtection security implications reverse proxy

💡 Result:

What these flags actually do

  • server.enableCORS is effectively “CORS protection” in Streamlit: when it’s true, Streamlit restricts which browser origins can call the app; when it’s false, Streamlit will allow cross-origin access broadly (e.g., Access-Control-Allow-Origin: * for many endpoints). Streamlit enables it by default as the “most secure posture.” [1][2]
  • server.enableXsrfProtection enables CSRF/XSRF protection by issuing an _xsrf token cookie and expecting the frontend to echo that token back, so the backend can tell requests are coming from the Streamlit frontend. [2]

The key security/behavior constraint (important behind proxies too)

  • You can’t safely mix “allow all origins” with CSRF protection. Streamlit treats:
    • enableCORS=false incompatible with enableXsrfProtection=true, and will override CORS back to enabled/restrictive mode if XSRF protection is on. This is documented and also logged as a warning by Streamlit. [1][2]

Security implications of disabling them

  • Disabling XSRF protection (enableXsrfProtection=false) removes a major defense against cross-site request forgery for any state-changing HTTP endpoints your app relies on (uploads, callbacks, component endpoints, etc.). Streamlit explicitly recommends only turning these off if you understand and accept the risk. [2]
  • Disabling CORS protection (enableCORS=false) allows other websites/origins to make browser-initiated requests to your Streamlit server much more freely, increasing the risk of cross-origin abuse (especially when combined with cookies/sessions). [2]

Reverse proxy guidance (what to do instead of disabling)

  • In most reverse-proxy setups, you should keep both enabled (defaults) and:
    • set server.corsAllowedOrigins to the external origin(s) your users will access (your proxy’s public URL), rather than disabling CORS. [1]
    • if you’re serving Streamlit under a subpath (e.g., /app1), configure server.baseUrlPath to match that path so the app works correctly behind the proxy (this is often confused with CORS/XSRF breakage). [1][5]
    • terminate TLS at the reverse proxy / load balancer (Streamlit docs recommend SSL termination there for production). [1]

Special cases to be aware of

  • If you configure Streamlit’s built-in authentication (st.login / OIDC via secrets.toml), Streamlit will automatically enable CORS and XSRF protection, and this overrides config.toml. [3]
  • On Streamlit Community Cloud, some security-related settings (including enableXsrfProtection) may be forced/overridden by the platform. [4]

Rule of thumb: behind a reverse proxy, treat CORS/XSRF errors as a signal that your public origin/path doesn’t match what Streamlit expects—fix with corsAllowedOrigins and/or baseUrlPath rather than turning protections off. [1][2][5]

Sources

[1] Streamlit docs — config.toml options (enableCORS, corsAllowedOrigins, enableXsrfProtection, baseUrlPath, SSL termination note) (docs.streamlit.io)
[2] Streamlit forum — explanation of how enableCORS/enableXsrfProtection work and why they conflict (discuss.streamlit.io)
[3] Streamlit docs — st.login note: auth config auto-enables CORS/XSRF and overrides config.toml (docs.streamlit.io)
[4] Streamlit docs — Community Cloud overrides/limitations including enableXsrfProtection (docs.streamlit.io)
[5] Streamlit forum — reverse proxy under subdirectory fixed via server.baseUrlPath (discuss.streamlit.io)

Citations:


Do not disable Streamlit's request-forgery protections in production deployments.

Lines 11-12 disable both CORS and XSRF protection. Combined with Line 8 exposing the server to all interfaces (0.0.0.0), this creates a cross-site request forgery vulnerability for state-changing operations—particularly concerning given the app's file upload and admin flow capabilities. The correct approach for reverse-proxy setups is to keep these protections enabled and configure corsAllowedOrigins to your proxy's external URL and baseUrlPath if serving under a subpath, rather than disabling the protections entirely.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.streamlit/config.toml around lines 11 - 12, Do not disable Streamlit's
request-forgery protections: revert enableCORS and enableXsrfProtection to true
instead of false, and instead of disabling protections configure
corsAllowedOrigins to the external proxy URL (set corsAllowedOrigins =
["https://your-proxy.example.com"]) and set baseUrlPath if the app is served
under a subpath; ensure server.address/server.host remains appropriate (e.g.,
0.0.0.0 for binding) while keeping XSRF and CORS protections enabled for
production.



[theme]
# The preset Streamlit theme that your custom theme inherits from. One of "light" or "dark".
Expand Down
8 changes: 8 additions & 0 deletions .streamlit/secrets.toml.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Streamlit Secrets Configuration
# Copy this file to secrets.toml and fill in your values.
# IMPORTANT: Never commit secrets.toml to version control!

[admin]
# Password required to save workspaces as demo workspaces (online mode only)
# Set a strong, unique password here
password = "your-secure-admin-password-here"
82 changes: 77 additions & 5 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,14 @@ RUN rm -rf openms-build

# Prepare and run streamlit app.
FROM compile-openms AS run-app

# Install Redis server for job queue and nginx for load balancing
RUN apt-get update && apt-get install -y --no-install-recommends redis-server nginx \
&& rm -rf /var/lib/apt/lists/*

# Create Redis data directory
RUN mkdir -p /var/lib/redis && chown redis:redis /var/lib/redis

# Create workdir and copy over all streamlit related files/folders.

# note: specifying folder with slash as suffix and repeating the folder name seems important to preserve directory structure
Expand All @@ -157,15 +165,79 @@ COPY src/ /app/src
COPY app.py /app/app.py
COPY settings.json /app/settings.json
COPY default-parameters.json /app/default-parameters.json
COPY presets.json /app/presets.json

# add cron job to the crontab
RUN echo "0 3 * * * /root/miniforge3/envs/streamlit-env/bin/python /app/clean-up-workspaces.py >> /app/clean-up-workspaces.log 2>&1" | crontab -

# create entrypoint script to start cron service and launch streamlit app
RUN echo "#!/bin/bash" > /app/entrypoint.sh && \
echo "source /root/miniforge3/bin/activate streamlit-env" >> /app/entrypoint.sh && \
echo "service cron start" >> /app/entrypoint.sh && \
echo "streamlit run app.py" >> /app/entrypoint.sh
# Set default worker count (can be overridden via environment variable)
ENV RQ_WORKER_COUNT=1
ENV REDIS_URL=redis://localhost:6379/0

# Number of Streamlit server instances for load balancing (default: 1 = no load balancer)
# Set to >1 to enable nginx load balancer with multiple Streamlit instances
ENV STREAMLIT_SERVER_COUNT=1

# create entrypoint script to start cron, Redis, RQ workers, and Streamlit
RUN echo -e '#!/bin/bash\n\
set -e\n\
source /root/miniforge3/bin/activate streamlit-env\n\
\n\
# Start cron for workspace cleanup\n\
service cron start\n\
\n\
# Start Redis server in background\n\
echo "Starting Redis server..."\n\
redis-server --daemonize yes --dir /var/lib/redis --appendonly no\n\
\n\
# Wait for Redis to be ready\n\
until redis-cli ping > /dev/null 2>&1; do\n\
echo "Waiting for Redis..."\n\
sleep 1\n\
done\n\
echo "Redis is ready"\n\
\n\
# Start RQ worker(s) in background\n\
WORKER_COUNT=${RQ_WORKER_COUNT:-1}\n\
echo "Starting $WORKER_COUNT RQ worker(s)..."\n\
for i in $(seq 1 $WORKER_COUNT); do\n\
rq worker openms-workflows --url $REDIS_URL --name worker-$i &\n\
done\n\
\n\
# Load balancer setup\n\
SERVER_COUNT=${STREAMLIT_SERVER_COUNT:-1}\n\
\n\
if [ "$SERVER_COUNT" -gt 1 ]; then\n\
echo "Starting $SERVER_COUNT Streamlit instances with nginx load balancer..."\n\
\n\
# Generate nginx upstream block\n\
UPSTREAM_SERVERS=""\n\
BASE_PORT=8510\n\
for i in $(seq 0 $((SERVER_COUNT - 1))); do\n\
PORT=$((BASE_PORT + i))\n\
UPSTREAM_SERVERS="${UPSTREAM_SERVERS} server 127.0.0.1:${PORT};\\n"\n\
done\n\
\n\
# Write nginx config\n\
mkdir -p /etc/nginx\n\
echo -e "worker_processes auto;\\npid /run/nginx.pid;\\n\\nevents {\\n worker_connections 1024;\\n}\\n\\nhttp {\\n client_max_body_size 0;\\n\\n map \\$cookie_stroute \\$route_key {\\n \\x22\\x22 \\$request_id;\\n default \\$cookie_stroute;\\n }\\n\\n upstream streamlit_backend {\\n hash \\$route_key consistent;\\n${UPSTREAM_SERVERS} }\\n\\n map \\$http_upgrade \\$connection_upgrade {\\n default upgrade;\\n \\x27\\x27 close;\\n }\\n\\n server {\\n listen 0.0.0.0:8501;\\n\\n location / {\\n proxy_pass http://streamlit_backend;\\n proxy_http_version 1.1;\\n proxy_set_header Upgrade \\$http_upgrade;\\n proxy_set_header Connection \\$connection_upgrade;\\n proxy_set_header Host \\$host;\\n proxy_set_header X-Real-IP \\$remote_addr;\\n proxy_set_header X-Forwarded-For \\$proxy_add_x_forwarded_for;\\n proxy_set_header X-Forwarded-Proto \\$scheme;\\n proxy_read_timeout 86400;\\n proxy_send_timeout 86400;\\n proxy_buffering off;\\n add_header Set-Cookie \\x22stroute=\\$route_key; Path=/; HttpOnly; SameSite=Lax\\x22 always;\\n }\\n }\\n}" > /etc/nginx/nginx.conf\n\
\n\
# Start Streamlit instances on internal ports\n\
for i in $(seq 0 $((SERVER_COUNT - 1))); do\n\
PORT=$((BASE_PORT + i))\n\
echo "Starting Streamlit instance on port $PORT..."\n\
streamlit run app.py --server.port $PORT --server.address 0.0.0.0 &\n\
done\n\
\n\
sleep 2\n\
echo "Starting nginx load balancer on port 8501..."\n\
exec /usr/sbin/nginx -g "daemon off;"\n\
else\n\
# Single instance mode (default) - run Streamlit directly on port 8501\n\
echo "Starting Streamlit app..."\n\
exec streamlit run app.py --server.address 0.0.0.0\n\
fi\n\
' > /app/entrypoint.sh
# make the script executable
RUN chmod +x /app/entrypoint.sh

Expand Down
5 changes: 4 additions & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ services:
- 8501:8501
volumes:
- workspaces-streamlit-template:/workspaces-streamlit-template
command: streamlit run openms-streamlit-template/app.py
environment:
# Number of Streamlit server instances (default: 1 = no load balancer).
# Set to >1 to enable nginx load balancing across multiple Streamlit instances.
- STREAMLIT_SERVER_COUNT=1
Comment on lines +15 to +18
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Persist Redis alongside workspaces if job recovery is expected.

This service now runs Redis locally, but the compose setup still only persists the workspace volume. Recreating the container wipes /var/lib/redis while leaving .job_id files behind, so recovered workflows can point to jobs that no longer exist. Add a Redis data volume or clear stale job IDs on startup.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docker-compose.yml` around lines 15 - 18, Persist Redis data by adding a
named volume for Redis in the docker-compose service that runs Redis (mount host
volume to /var/lib/redis) and declare the volume under top-level volumes so
Redis state survives container recreation; alternatively, modify that service's
startup/entrypoint to remove stale .job_id files from workspace paths on boot
(detect and delete leftover .job_id files) so recovered workflows don't
reference missing Redis entries — refer to the Redis data directory
(/var/lib/redis) and the .job_id marker files when making changes.

volumes:
workspaces-streamlit-template:
Loading
Loading