diff --git a/README.md b/README.md index b3ae04f1e..1d2dbe7a8 100644 --- a/README.md +++ b/README.md @@ -75,16 +75,14 @@ Inspired by [Jellyfin](https://jellyfin.org/), allows you to manage all your gam Docker should be installed and set up before running the [image](https://hub.docker.com/r/zurdi15/romm/tags). -1. Generate an API key for [IGDB](https://www.igdb.com/), and set the `IGDB_CLIENT_ID` and `IGDB_CLIENT_SECRET` variables. _This is required to run a library scan._ Instructions on generating the ID and Secret are [here](https://api-docs.igdb.com/#about). Note that IDGB requires a Twitch account with 2FA enabled to generate the ID and Secret. +1. Generate an API key for [IGDB](https://www.igdb.com/), and set the `IGDB_CLIENT_ID` and `IGDB_CLIENT_SECRET` variables. _This is required to run a library scan._ Instructions on generating the ID and Secret are [here](https://api-docs.igdb.com/#about). Note that IGDB requires a Twitch account with 2FA enabled to generate the ID and Secret. 2. Verify that your library folder structure matches one of the options listed in the [following section](#folder-structure). -3. Create a docker-compose file. See the following example [docker-compose.yml](https://github.com/zurdi15/romm/blob/master/examples/docker-compose.example.yml) file for reference. Customize for your setup and include the `IGDB_CLIENT_ID` and `IGDB_CLIENT_SECRET` vareiables where indicated in the environment section of the file. +3. Create a docker-compose file. See the following example [docker-compose.yml](https://github.com/zurdi15/romm/blob/master/examples/docker-compose.example.yml) file for reference. Customize for your setup and include the `IGDB_CLIENT_ID` and `IGDB_CLIENT_SECRET` variables where indicated in the environment section of the file. 4. Launch the container: ```bash docker-compose up -d ``` - - If configured correctly, Romm will automatically run an initial scan on your library. # Configuration @@ -143,7 +141,7 @@ If you want to enable the user management system, a redis container and some env - `ROMM_AUTH_ENABLED` and `ENABLE_EXPERIMENTAL_REDIS` must be set as `true` - `ROMM_AUTH_SECRET_KEY` must be generated with `openssl rand -hex 32` - `ROMM_AUTH_USERNAME` and `ROMM_AUTH_PASSWORD` can be set as wanted, being both `admin` by default. - - `REDIS_HOST` and `REDIS_PORT` must point to your redis instance + - `REDIS_HOST` and `REDIS_PORT` must point to your redis instance. Aditionally, if your redis is secured you can set `REDIS_PASSWORD`

⚙️ Configuration file

diff --git a/backend/config/__init__.py b/backend/config/__init__.py index a26453fd0..f01dd7b0e 100644 --- a/backend/config/__init__.py +++ b/backend/config/__init__.py @@ -43,6 +43,7 @@ ENABLE_EXPERIMENTAL_REDIS: Final = ( ) REDIS_HOST: Final = os.environ.get("REDIS_HOST", "localhost") REDIS_PORT: Final = os.environ.get("REDIS_PORT", "6379") +REDIS_PASSWORD: Final = os.environ.get("REDIS_PASSWORD") # IGDB IGDB_CLIENT_ID: Final = os.environ.get( diff --git a/backend/utils/cache.py b/backend/utils/cache.py index cfd900df9..60c80519d 100644 --- a/backend/utils/cache.py +++ b/backend/utils/cache.py @@ -1,6 +1,16 @@ from redis import Redis -from config import REDIS_HOST, REDIS_PORT, ENABLE_EXPERIMENTAL_REDIS +from config import REDIS_HOST, REDIS_PORT, REDIS_PASSWORD, ENABLE_EXPERIMENTAL_REDIS + +redis_client = Redis( + host=REDIS_HOST, port=int(REDIS_PORT), password=REDIS_PASSWORD, db=0 +) +redis_url = ( + f"redis://:{REDIS_PASSWORD}@{REDIS_HOST}:{REDIS_PORT}" + if REDIS_PASSWORD + else f"redis://{REDIS_HOST}:{REDIS_PORT}" +) + class FallbackCache: def __init__(self) -> None: @@ -30,7 +40,11 @@ class FallbackCache: # A seperate client that auto-decodes responses is needed _cache_client = Redis( - host=REDIS_HOST, port=int(REDIS_PORT), db=0, decode_responses=True + host=REDIS_HOST, + port=int(REDIS_PORT), + password=REDIS_PASSWORD, + db=0, + decode_responses=True, ) _fallback_cache = FallbackCache() cache = _cache_client if ENABLE_EXPERIMENTAL_REDIS else _fallback_cache diff --git a/docker-compose.yml b/docker-compose.yml index fadac7b25..203d11d58 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -15,6 +15,16 @@ services: redis: image: redis:alpine container_name: redis + command: + # - /bin/sh + # - -c + # # - Double dollars, so that the variable is not expanded by Docker Compose + # # - Surround by quotes, so that the shell does not split the password + # # - The ${variable:?message} syntax causes shell to exit with a non-zero + # # code and print a message, when the variable is not set or empty + # - redis-server --requirepass "$${REDIS_PASSWORD:?REDIS_PASSWORD variable is not set}" restart: unless-stopped ports: - ${REDIS_PORT}:6379 + env_file: + - .env diff --git a/env.template b/env.template index d9f6cbe82..fadefe4c5 100644 --- a/env.template +++ b/env.template @@ -24,6 +24,7 @@ ENABLE_EXPERIMENTAL_REDIS=true REDIS_HOST=127.0.0.1 REDIS_PORT=6379 ENABLE_EXPERIMENTAL_REDIS=true +#REDIS_PASSWORD=admin_redis # Authentication (optional) ROMM_AUTH_ENABLED=true diff --git a/examples/docker-compose.example.yml b/examples/docker-compose.example.yml index e8d6f9c9a..dbc1a5376 100644 --- a/examples/docker-compose.example.yml +++ b/examples/docker-compose.example.yml @@ -27,6 +27,7 @@ services: - ENABLE_EXPERIMENTAL_REDIS=true # default: false - REDIS_HOST=redis # default: localhost - REDIS_PORT=6379 # default: 6379 + - REDIS_PASSWORD= # [Optional] Support for secured redis volumes: - "/path/to/library:/romm/library" - "/path/to/resources:/romm/resources" # [Optional] Path where roms metadata (covers) are stored @@ -40,9 +41,9 @@ services: restart: "unless-stopped" # [Optional] Only required if using MariaDB as the database - mariadb: + romm_db: image: mariadb:latest - container_name: mariadb + container_name: romm_db environment: - MYSQL_ROOT_PASSWORD= - MYSQL_DATABASE=romm diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 1147bf500..58419b97e 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,6 +1,6 @@ { "name": "romm", - "version": "2.0.0", + "version": "2.0.1", "lockfileVersion": 3, "requires": true, "packages": { diff --git a/frontend/package.json b/frontend/package.json index 3536ecb36..cdbbd2101 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "romm", - "version": "2.0.0", + "version": "2.0.1", "private": true, "scripts": { "dev": "vite --host", diff --git a/frontend/src/components/Drawer/Base.vue b/frontend/src/components/Drawer/Base.vue index 9105f1702..84a30a170 100644 --- a/frontend/src/components/Drawer/Base.vue +++ b/frontend/src/components/Drawer/Base.vue @@ -50,7 +50,7 @@ emitter.on("toggleDrawerRail", () => { value.reduce((count, p) => count + p.n_roms, 0), + filledPlatforms: ({ value }) => value.filter((p) => p.n_roms > 0), }, actions: { set(platforms) { diff --git a/frontend/src/stores/roms.js b/frontend/src/stores/roms.js index 48df6d1b5..64e1b84eb 100644 --- a/frontend/src/stores/roms.js +++ b/frontend/src/stores/roms.js @@ -3,6 +3,7 @@ import { defineStore } from "pinia"; export default defineStore("roms", { state: () => ({ + _platform: "", _all: [], _filteredIDs: [], _searchIDs: [], @@ -14,6 +15,7 @@ export default defineStore("roms", { }), getters: { + platform: (state) => state._platform, allRoms: (state) => state._all, filteredRoms: (state) => state._all.filter((rom) => state._filteredIDs.includes(rom.id)), @@ -33,6 +35,9 @@ export default defineStore("roms", { "id" ); }, + setPlatform(platform) { + this._platform = platform; + }, // All roms set(roms) { this._all = roms; diff --git a/frontend/src/views/Dashboard/Platforms.vue b/frontend/src/views/Dashboard/Platforms.vue index af6b4b349..d71faef44 100644 --- a/frontend/src/views/Dashboard/Platforms.vue +++ b/frontend/src/views/Dashboard/Platforms.vue @@ -17,7 +17,7 @@ const platforms = storePlatforms(); mdi-controller{{ platforms.value.length }} Platforms + >{{ platforms.filledPlatforms.length }} Platforms mdi-disc{{ platforms.totalGames }} Games diff --git a/frontend/src/views/Gallery/Base.vue b/frontend/src/views/Gallery/Base.vue index 53427ec1a..197f52acd 100644 --- a/frontend/src/views/Gallery/Base.vue +++ b/frontend/src/views/Gallery/Base.vue @@ -68,10 +68,11 @@ async function fetchRoms(platform) { const allRomsSet = [...allRoms.value, ...response.data.items]; romsStore.set(allRomsSet); romsStore.setFiltered(allRomsSet); + romsStore.setPlatform(platform); if (isFiltered) { searchCursor.value = response.data.next_page; - + const serchedRomsSet = [...searchRoms.value, ...response.data.items]; romsStore.setSearch(serchedRomsSet); romsStore.setFiltered(serchedRomsSet); @@ -143,7 +144,23 @@ function selectRom({ event, index, selected }) { } } +function resetGallery() { + cursor.value = ""; + searchCursor.value = ""; + romsStore.reset(); + scrolledToTop.value = true; +} + onMounted(() => { + const platform = route.params.platform; + + // If platform is different, reset store and fetch roms + if (platform != romsStore.platform) { + resetGallery(); + fetchRoms(route.params.platform); + } + + // If platform is the same but there are no roms, fetch them if (filteredRoms.value.length == 0) { fetchRoms(route.params.platform); } @@ -154,14 +171,12 @@ onBeforeUnmount(() => { }); onBeforeRouteLeave((to, from, next) => { - // Only reset selection if platform is the same + // Reset only the selection if platform is the same if (to.fullPath.includes(from.path)) { romsStore.resetSelection(); - // Otherwise reset store + // Otherwise reset the entire store } else { - cursor.value = ""; - searchCursor.value = ""; - romsStore.reset(); + resetGallery(); } next(); @@ -169,11 +184,8 @@ onBeforeRouteLeave((to, from, next) => { onBeforeRouteUpdate((to, _) => { // Reset store if switching to another platform - cursor.value = ""; - searchCursor.value = ""; - romsStore.reset(); + resetGallery(); fetchRoms(to.params.platform); - scrolledToTop.value = true; }); diff --git a/frontend/src/views/Login.vue b/frontend/src/views/Login.vue index f594be00a..2c19c9f56 100644 --- a/frontend/src/views/Login.vue +++ b/frontend/src/views/Login.vue @@ -11,8 +11,10 @@ const router = useRouter(); const username = ref(); const password = ref(); const visiblePassword = ref(false); +const logging = ref(false); function login() { + logging.value = true; api .post( "/login", @@ -36,6 +38,9 @@ function login() { icon: "mdi-close-circle", color: "red", }); + }) + .finally(() => { + logging.value = false; }); } @@ -88,10 +93,20 @@ onBeforeMount(async () => { Login + :loading="logging" + >Login + + diff --git a/pyproject.toml b/pyproject.toml index c89f73312..72e89eafa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "RomM" -version = "2.0.0" +version = "2.0.1" description = "RomM (Rom Manager) is a web based retro roms manager integrated with IGDB." authors = ["zurdi "] readme = "README.md" diff --git a/unraid_template/romm.xml b/unraid_template/romm.xml index 9370f8070..bebb175d0 100644 --- a/unraid_template/romm.xml +++ b/unraid_template/romm.xml @@ -44,6 +44,7 @@ +