Compare commits

...

19 Commits

Author SHA1 Message Date
0dedc0d8e0 update romm to 4.6.1 2026-01-27 19:58:05 -05:00
d1569094e7 update romm to 4.6.0 2026-01-26 19:14:18 -05:00
2703e60881 Fix config for netplay and run two job workers 2026-01-08 16:20:38 -05:00
51935bbb09 Add support for turn for netplay. Still needs testing. 2026-01-08 12:56:41 -05:00
3f9cab66f5 Redo frontend and dockerfile to use nginx and build in stages based on prod steps in romm 2026-01-07 23:05:27 -05:00
9e5f377768 remove test data and don't run uv twice 2026-01-06 19:43:36 -05:00
571e449581 Add missing workers for jobs, post install message for setup, and pull in emujs/ruffle from docker release 2026-01-06 17:11:28 -05:00
74db5b8b61 Put env vars use can update into separate file that is built from a template. 2026-01-06 14:36:50 -05:00
707aaeae15 Fix some mistakes I made preventing app from working and get OIDC working. 2026-01-05 16:38:59 -05:00
6fde39d6a4 add manifest info and icon 2026-01-03 16:01:52 -05:00
ee64902a06 add redis and cache 2026-01-03 15:48:16 -05:00
7cce873fc6 port was originally correct. set allowed hosts 2026-01-03 14:51:27 -05:00
bcf6088f3a use correct port and don't use builtin https 2026-01-03 14:26:22 -05:00
7ad6fcd4d0 move nvm outside of root 2026-01-03 14:13:18 -05:00
7b05b5ec4c put in better place 2026-01-03 14:01:43 -05:00
bfec74b10e set uv dir 2026-01-03 13:59:00 -05:00
56652719aa don't chown readonly dir 2026-01-03 13:50:41 -05:00
0ee1a88875 use real arg 2026-01-03 13:41:36 -05:00
21de7b0121 change where python installed 2026-01-03 13:38:32 -05:00
14 changed files with 589 additions and 47 deletions

7
.dockerignore Normal file
View File

@@ -0,0 +1,7 @@
.git
.gitignore
.dockerignore
node_modules
screenshots
test

View File

@@ -1,16 +1,25 @@
{
"title": "Romm",
"version": "0.1.0",
"healthCheckPath": "/",
"version": "0.2.0",
"upstreamVersion":"4.6.1",
"author":"Kevin Whitaker <kevin@eyecreate.org>",
"description":"A beautiful, powerful, self-hosted rom manager and player.",
"healthCheckPath": "/api/heartbeat",
"httpPort": 3000,
"memoryLimit": 2097152000,
"addons": {
"localstorage": {},
"postgresql": {},
"oidc": {
"loginRedirectUri": "/api/oauth/openid",
"logoutRedirectUri": "/",
"logoutRedirectUri": "/login",
"tokenSignatureAlgorithm": "RS256"
}
},
"manifestVersion": 2
"redis": { "noPassword": true },
"turn":{}
},
"website":"https://romm.app/",
"icon":"file://logo.png",
"manifestVersion": 2,
"postInstallMessage":"Before setting up the first admin user, please check /app/data/env.sh for some API keys for metadata sources and fill in any you want to use. Put any ROMs in the /app/data/library/roms folder. Please see https://docs.romm.app/latest/Getting-Started/Folder-Structure/ for details on folder structure. RESTART AFTER ANY API KEY CHANGES!"
}

View File

@@ -1,9 +1,46 @@
FROM cloudron/base:5.0.0@sha256:04fd70dbd8ad6149c19de39e35718e024417c3e01dc9c6637eaf4a41ec4e596c
ARG VERSION=4.6.1
RUN mkdir -p /app/code
WORKDIR /app/code
ARG VERSION=4.5.0
ARG ALPINE_VERSION=3.22
ARG NODE_VERSION=20.19
# FRONTEND BUILD
FROM node:${NODE_VERSION}-trixie AS frontend-build
ARG VERSION
WORKDIR /front
RUN wget "https://github.com/rommapp/romm/archive/refs/tags/${VERSION}.tar.gz" && \
tar xfz ${VERSION}.tar.gz --strip-components=2 -C /front romm-${VERSION}/frontend && rm ${VERSION}.tar.gz
RUN npm ci --ignore-scripts --no-audit --no-fund
RUN npm run build
# FETCH EMULATORJS AND RUFFLE
FROM alpine:${ALPINE_VERSION} AS emulator-stage
RUN apk add --no-cache \
7zip \
wget \
ca-certificates
ARG EMULATORJS_VERSION=4.2.3
ARG EMULATORJS_SHA256=07d451bc06fa3ad04ab30d9b94eb63ac34ad0babee52d60357b002bde8f3850b
RUN wget "https://github.com/EmulatorJS/EmulatorJS/releases/download/v${EMULATORJS_VERSION}/${EMULATORJS_VERSION}.7z" && \
echo "${EMULATORJS_SHA256} ${EMULATORJS_VERSION}.7z" | sha256sum -c - && \
7z x -y "${EMULATORJS_VERSION}.7z" -o/emulatorjs && \
rm -f "${EMULATORJS_VERSION}.7z"
ARG RUFFLE_VERSION=nightly-2025-08-14
ARG RUFFLE_FILE=ruffle-nightly-2025_08_14-web-selfhosted.zip
ARG RUFFLE_SHA256=178870c5e7dd825a8df35920dfc5328d83e53f3c4d5d95f70b1ea9cd13494151
RUN wget "https://github.com/ruffle-rs/ruffle/releases/download/${RUFFLE_VERSION}/${RUFFLE_FILE}" && \
echo "${RUFFLE_SHA256} ${RUFFLE_FILE}" | sha256sum -c - && \
unzip -o "${RUFFLE_FILE}" -d /ruffle && \
rm -f "${RUFFLE_FILE}"
FROM cloudron/base:5.0.0@sha256:04fd70dbd8ad6149c19de39e35718e024417c3e01dc9c6637eaf4a41ec4e596c as cloudron-builder
ARG NJS_VERSION=0.9.4
ARG MODZIP_VERSION=1.3.0
# Prevent interactive prompts during installation
ENV DEBIAN_FRONTEND=noninteractive
@@ -31,21 +68,19 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
liblzma-dev \
libncurses5-dev \
libncursesw5-dev \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
nginx \
nginx-dev \
libpcre3-dev \
libssl-dev \
libxml2-dev \
libxslt-dev
# Clone release
RUN wget https://github.com/rommapp/romm/archive/refs/tags/$VERSION.tar.gz && \
tar xfz $VERSION.tar.gz --strip-components=1 -C /app/code && rm $VERSION.tar.gz
WORKDIR /
RUN git clone -b ${NJS_VERSION} https://github.com/nginx/njs.git
RUN git clone -b ${MODZIP_VERSION} https://github.com/evanmiller/mod_zip.git
# Install nvm
ENV NVM_DIR="/root/.nvm"
RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash \
&& . "$NVM_DIR/nvm.sh" \
&& nvm install 18.20.8 \
&& nvm use 18.20.8 \
&& nvm alias default 18.20.8
ENV PATH="$NVM_DIR/versions/node/v18.20.8/bin:$PATH"
WORKDIR /usr/share/nginx/src
RUN ./configure --add-dynamic-module=/njs/nginx --add-dynamic-module=/mod_zip --with-compat --builddir=. && make modules
# Build and install RAHasher (optional for RA hashes)
RUN git clone --recursive --branch 1.8.1 --depth 1 https://github.com/RetroAchievements/RALibretro.git /tmp/RALibretro
@@ -55,23 +90,65 @@ RUN sed -i '22a #include <ctime>' ./src/Util.h \
./src/libchdr/deps/zlib-1.3.1/gzlib.c \
./src/libchdr/deps/zlib-1.3.1/gzread.c \
./src/libchdr/deps/zlib-1.3.1/gzwrite.c \
&& make HAVE_CHD=1 -f ./Makefile.RAHasher \
&& cp ./bin64/RAHasher /usr/bin/RAHasher
RUN rm -rf /tmp/RALibretro
&& make HAVE_CHD=1 -f ./Makefile.RAHasher
# Install frontend dependencies
WORKDIR /app/code/frontend
RUN npm install
RUN ln -s /tmp/vite-temp /app/code/frontend/node_modules/.vite-temp
# Main image starts here
FROM cloudron/base:5.0.0@sha256:04fd70dbd8ad6149c19de39e35718e024417c3e01dc9c6637eaf4a41ec4e596c
RUN mkdir -p /app/code
WORKDIR /app/code
ARG WEBSERVER_FOLDER=/app/code/frontend
ARG UV_VERSION=0.8.24
ARG VERSION
# Prevent interactive prompts during installation
ENV DEBIAN_FRONTEND=noninteractive
# Install system dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
git \
libpq-dev \
libmariadb-dev \
curl \
ca-certificates \
7zip \
tzdata \
nginx \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# Clone release backend code
RUN wget "https://github.com/rommapp/romm/archive/refs/tags/${VERSION}.tar.gz" && \
tar xfz ${VERSION}.tar.gz --strip-components=1 -C /app/code romm-${VERSION}/backend romm-${VERSION}/pyproject.toml romm-${VERSION}/uv.lock && rm ${VERSION}.tar.gz
# Install RAHasher into image
COPY --from=cloudron-builder /tmp/RALibretro/bin64/RAHasher /usr/bin/RAHasher
# Install frontend into image
WORKDIR ${WEBSERVER_FOLDER}
COPY --from=frontend-build /front/dist ${WEBSERVER_FOLDER}
COPY --from=frontend-build /front/assets ${WEBSERVER_FOLDER}/assets
COPY ./decode.js /etc/nginx/js/decode.js
COPY ./nginx.conf /etc/nginx/nginx.conf
COPY ./default.conf /etc/nginx/conf.d/default.conf
COPY --from=cloudron-builder /usr/share/nginx/src/ngx_http_js_module.so /usr/lib/nginx/modules/
COPY --from=cloudron-builder /usr/share/nginx/src/ngx_http_zip_module.so /usr/lib/nginx/modules/
RUN mkdir -p assets/romm && ln -s /app/data/assets ${WEBSERVER_FOLDER}/assets/romm/assets && ln -s /app/data/resources ${WEBSERVER_FOLDER}/assets/romm/resources
# Copy emulator to web folder
COPY --from=emulator-stage /emulatorjs ${WEBSERVER_FOLDER}/assets/emulatorjs
COPY --from=emulator-stage /ruffle ${WEBSERVER_FOLDER}/assets/ruffle
WORKDIR /app/code/
# Install uv for the non-root user
COPY --from=ghcr.io/astral-sh/uv:0.7.19 /uv /uvx /usr/local/bin/
COPY --from=ghcr.io/astral-sh/uv:$UV_VERSION /uv /uvx /usr/local/bin/
# Install Python
RUN uv python install 3.13
RUN mkdir /app/code/uv && uv python install -i /app/code/uv 3.13
# Install Python dependencies
ENV UV_PYTHON_INSTALL_DIR=/app/code/uv
RUN uv sync --all-extras
# add supervisor configs
@@ -81,6 +158,8 @@ RUN ln -sf /run/supervisord.log /var/log/supervisor/supervisord.log
ENV PATH="/app/code/.venv/bin:${PATH}"
COPY start.sh /app/pkg/
COPY env.sh.template /app/pkg/
COPY config.yml.template /app/pkg/
CMD [ "/app/pkg/start.sh" ]

171
config.yml.template Normal file
View File

@@ -0,0 +1,171 @@
# Only uncomment the lines you want to use/modify, or add new ones where needed
# exclude:
# # Exclude platforms to be scanned
# platforms:
# - excluded_folder_a
# - excluded_folder_b
# # Exclude roms or parts of roms to be scanned
# roms:
# # Single file games section.
# # Will not apply to files that are in sub-folders (multi-disc roms, games with updates, DLC, patches, etc.)
# single_file:
# # Exclude all files with certain extensions to be scanned
# extensions:
# - xml
# - txt
# # Exclude matched file names to be scanned
# # Supports unix filename pattern matching
# names:
# - 'info.txt'
# - '._*'
# - '*.nfo'
# # Multi files games section
# # Will apply to files that are in sub-folders (multi-disc roms, games with updates, DLC, patches, etc.)
# multi_file:
# # Exclude matched 'folder' names to be scanned (RomM identifies folders as multi file games)
# # Common ES-DE media folders are listed below
# names:
# - 3dboxes
# - backcovers
# - covers
# - fanart
# - manuals
# - marquees
# - miximages
# - physicalmedia
# - screenshots
# - titlescreens
# - videos
# - downloaded_media
# - media
# # Exclude files within sub-folders.
# parts:
# # Exclude matched file names to be scanned from multi file roms
# # Keep in mind that RomM doesn't scan folders inside multi files games,
# # so there is no need to exclude folders from inside of multi files games.
# names:
# - 'data.xml'
# - '._*' # Supports unix filename pattern matching
# # Exclude all files with certain extensions to be scanned from multi file roms
# extensions:
# - xml
# - txt
# system:
# # Asociate different platform names to your current file system platform names
# # [your custom platform folder name]: [RomM platform name]
# # In this example if you have a 'gc' folder, RomM will treat it like the 'ngc' folder
# platforms:
# gc: ngc
# ps1: psx
# # Asociate one platform to it's main version (IGDB only)
# versions:
# naomi: arcade
# The folder name where your roms are located
# filesystem:
# roms_folder: 'roms' # For example if your folder structure is /home/user/library/roms_folder
# skip_hash_calculation: false # Skip file hash calculations on low power devices (eg. Raspberry PI)
# scan:
# # Metadata priority during scans
# priority:
# # Below are the default priority values used
# metadata: # Top-level metadata source priority
# - "igdb" # IGDB (highest priority)
# - "moby" # MobyGames
# - "ss" # Screenscraper
# - "ra" # RetroAchievements
# - "launchbox" # Launchbox
# - "gamelist" # ES-DE gamelist.xml
# - "hasheous" # Hasheous
# - "flashpoint" # Flashpoint Project
# - "hltb" # HowLongToBeat (lowest priority)
# artwork: # Cover art and screenshots
# - "igdb" # IGDB
# - "moby" # MobyGames
# - "ss" # Screenscraper
# - "ra" # RetroAchievements
# - "launchbox" # Launchbox
# - "gamelist" # ES-DE gamelist.xml
# - "hasheous" # Hasheous
# - "flashpoint" # Flashpoint Project
# - "hltb" # HowLongToBeat
# region: # Used by IGDB and ScreenScraper for regional variants
# - "us"
# - "wor"
# - "ss"
# - "eu"
# - "jp"
# language: # Used by ScreenScraper for descriptions
# - "en"
# - "fr"
# # Media assets to download
# # Only used by Screenscraper and ES-DE gamelist.xml
# media:
# # Used as alternative cover art
# - box2d # Normal cover art (always enabled)
# - box3d # 3D box art
# - miximage # Mixed image of multiple media
# - physical # Disc, cartridge, etc.
# # Added to the screenshots carousel
# - screenshot # Screenshot (enabled by default)
# - title_screen # Title screen
# - fanart # User uploaded artwork
# # Bezel displayed around the emulatorjs window
# - bezel
# # Manual in PDF format
# - manual # Manual (enabled by default)
# # Gameplay video
# - video # Video (warning: large file size)
# # Media used for batocera gamelist.xml export
# - box2d_back # Back cover art
# - logo # Transparent logo
# # Other media assets (might be used in the future)
# - marquee # Custom marquee
# EmulatorJS per-core options
# emulatorjs:
# debug: true # Available options will be logged to the browser console
# cache_limit: null # Cache limit per ROM (in bytes)
# disable_batch_bootup: false
# disable_auto_unload: false
# settings:
# parallel_n64: # Use the exact core name
# vsync: disabled
# snes9x:
# snes9x_region: ntsc
# default: # These settings apply to all cores
# fps: show
# netplay:
# enabled: true
# ice_servers:
# - urls: "stun:stun.relay.metered.ca:80"
# - urls: "turn:global.relay.metered.ca:80"
# username: "<username>"
# credential: "<password>"
# controls: # https://emulatorjs.org/docs4devs/control-mapping/
# snes9x:
# 0: # Player 1
# 0: # A MAPPING FOR EACH BUTTON MUST BE SET!
# value: x # Mapping for keyboard
# value2: BUTTON_2 # Mapping for connected controller
# 1: # Player 2
# 2: # Player 3
# 3: # Player 4
emulatorjs:
netplay:
enabled: true
ice_servers:
- urls: "stun:$CLOUDRON_STUN_SERVER:$CLOUDRON_STUN_PORT"
- urls: "turn:$CLOUDRON_TURN_SERVER:$CLOUDRON_TURN_PORT"
username: "$turn_username"
credential: "$turn_password"

19
decode.js Normal file
View File

@@ -0,0 +1,19 @@
// Decode a Base64 encoded string received as a query parameter named 'value',
// and return the decoded value in the response body.
function decodeBase64(r) {
var encodedValue = r.args.value;
if (!encodedValue) {
r.return(400, "Missing 'value' query parameter");
return;
}
try {
var decodedValue = atob(encodedValue);
r.return(200, decodedValue);
} catch (e) {
r.return(400, "Invalid Base64 encoding");
}
}
export default { decodeBase64 };

73
default.conf Normal file
View File

@@ -0,0 +1,73 @@
# Helper to get scheme regardless if we are behind a proxy or not
map $http_x_forwarded_proto $forwardscheme {
default $scheme;
https https;
}
# COEP and COOP headers for cross-origin isolation, which are set only for the
# EmulatorJS player path, to enable SharedArrayBuffer support, which is needed
# for multi-threaded cores.
map $request_uri $coep_header {
default "";
~^/rom/.*/ejs$ "require-corp";
}
map $request_uri $coop_header {
default "";
~^/rom/.*/ejs$ "same-origin";
}
server {
root /app/code/frontend;
listen 3000;
listen [::]:3000;
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 $forwardscheme;
location / {
try_files $uri $uri/ /index.html;
proxy_redirect off;
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods *;
add_header Access-Control-Allow-Headers *;
add_header Cross-Origin-Embedder-Policy $coep_header;
add_header Cross-Origin-Opener-Policy $coop_header;
}
# Static files
location /assets {
try_files $uri $uri/ =404;
}
# OpenAPI for swagger and redoc
location /openapi.json {
proxy_pass http://backend_server;
}
# Backend api calls
location /api {
proxy_pass http://backend_server;
proxy_request_buffering off;
proxy_buffering off;
}
location ~ ^/(ws|netplay) {
proxy_pass http://backend_server;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
# Internally redirect download requests
location /library/ {
internal;
alias "/app/data/library/";
}
# Internal decoding endpoint, used to decode base64 encoded data
location /decode {
internal;
js_content decode.decodeBase64;
}
}

13
env.sh.template Normal file
View File

@@ -0,0 +1,13 @@
#!/bin/bash
set -eu
export IGDB_CLIENT_ID=
export IGDB_CLIENT_SECRET=
export MOBYGAMES_API_KEY=
export STEAMGRIDDB_API_KEY=
export RETROACHIEVEMENTS_API_KEY=
export SCREENSCRAPER_USER=
export SCREENSCRAPER_PASSWORD=
export HASHEOUS_API_ENABLED=true
export OIDC_ENABLED=true

BIN
logo.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 13 KiB

88
nginx.conf Normal file
View File

@@ -0,0 +1,88 @@
load_module modules/ngx_http_js_module.so;
load_module modules/ngx_http_zip_module.so;
worker_processes auto;
pid /tmp/nginx.pid;
events {
worker_connections 768;
multi_accept on;
}
http {
client_body_temp_path /tmp/client_body 1 2;
fastcgi_temp_path /tmp/fastcgi 1 2;
proxy_temp_path /tmp/proxy;
uwsgi_temp_path /tmp/uwsgi;
scgi_temp_path /tmp/scgi;
sendfile on;
client_body_buffer_size 128k;
client_max_body_size 0;
client_header_buffer_size 1k;
large_client_header_buffers 4 16k;
send_timeout 600s;
keepalive_timeout 600s;
client_body_timeout 600s;
tcp_nopush on;
tcp_nodelay on;
include /etc/nginx/mime.types;
default_type application/octet-stream;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE
ssl_prefer_server_ciphers on;
js_import /etc/nginx/js/decode.js;
map $time_iso8601 $date {
~([^+]+)T $1;
}
map $time_iso8601 $time {
~T([0-9:]+)\+ $1;
}
# Map to extract the browser name (e.g., Chrome, Firefox, etc.)
map $http_user_agent $browser {
default "Unknown";
"~Chrome/" "Chrome";
"~Firefox/" "Firefox";
"~Safari/" "Safari";
"~Edge/" "Edge";
"~Opera/" "Opera";
}
# Map to extract the OS (e.g., Windows, MacOS, Linux)
map $http_user_agent $os {
default "Unknown";
"~Windows NT" "Windows";
"~Macintosh" "macOS";
"~Linux" "Linux";
"~Android" "Android";
"~iPhone" "iOS";
}
#INFO: [nginx][2023-11-14 09:20:29] 127.0.0.1 - -"GET / HTTP/1.1" 500 177 "-" "Mozilla/5.0 (X11; Linux x86_64)"rt=0.000 uct="-" uht="-" urt="-"
log_format romm_logs 'INFO: [RomM][nginx][$date $time] '
'$remote_addr | $http_x_forwarded_for | '
'$request_method $request_uri $status | $body_bytes_sent | '
'$browser $os | $request_time';
access_log /dev/stdout romm_logs;
error_log /dev/stderr;
gzip on;
gzip_proxied any;
gzip_vary on;
gzip_comp_level 6;
gzip_buffers 16 8k;
gzip_min_length 1024;
gzip_http_version 1.1;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
upstream backend_server {
server 127.0.0.1:5000;
}
include /etc/nginx/conf.d/*.conf;
}

View File

@@ -2,10 +2,32 @@
set -eu
mkdir -p /app/data/library /app/data/config /app/data/assets /app/data/resources
mkdir -p /tmp/vite-temp
mkdir -p /tmp/vite-temp /tmp/vite /tmp/cache
CONFIG_TEMPLATE_FILE="/app/pkg/config.yml.template"
CONFIG_FILE="/app/data/config/config.yml"
# Check if config file exists in /app/data/config
if [ -f "$CONFIG_FILE" ]; then
echo "config.yml already exists in $CONFIG_FILE"
else
# Copy the template to the target location
echo "Copying $CONFIG_TEMPLATE_FILE to $CONFIG_FILE"
cp "$CONFIG_TEMPLATE_FILE" "$CONFIG_FILE"
# Add in TURN
readonly time=$(date +%s)
readonly expiry=512640 # one year in seconds
export turn_username=$(( $time + $expiry ))
export turn_password=$(echo -n $turn_username | openssl dgst -binary -sha1 -hmac ${CLOUDRON_TURN_SECRET} | openssl base64)
envsubst < $CONFIG_TEMPLATE_FILE > $CONFIG_FILE
echo "Successfully created $CONFIG_FILE from template"
fi
# ensure that data directory is owned by 'cloudron' user
chown -R cloudron:cloudron /app/data /tmp/vite-temp /root/.local/share/uv/python
chown -R cloudron:cloudron /app/data /tmp/vite-temp /tmp/vite /tmp/cache
cd /app/data
@@ -15,18 +37,40 @@ export DB_PORT=${CLOUDRON_POSTGRESQL_PORT}
export DB_NAME=${CLOUDRON_POSTGRESQL_DATABASE}
export DB_USER=${CLOUDRON_POSTGRESQL_USERNAME}
export DB_PASSWD=${CLOUDRON_POSTGRESQL_PASSWORD}
export ROMM_AUTH_SECRET_KEY=$(/usr/bin/openssl rand -hex 32)
export IGDB_CLIENT_ID=
export IGDB_CLIENT_SECRET=
export MOBYGAMES_API_KEY=
export STEAMGRIDDB_API_KEY=
export OIDC_ENABLED=true
export OIDC_PROVIDER=${CLOUDRON_OIDC_PROVIDER_NAME}
export OIDC_CLIENT_ID=${CLOUDRON_OIDC_CLIENT_ID}
export OIDC_CLIENT_SECRET=${CLOUDRON_OIDC_CLIENT_SECRET}
export OIDC_REDIRECT_URI="/api/oauth/openid"
export OIDC_SERVER_APPLICATION_URL=${CLOUDRON_OIDC_AUTH_ENDPOINT}
export OIDC_REDIRECT_URI="${CLOUDRON_APP_ORIGIN}/api/oauth/openid"
export OIDC_SERVER_APPLICATION_URL=${CLOUDRON_OIDC_ISSUER}
export UV_PYTHON_INSTALL_DIR=/app/code/uv
export DEV_HTTPS=false
export __VITE_ADDITIONAL_SERVER_ALLOWED_HOSTS=${CLOUDRON_APP_DOMAIN}
export XDG_CACHE_HOME=/tmp/cache
export REDIS_HOST=${CLOUDRON_REDIS_HOST}
export REDIS_PORT=${CLOUDRON_REDIS_PORT}
export ROMM_BASE_PATH=/app/data
export ROMM_TMP_PATH=/tmp
ENV_TEMPLATE_FILE="/app/pkg/env.sh.template"
ENV_FILE="/app/data/env.sh"
# Check if env.sh exists in /app/data
if [ -f "$ENV_FILE" ]; then
echo "env.sh already exists in $ENV_FILE"
else
# Copy the template to the target location
echo "Copying $ENV_TEMPLATE_FILE to $ENV_FILE"
cp "$ENV_TEMPLATE_FILE" "$ENV_FILE"
chmod +x "$ENV_FILE"
# Generate a new secret key using openssl
SECRET_KEY=$(openssl rand -hex 32 2>/dev/null)
echo "export ROMM_AUTH_SECRET_KEY=$SECRET_KEY" >> "$ENV_FILE"
echo "Successfully created $ENV_FILE from template"
fi
source /app/data/env.sh
echo "==> Starting supervisor"
exec /usr/bin/supervisord --configuration /etc/supervisor/supervisord.conf --nodaemon -i Romm

View File

@@ -1,12 +1,12 @@
[program:backend]
priority=5
directory=/app/code/frontend
environment=HOME=/app/code
command=npm run dev
user=cloudron
[program:frontend]
priority=50
directory=/tmp
command=/usr/sbin/nginx -g "daemon off;"
user=root
autostart=true
autorestart=true
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0

15
supervisor/rq.conf Normal file
View File

@@ -0,0 +1,15 @@
[program:rq]
priority=10
process_name="%(program_name)s_%(process_num)02d"
numprocs=2
directory=/app/code/backend
environment=HOME=/app/code
environment=PYTHONPATH="/app/code/backend:${PYTHONPATH-}"
command=uv run rq worker --path /app/code/backend --pid /tmp/rq_worker_%(process_num)02d.pid --url "redis://%(ENV_REDIS_HOST)s:%(ENV_REDIS_PORT)s/0" high default low
user=cloudron
autostart=true
autorestart=true
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0

View File

@@ -0,0 +1,12 @@
[program:rqscheduler]
priority=10
directory=/app/code/backend
environment=HOME=/app/code
command=uv run rqscheduler --host %(ENV_REDIS_HOST)s --port %(ENV_REDIS_PORT)s --db 0 --path /app/code.backend --pid /tmp/rq_scheduler.pid
user=cloudron
autostart=true
autorestart=true
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0

12
supervisor/watcher.conf Normal file
View File

@@ -0,0 +1,12 @@
[program:watcher]
priority=20
directory=/app/code/backend
environment=HOME=/app/code
command=uv run watchfiles --target-type command 'python watcher.py' /app/data/library
user=cloudron
autostart=true
autorestart=true
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0