Compare commits
19 Commits
1b8bd7b94f
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 0dedc0d8e0 | |||
| d1569094e7 | |||
| 2703e60881 | |||
| 51935bbb09 | |||
| 3f9cab66f5 | |||
| 9e5f377768 | |||
| 571e449581 | |||
| 74db5b8b61 | |||
| 707aaeae15 | |||
| 6fde39d6a4 | |||
| ee64902a06 | |||
| 7cce873fc6 | |||
| bcf6088f3a | |||
| 7ad6fcd4d0 | |||
| 7b05b5ec4c | |||
| bfec74b10e | |||
| 56652719aa | |||
| 0ee1a88875 | |||
| 21de7b0121 |
7
.dockerignore
Normal file
7
.dockerignore
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
.dockerignore
|
||||||
|
node_modules
|
||||||
|
screenshots
|
||||||
|
test
|
||||||
|
|
||||||
@@ -1,16 +1,25 @@
|
|||||||
{
|
{
|
||||||
"title": "Romm",
|
"title": "Romm",
|
||||||
"version": "0.1.0",
|
"version": "0.2.0",
|
||||||
"healthCheckPath": "/",
|
"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,
|
"httpPort": 3000,
|
||||||
|
"memoryLimit": 2097152000,
|
||||||
"addons": {
|
"addons": {
|
||||||
"localstorage": {},
|
"localstorage": {},
|
||||||
"postgresql": {},
|
"postgresql": {},
|
||||||
"oidc": {
|
"oidc": {
|
||||||
"loginRedirectUri": "/api/oauth/openid",
|
"loginRedirectUri": "/api/oauth/openid",
|
||||||
"logoutRedirectUri": "/",
|
"logoutRedirectUri": "/login",
|
||||||
"tokenSignatureAlgorithm": "RS256"
|
"tokenSignatureAlgorithm": "RS256"
|
||||||
}
|
},
|
||||||
|
"redis": { "noPassword": true },
|
||||||
|
"turn":{}
|
||||||
},
|
},
|
||||||
"manifestVersion": 2
|
"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!"
|
||||||
}
|
}
|
||||||
|
|||||||
131
Dockerfile
131
Dockerfile
@@ -1,9 +1,46 @@
|
|||||||
FROM cloudron/base:5.0.0@sha256:04fd70dbd8ad6149c19de39e35718e024417c3e01dc9c6637eaf4a41ec4e596c
|
ARG VERSION=4.6.1
|
||||||
|
|
||||||
RUN mkdir -p /app/code
|
ARG ALPINE_VERSION=3.22
|
||||||
WORKDIR /app/code
|
ARG NODE_VERSION=20.19
|
||||||
ARG VERSION=4.5.0
|
|
||||||
|
|
||||||
|
# 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
|
# Prevent interactive prompts during installation
|
||||||
ENV DEBIAN_FRONTEND=noninteractive
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
@@ -31,21 +68,19 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
|||||||
liblzma-dev \
|
liblzma-dev \
|
||||||
libncurses5-dev \
|
libncurses5-dev \
|
||||||
libncursesw5-dev \
|
libncursesw5-dev \
|
||||||
&& apt-get clean \
|
nginx \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
nginx-dev \
|
||||||
|
libpcre3-dev \
|
||||||
|
libssl-dev \
|
||||||
|
libxml2-dev \
|
||||||
|
libxslt-dev
|
||||||
|
|
||||||
# Clone release
|
WORKDIR /
|
||||||
RUN wget https://github.com/rommapp/romm/archive/refs/tags/$VERSION.tar.gz && \
|
RUN git clone -b ${NJS_VERSION} https://github.com/nginx/njs.git
|
||||||
tar xfz $VERSION.tar.gz --strip-components=1 -C /app/code && rm $VERSION.tar.gz
|
RUN git clone -b ${MODZIP_VERSION} https://github.com/evanmiller/mod_zip.git
|
||||||
|
|
||||||
# Install nvm
|
WORKDIR /usr/share/nginx/src
|
||||||
ENV NVM_DIR="/root/.nvm"
|
RUN ./configure --add-dynamic-module=/njs/nginx --add-dynamic-module=/mod_zip --with-compat --builddir=. && make modules
|
||||||
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"
|
|
||||||
|
|
||||||
# Build and install RAHasher (optional for RA hashes)
|
# 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
|
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/gzlib.c \
|
||||||
./src/libchdr/deps/zlib-1.3.1/gzread.c \
|
./src/libchdr/deps/zlib-1.3.1/gzread.c \
|
||||||
./src/libchdr/deps/zlib-1.3.1/gzwrite.c \
|
./src/libchdr/deps/zlib-1.3.1/gzwrite.c \
|
||||||
&& make HAVE_CHD=1 -f ./Makefile.RAHasher \
|
&& make HAVE_CHD=1 -f ./Makefile.RAHasher
|
||||||
&& cp ./bin64/RAHasher /usr/bin/RAHasher
|
|
||||||
RUN rm -rf /tmp/RALibretro
|
|
||||||
|
|
||||||
# Install frontend dependencies
|
# Main image starts here
|
||||||
WORKDIR /app/code/frontend
|
FROM cloudron/base:5.0.0@sha256:04fd70dbd8ad6149c19de39e35718e024417c3e01dc9c6637eaf4a41ec4e596c
|
||||||
RUN npm install
|
|
||||||
RUN ln -s /tmp/vite-temp /app/code/frontend/node_modules/.vite-temp
|
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/
|
WORKDIR /app/code/
|
||||||
# Install uv for the non-root user
|
# 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
|
# 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
|
# Install Python dependencies
|
||||||
|
ENV UV_PYTHON_INSTALL_DIR=/app/code/uv
|
||||||
RUN uv sync --all-extras
|
RUN uv sync --all-extras
|
||||||
|
|
||||||
# add supervisor configs
|
# 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}"
|
ENV PATH="/app/code/.venv/bin:${PATH}"
|
||||||
|
|
||||||
COPY start.sh /app/pkg/
|
COPY start.sh /app/pkg/
|
||||||
|
COPY env.sh.template /app/pkg/
|
||||||
|
COPY config.yml.template /app/pkg/
|
||||||
|
|
||||||
CMD [ "/app/pkg/start.sh" ]
|
CMD [ "/app/pkg/start.sh" ]
|
||||||
|
|
||||||
|
|||||||
171
config.yml.template
Normal file
171
config.yml.template
Normal 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
19
decode.js
Normal 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
73
default.conf
Normal 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
13
env.sh.template
Normal 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
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
88
nginx.conf
Normal 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;
|
||||||
|
}
|
||||||
64
start.sh
64
start.sh
@@ -2,10 +2,32 @@
|
|||||||
set -eu
|
set -eu
|
||||||
|
|
||||||
mkdir -p /app/data/library /app/data/config /app/data/assets /app/data/resources
|
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
|
# 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
|
cd /app/data
|
||||||
|
|
||||||
@@ -15,18 +37,40 @@ export DB_PORT=${CLOUDRON_POSTGRESQL_PORT}
|
|||||||
export DB_NAME=${CLOUDRON_POSTGRESQL_DATABASE}
|
export DB_NAME=${CLOUDRON_POSTGRESQL_DATABASE}
|
||||||
export DB_USER=${CLOUDRON_POSTGRESQL_USERNAME}
|
export DB_USER=${CLOUDRON_POSTGRESQL_USERNAME}
|
||||||
export DB_PASSWD=${CLOUDRON_POSTGRESQL_PASSWORD}
|
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_PROVIDER=${CLOUDRON_OIDC_PROVIDER_NAME}
|
||||||
export OIDC_CLIENT_ID=${CLOUDRON_OIDC_CLIENT_ID}
|
export OIDC_CLIENT_ID=${CLOUDRON_OIDC_CLIENT_ID}
|
||||||
export OIDC_CLIENT_SECRET=${CLOUDRON_OIDC_CLIENT_SECRET}
|
export OIDC_CLIENT_SECRET=${CLOUDRON_OIDC_CLIENT_SECRET}
|
||||||
export OIDC_REDIRECT_URI="/api/oauth/openid"
|
export OIDC_REDIRECT_URI="${CLOUDRON_APP_ORIGIN}/api/oauth/openid"
|
||||||
export OIDC_SERVER_APPLICATION_URL=${CLOUDRON_OIDC_AUTH_ENDPOINT}
|
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_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"
|
echo "==> Starting supervisor"
|
||||||
exec /usr/bin/supervisord --configuration /etc/supervisor/supervisord.conf --nodaemon -i Romm
|
exec /usr/bin/supervisord --configuration /etc/supervisor/supervisord.conf --nodaemon -i Romm
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
[program:backend]
|
[program:frontend]
|
||||||
priority=5
|
priority=50
|
||||||
directory=/app/code/frontend
|
directory=/tmp
|
||||||
environment=HOME=/app/code
|
command=/usr/sbin/nginx -g "daemon off;"
|
||||||
command=npm run dev
|
user=root
|
||||||
user=cloudron
|
|
||||||
autostart=true
|
autostart=true
|
||||||
autorestart=true
|
autorestart=true
|
||||||
stdout_logfile=/dev/stdout
|
stdout_logfile=/dev/stdout
|
||||||
stdout_logfile_maxbytes=0
|
stdout_logfile_maxbytes=0
|
||||||
stderr_logfile=/dev/stderr
|
stderr_logfile=/dev/stderr
|
||||||
stderr_logfile_maxbytes=0
|
stderr_logfile_maxbytes=0
|
||||||
|
|
||||||
|
|||||||
15
supervisor/rq.conf
Normal file
15
supervisor/rq.conf
Normal 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
|
||||||
12
supervisor/rqscheduler.conf
Normal file
12
supervisor/rqscheduler.conf
Normal 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
12
supervisor/watcher.conf
Normal 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
|
||||||
Reference in New Issue
Block a user