Compare commits

...

2 Commits

Author SHA1 Message Date
cpu
ec39b8d774 visuals fixed on mobile 2025-05-09 12:09:15 +02:00
cpu
04379a4cec improved readme 2025-05-09 10:20:46 +02:00
9 changed files with 87 additions and 62 deletions

View File

@@ -7,6 +7,8 @@ node_modules
# Docker specific files (if any, other than Dockerfile itself) # Docker specific files (if any, other than Dockerfile itself)
# .dockerignore (to avoid including itself if context changes) # .dockerignore (to avoid including itself if context changes)
docker
systemd
# Local development environment files # Local development environment files
.env .env

View File

@@ -125,7 +125,11 @@ For an enhanced tactile experience, Nexus Timer supports Smart Buttons based on
} }
``` ```
## Building for the production ## Building for the production
Clone the repo Navigate to your project directory on the server
```bash
cd /virt
```
Clone the repository
```bash ```bash
git clone --depth 1 https://gitea.virtonline.eu/2HoursProject/nexus-timer.git git clone --depth 1 https://gitea.virtonline.eu/2HoursProject/nexus-timer.git
cd nexus-timer cd nexus-timer
@@ -134,76 +138,40 @@ Build the docker image
```bash ```bash
docker build -t virt-nexus-timer . docker build -t virt-nexus-timer .
``` ```
Do some sanity check first: Run the app in the container ## Exposing the App Behind Traefik (Reverse Proxy)
### Review the provided docker labels and systemd service file
Copy the example label file to its destination
```bash ```bash
docker run --rm -p 8080:80 virt-nexus-timer cp docker/traefik.labels labels
``` ```
Test the web app View the example service definition:
```bash ```bash
curl http://localhost:8080/ cat systemd/virt-nexus-timer.service
``` ```
## Expose the app on Internet behind the reverse proxy (Traefik) ### Create the systemd service
Create a 'labels' file for Traefik Use the editor to create or overwrite the service:
```bash
sudo tee labels <<EOF
traefik.enable=true
traefik.docker.network=traefik
traefik.http.routers.virt-nexus-timer.rule=Host("nexus-timer.virtonline.eu")
traefik.http.routers.virt-nexus-timer.service=virt-nexus-timer
traefik.http.routers.virt-nexus-timer.tls=true
traefik.http.routers.virt-nexus-timer.tls.certResolver=default
traefik.http.routers.virt-nexus-timer.entrypoints=web-secure
traefik.http.services.virt-nexus-timer.loadbalancer.server.port=80
EOF
```
Create the systemd service file
```bash ```bash
sudo systemctl edit --force --full virt-nexus-timer.service sudo systemctl edit --force --full virt-nexus-timer.service
``` ```
File editor will open. Insert the service definition bellow then save and exit. Paste the contents from virt-nexus-timer.service, then save and exit.
```bash
[Unit]
Description=nexus-timer (virt-nexus-timer)
Requires=docker.service
After=docker.service
DefaultDependencies=no
[Service] Enable on system boot and start the service
Type=simple
Environment="HOME=/root"
ExecStartPre=-/usr/bin/env sh -c '/usr/bin/env docker kill virt-nexus-timer 2>/dev/null || true'
ExecStartPre=-/usr/bin/env sh -c '/usr/bin/env docker rm virt-nexus-timer 2>/dev/null || true'
ExecStart=/usr/bin/env docker run \
--rm \
--name=virt-nexus-timer \
--network=traefik-net \
--label-file /opt/nexus-timer/labels \
virt-nexus-timer
ExecStop=-/usr/bin/env sh -c '/usr/bin/env docker kill virt-nexus-timer 2>/dev/null || true'
ExecStop=-/usr/bin/env sh -c '/usr/bin/env docker rm virt-nexus-timer 2>/dev/null || true'
Restart=always
RestartSec=30
SyslogIdentifier=virt-nexus-timer
[Install]
WantedBy=multi-user.target
```
Enable the app to start when the server boots and start it now
```bash ```bash
sudo systemctl enable --now virt-nexus-timer.service sudo systemctl enable --now virt-nexus-timer.service
``` ```
See the service status Check the service status
```bash ```bash
systemctl status virt-nexus-timer.service systemctl status virt-nexus-timer.service
``` ```
See the logs View real-time logs
```bash ```bash
journalctl -fu virt-nexus-timer.service journalctl -fu virt-nexus-timer.service
``` ```
Finally test the web app # Test the web application
Verify that the application is accessible via HTTPS:
```bash ```bash
curl https://nexus-timer.virtonline.eu curl https://nexus-timer.virtonline.eu
``` ```
Or open it in your browser:
[https://nexus-timer.virtonline.eu](https://nexus-timer.virtonline.eu)

8
docker/traefik.labels Normal file
View File

@@ -0,0 +1,8 @@
traefik.enable=true
traefik.docker.network=traefik
traefik.http.routers.virt-nexus-timer.rule=Host("nexus-timer.virtonline.eu")
traefik.http.routers.virt-nexus-timer.service=virt-nexus-timer
traefik.http.routers.virt-nexus-timer.tls=true
traefik.http.routers.virt-nexus-timer.tls.certResolver=default
traefik.http.routers.virt-nexus-timer.entrypoints=web-secure
traefik.http.services.virt-nexus-timer.loadbalancer.server.port=80

8
labels Normal file
View File

@@ -0,0 +1,8 @@
traefik.enable=true
traefik.docker.network=traefik
traefik.http.routers.virt-nexus-timer.rule=Host("nexus-timer.virtonline.eu")
traefik.http.routers.virt-nexus-timer.service=virt-nexus-timer
traefik.http.routers.virt-nexus-timer.tls=true
traefik.http.routers.virt-nexus-timer.tls.certResolver=default
traefik.http.routers.virt-nexus-timer.entrypoints=web-secure
traefik.http.services.virt-nexus-timer.loadbalancer.server.port=80

View File

@@ -1,4 +1,4 @@
const CACHE_VERSION = 'nexus-timer-cache-v3.3'; const CACHE_VERSION = 'nexus-timer-cache-v3.4';
const APP_SHELL_URLS = [ const APP_SHELL_URLS = [
// '/', // Let NetworkFirst handle '/' // '/', // Let NetworkFirst handle '/'
'/manifest.json', '/manifest.json',

View File

@@ -7,6 +7,17 @@ body {
overscroll-behavior-y: contain; /* Prevents pull-to-refresh on mobile */ overscroll-behavior-y: contain; /* Prevents pull-to-refresh on mobile */
} }
/* Safe area insets for mobile devices (status bar, home indicator) */
.safe-area-padding {
padding-top: env(safe-area-inset-top);
/* padding-bottom: env(safe-area-inset-bottom); */
}
.safe-area-height {
height: calc(100dvh - env(safe-area-inset-top));
min-height: 0;
}
/* Basic button styling */ /* Basic button styling */
.btn { .btn {
@apply px-4 py-2 rounded font-semibold focus:outline-none focus:ring-2 focus:ring-opacity-50; @apply px-4 py-2 rounded font-semibold focus:outline-none focus:ring-2 focus:ring-opacity-50;

View File

@@ -32,7 +32,7 @@
</div> </div>
<!-- Player Name --> <!-- Player Name -->
<h2 class="font-semibold mb-6 md:mb-6 text-5xl sm:text-2xl md:text-3xl lg:text-5xl"> <h2 class="font-semibold mb-4 md:mb-6 text-5xl sm:text-2xl md:text-3xl lg:text-5xl">
{{ player.name }} {{ player.name }}
</h2> </h2>
<TimerDisplay <TimerDisplay
@@ -42,7 +42,6 @@
class="text-4xl sm:text-5xl md:text-6xl lg:text-6xl xl:text-5xl" class="text-4xl sm:text-5xl md:text-6xl lg:text-6xl xl:text-5xl"
/> />
<p v-if="player.isSkipped" class="text-red-500 dark:text-red-400 mt-1 md:mt-2 font-semibold text-sm md:text-base lg:text-lg">SKIPPED</p> <p v-if="player.isSkipped" class="text-red-500 dark:text-red-400 mt-1 md:mt-2 font-semibold text-sm md:text-base lg:text-lg">SKIPPED</p>
<p v-if="isNextPlayerArea && !player.isSkipped" class="mt-1 md:mt-2 text-xs md:text-sm text-gray-600 dark:text-gray-400">(Swipe up to pass turn)</p>
</div> </div>
</template> </template>
@@ -109,9 +108,9 @@ const calculateAvatarSize = () => {
const availableHeight = screenHeight / 2; const availableHeight = screenHeight / 2;
let size = Math.min(availableHeight * 0.5, screenWidth * 0.4, 175); let size = Math.min(availableHeight * 0.5, screenWidth * 0.4, 175);
if (screenWidth < 960) { if (screenWidth < 768) { // Mobile
size = Math.min(availableHeight * 0.7, screenWidth * 0.6, 250); size = Math.min(availableHeight * 0.7, screenWidth * 0.6, 230);
} else if (screenWidth < 1024) { } else if (screenWidth < 1024) { // Tablet
size = Math.min(availableHeight * 0.55, screenWidth * 0.45, 180); size = Math.min(availableHeight * 0.55, screenWidth * 0.45, 180);
} }
size = Math.max(size, 100); size = Math.max(size, 100);

View File

@@ -1,5 +1,5 @@
<template> <template>
<div class="flex flex-col h-screen overflow-hidden" :class="{'dark': theme === 'dark'}"> <div class="flex flex-col safe-area-height safe-area-padding overflow-hidden" :class="{'dark': theme === 'dark'}">
<header class="p-3 bg-gray-100 dark:bg-gray-800 shadow-md flex justify-between items-center shrink-0"> <header class="p-3 bg-gray-100 dark:bg-gray-800 shadow-md flex justify-between items-center shrink-0">
<!-- ... header content ... --> <!-- ... header content ... -->
<div class="flex items-center space-x-2"> <div class="flex items-center space-x-2">

View File

@@ -0,0 +1,29 @@
[Unit]
Description=nexus-timer (virt-nexus-timer)
Requires=docker.service
After=docker.service
DefaultDependencies=no
[Service]
Type=simple
Environment="HOME=/root"
ExecStartPre=-/usr/bin/env sh -c '/usr/bin/env docker kill virt-nexus-timer 2>/dev/null || true'
ExecStartPre=-/usr/bin/env sh -c '/usr/bin/env docker rm virt-nexus-timer 2>/dev/null || true'
ExecStart=/usr/bin/env docker run \
--rm \
--name=virt-nexus-timer \
--log-driver=none \
--network=traefik \
--label-file /virt/nexus-timer/labels \
virt-nexus-timer
ExecStop=-/usr/bin/env sh -c '/usr/bin/env docker kill virt-nexus-timer 2>/dev/null || true'
ExecStop=-/usr/bin/env sh -c '/usr/bin/env docker rm virt-nexus-timer 2>/dev/null || true'
Restart=always
RestartSec=30
SyslogIdentifier=virt-nexus-timer
[Install]
WantedBy=multi-user.target