diff --git a/.github/workflows/build-mips.yml b/.github/workflows/build-mips.yml deleted file mode 100644 index 4f1deb3..0000000 --- a/.github/workflows/build-mips.yml +++ /dev/null @@ -1,80 +0,0 @@ -name: Build MIPS Binary - -on: - push: - branches: [ "main" ] - workflow_dispatch: - -permissions: - contents: write - -env: - CARGO_TERM_COLOR: always - -jobs: - build: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - - - name: Debug - List Files (Pre-Build) - run: ls -R - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: '20' - - - name: Install Rust - uses: dtolnay/rust-toolchain@nightly - with: - targets: wasm32-unknown-unknown - components: rust-src - - - name: Rust Cache - uses: Swatinem/rust-cache@v2 - - - name: Install Trunk - uses: jetli/trunk-action@v0.5.0 - with: - version: 'latest' - - - name: Build Frontend - run: | - cd frontend - npm install - trunk build --release - - - name: Install Cross - run: cargo install cross - - - name: Build Backend (MIPS) - env: - RUSTUP_TOOLCHAIN: nightly - CROSS_NO_WARNINGS: 0 - run: | - cd backend - cross build --target mips-unknown-linux-musl --release -Z build-std=std,panic_abort - - - name: Debug - List Files - run: | - echo "Listing target directory..." - find target -maxdepth 5 || true - ls -R target/mips-unknown-linux-musl/release || true - - - name: Rename Binary - run: mv target/mips-unknown-linux-musl/release/backend target/mips-unknown-linux-musl/release/vibetorrent-mips - - - name: Generate Tag - id: tag - run: echo "release_tag=release-$(date +'%Y%m%d-%H%M')" >> $GITHUB_OUTPUT - - - name: Create Release - uses: softprops/action-gh-release@v1 - with: - tag_name: ${{ steps.tag.outputs.release_tag }} - name: Release ${{ steps.tag.outputs.release_tag }} - files: target/mips-unknown-linux-musl/release/vibetorrent-mips - draft: false - prerelease: false diff --git a/docker/runner/Dockerfile b/docker/runner/Dockerfile index bb24c0d..800adf3 100644 --- a/docker/runner/Dockerfile +++ b/docker/runner/Dockerfile @@ -16,6 +16,8 @@ RUN apt-get update && apt-get install -y \ xz-utils \ libssl-dev \ pkg-config \ + file \ + jq \ # Needed for some crate compilations protobuf-compiler \ && rm -rf /var/lib/apt/lists/* diff --git a/frontend/sw.js b/frontend/sw.js index cdeb36a..62a2eaf 100644 --- a/frontend/sw.js +++ b/frontend/sw.js @@ -1,127 +1,158 @@ -const CACHE_NAME = 'vibetorrent-v1'; +const CACHE_NAME = "vibetorrent-v2"; const ASSETS_TO_CACHE = [ - '/', - '/index.html', - '/manifest.json', - '/icon-192.png', - '/icon-512.png' + "/", + "/index.html", + "/manifest.json", + "/icon-192.png", + "/icon-512.png", ]; // Install event - cache assets -self.addEventListener('install', (event) => { - console.log('[Service Worker] Installing...'); - event.waitUntil( - caches.open(CACHE_NAME).then((cache) => { - console.log('[Service Worker] Caching static assets'); - return cache.addAll(ASSETS_TO_CACHE); - }).then(() => { - console.log('[Service Worker] Skip waiting'); - return self.skipWaiting(); - }) - ); +self.addEventListener("install", (event) => { + console.log("[Service Worker] Installing..."); + event.waitUntil( + caches + .open(CACHE_NAME) + .then((cache) => { + console.log("[Service Worker] Caching static assets"); + return cache.addAll(ASSETS_TO_CACHE); + }) + .then(() => { + console.log("[Service Worker] Skip waiting"); + return self.skipWaiting(); + }), + ); }); // Activate event - clean old caches -self.addEventListener('activate', (event) => { - console.log('[Service Worker] Activating...'); - event.waitUntil( - caches.keys().then((cacheNames) => { - return Promise.all( - cacheNames.map((key) => { - if (key !== CACHE_NAME) { - console.log('[Service Worker] Deleting old cache:', key); - return caches.delete(key); - } - }) - ); - }).then(() => { - console.log('[Service Worker] Claiming clients'); - return self.clients.claim(); - }) - ); +self.addEventListener("activate", (event) => { + console.log("[Service Worker] Activating..."); + event.waitUntil( + caches + .keys() + .then((cacheNames) => { + return Promise.all( + cacheNames.map((key) => { + if (key !== CACHE_NAME) { + console.log("[Service Worker] Deleting old cache:", key); + return caches.delete(key); + } + }), + ); + }) + .then(() => { + console.log("[Service Worker] Claiming clients"); + return self.clients.claim(); + }), + ); }); -// Fetch event - network first, cache fallback for API calls -self.addEventListener('fetch', (event) => { - const url = new URL(event.request.url); - - // Network-first strategy for API calls - if (url.pathname.startsWith('/api/')) { - event.respondWith( - fetch(event.request) - .catch(() => { - // Could return cached API response or offline page - return new Response( - JSON.stringify({ error: 'Offline' }), - { headers: { 'Content-Type': 'application/json' } } - ); - }) - ); - return; - } - - // Cache-first strategy for static assets +// Fetch event - network first for HTML, cache fallback for API calls +self.addEventListener("fetch", (event) => { + const url = new URL(event.request.url); + + // Network-first strategy for API calls + if (url.pathname.startsWith("/api/")) { event.respondWith( - caches.match(event.request).then((response) => { - return response || fetch(event.request).then((fetchResponse) => { - // Optionally cache new requests - if (fetchResponse && fetchResponse.status === 200) { - const responseToCache = fetchResponse.clone(); - caches.open(CACHE_NAME).then((cache) => { - cache.put(event.request, responseToCache); - }); - } - return fetchResponse; - }); - }) + fetch(event.request).catch(() => { + // Could return cached API response or offline page + return new Response(JSON.stringify({ error: "Offline" }), { + headers: { "Content-Type": "application/json" }, + }); + }), ); + return; + } + + // Network-first strategy for HTML pages (entry points) + // This ensures users always get the latest version of the app + if ( + event.request.mode === "navigate" || + url.pathname.endsWith("index.html") || + url.pathname === "/" + ) { + event.respondWith( + fetch(event.request) + .then((response) => { + // Cache the latest version of the HTML + const responseToCache = response.clone(); + caches.open(CACHE_NAME).then((cache) => { + cache.put(event.request, responseToCache); + }); + return response; + }) + .catch(() => { + return caches.match(event.request); + }), + ); + return; + } + + // Cache-first strategy for static assets (JS, CSS, Images) + event.respondWith( + caches.match(event.request).then((response) => { + return ( + response || + fetch(event.request).then((fetchResponse) => { + // Optionally cache new requests + if (fetchResponse && fetchResponse.status === 200) { + const responseToCache = fetchResponse.clone(); + caches.open(CACHE_NAME).then((cache) => { + cache.put(event.request, responseToCache); + }); + } + return fetchResponse; + }) + ); + }), + ); }); // Notification click event - focus or open app -self.addEventListener('notificationclick', (event) => { - console.log('[Service Worker] Notification clicked:', event.notification.tag); - event.notification.close(); - - event.waitUntil( - clients.matchAll({ type: 'window', includeUncontrolled: true }).then((clientList) => { - // If app is already open, focus it - for (let client of clientList) { - if (client.url === '/' && 'focus' in client) { - return client.focus(); - } - } - // Otherwise open new window - if (clients.openWindow) { - return clients.openWindow('/'); - } - }) - ); +self.addEventListener("notificationclick", (event) => { + console.log("[Service Worker] Notification clicked:", event.notification.tag); + event.notification.close(); + + event.waitUntil( + clients + .matchAll({ type: "window", includeUncontrolled: true }) + .then((clientList) => { + // If app is already open, focus it + for (let client of clientList) { + if (client.url === "/" && "focus" in client) { + return client.focus(); + } + } + // Otherwise open new window + if (clients.openWindow) { + return clients.openWindow("/"); + } + }), + ); }); // Push notification event -self.addEventListener('push', (event) => { - console.log('[Service Worker] Push notification received'); - const data = event.data ? event.data.json() : {}; - - const title = data.title || 'VibeTorrent'; - const options = { - body: data.body || 'New notification', - icon: data.icon || '/icon-192.png', - badge: data.badge || '/icon-192.png', - tag: data.tag || 'vibetorrent-notification', - requireInteraction: false, - // iOS-specific: vibrate pattern (if supported) - vibrate: [200, 100, 200], - // Add data for notification click handling - data: { - url: data.url || '/', - timestamp: Date.now() - } - }; - - console.log('[Service Worker] Showing notification:', title, options); - - event.waitUntil( - self.registration.showNotification(title, options) - ); +self.addEventListener("push", (event) => { + console.log("[Service Worker] Push notification received"); + const data = event.data ? event.data.json() : {}; + + const title = data.title || "VibeTorrent"; + const options = { + body: data.body || "New notification", + icon: data.icon || "/icon-192.png", + badge: data.badge || "/icon-192.png", + tag: data.tag || "vibetorrent-notification", + requireInteraction: false, + // iOS-specific: vibrate pattern (if supported) + vibrate: [200, 100, 200], + // Add data for notification click handling + data: { + url: data.url || "/", + timestamp: Date.now(), + }, + }; + + console.log("[Service Worker] Showing notification:", title, options); + + event.waitUntil(self.registration.showNotification(title, options)); });