diff --git a/.gitea/workflows/build-and-deploy.yml b/.gitea/workflows/build-and-deploy.yml index 65b118c..d4ec18b 100644 --- a/.gitea/workflows/build-and-deploy.yml +++ b/.gitea/workflows/build-and-deploy.yml @@ -19,6 +19,8 @@ jobs: # Authentik configuration NUXT_PUBLIC_AUTHENTIK_URL: ${{ vars.NUXT_PUBLIC_AUTHENTIK_URL }} NUXT_PUBLIC_AUTH_ENABLED: ${{ vars.NUXT_PUBLIC_AUTH_ENABLED }} + # MCP Server authentication + MCP_AUTH_TOKEN: ${{ secrets.MCP_AUTH_TOKEN }} steps: - name: Build, Push and Deploy run: | diff --git a/docker-compose.yml b/docker-compose.yml index 8ac8210..67c77e0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -18,6 +18,8 @@ services: # Authentik configuration - NUXT_PUBLIC_AUTHENTIK_URL=${NUXT_PUBLIC_AUTHENTIK_URL:-https://authentik.nucleoriofrio.com} - NUXT_PUBLIC_AUTH_ENABLED=${NUXT_PUBLIC_AUTH_ENABLED:-false} + # MCP Server token + - MCP_AUTH_TOKEN=${MCP_AUTH_TOKEN:-} volumes: - printercentral-data:/app/data networks: @@ -32,7 +34,7 @@ services: # Router para assets estaticos de Nuxt y PWA (sin autenticacion) - mayor prioridad # Incluye recursos PWA y .well-known para scope extensions - - "traefik.http.routers.${APP_NAME}-public.rule=Host(`${APP_DOMAIN}`) && (PathPrefix(`/_nuxt`) || PathPrefix(`/.well-known`) || PathPrefix(`/icons`) || Path(`/manifest.webmanifest`) || Path(`/sw.js`) || PathPrefix(`/workbox-`) || Path(`/favicon.ico`) || Path(`/offline.html`))" + - "traefik.http.routers.${APP_NAME}-public.rule=Host(`${APP_DOMAIN}`) && (PathPrefix(`/_nuxt`) || PathPrefix(`/.well-known`) || PathPrefix(`/icons`) || Path(`/manifest.webmanifest`) || Path(`/sw.js`) || PathPrefix(`/workbox-`) || Path(`/favicon.ico`) || Path(`/offline.html`) || PathPrefix(`/api/mcp`))" - "traefik.http.routers.${APP_NAME}-public.entrypoints=websecure" - "traefik.http.routers.${APP_NAME}-public.tls=true" - "traefik.http.routers.${APP_NAME}-public.tls.certresolver=letsencrypt" diff --git a/pwa/icon.png b/pwa/icon.png new file mode 100644 index 0000000..18170a4 Binary files /dev/null and b/pwa/icon.png differ diff --git a/pwa/icons/icon-128.png b/pwa/icons/icon-128.png new file mode 100644 index 0000000..69465ef Binary files /dev/null and b/pwa/icons/icon-128.png differ diff --git a/pwa/icons/icon-144.png b/pwa/icons/icon-144.png new file mode 100644 index 0000000..186bc2d Binary files /dev/null and b/pwa/icons/icon-144.png differ diff --git a/pwa/icons/icon-152.png b/pwa/icons/icon-152.png new file mode 100644 index 0000000..ed534b0 Binary files /dev/null and b/pwa/icons/icon-152.png differ diff --git a/pwa/icons/icon-192.png b/pwa/icons/icon-192.png new file mode 100644 index 0000000..8cad616 Binary files /dev/null and b/pwa/icons/icon-192.png differ diff --git a/pwa/icons/icon-384.png b/pwa/icons/icon-384.png new file mode 100644 index 0000000..89adc10 Binary files /dev/null and b/pwa/icons/icon-384.png differ diff --git a/pwa/icons/icon-512.png b/pwa/icons/icon-512.png new file mode 100644 index 0000000..1893d4b Binary files /dev/null and b/pwa/icons/icon-512.png differ diff --git a/pwa/icons/icon-72.png b/pwa/icons/icon-72.png new file mode 100644 index 0000000..7d98253 Binary files /dev/null and b/pwa/icons/icon-72.png differ diff --git a/pwa/icons/icon-96.png b/pwa/icons/icon-96.png new file mode 100644 index 0000000..ecd10c0 Binary files /dev/null and b/pwa/icons/icon-96.png differ diff --git a/pwa/index.html b/pwa/index.html new file mode 100644 index 0000000..a7a3a98 --- /dev/null +++ b/pwa/index.html @@ -0,0 +1,37 @@ + + + + + + Printer Central + + + + + + +
+ +

Bienvenido a Printer Central

+

Administra tus impresoras de forma fácil y rápida.

+ +
+ + + + diff --git a/pwa/manifest.json b/pwa/manifest.json new file mode 100644 index 0000000..a0436e2 --- /dev/null +++ b/pwa/manifest.json @@ -0,0 +1,51 @@ +{ + "name": "Printer Central", + "short_name": "Printer", + "description": "Manage your printers from one central app.", + "start_url": "index.html", + "display": "standalone", + "background_color": "#ffffff", + "theme_color": "#3f75d2", + "icons": [ + { + "src": "icons/icon-72.png", + "sizes": "72x72", + "type": "image/png" + }, + { + "src": "icons/icon-96.png", + "sizes": "96x96", + "type": "image/png" + }, + { + "src": "icons/icon-128.png", + "sizes": "128x128", + "type": "image/png" + }, + { + "src": "icons/icon-144.png", + "sizes": "144x144", + "type": "image/png" + }, + { + "src": "icons/icon-152.png", + "sizes": "152x152", + "type": "image/png" + }, + { + "src": "icons/icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "icons/icon-384.png", + "sizes": "384x384", + "type": "image/png" + }, + { + "src": "icons/icon-512.png", + "sizes": "512x512", + "type": "image/png" + } + ] +} \ No newline at end of file diff --git a/pwa/sw.js b/pwa/sw.js new file mode 100644 index 0000000..bacf9dd --- /dev/null +++ b/pwa/sw.js @@ -0,0 +1,49 @@ +const CACHE_NAME = 'printer-central-cache-v1'; +const urlsToCache = [ + 'index.html', + 'manifest.json', + 'icon.png', + 'icons/icon-72.png', + 'icons/icon-96.png', + 'icons/icon-128.png', + 'icons/icon-144.png', + 'icons/icon-152.png', + 'icons/icon-192.png', + 'icons/icon-384.png', + 'icons/icon-512.png' +]; + +self.addEventListener('install', event => { + event.waitUntil( + caches.open(CACHE_NAME) + .then(cache => { + return cache.addAll(urlsToCache); + }) + ); +}); + +self.addEventListener('fetch', event => { + event.respondWith( + caches.match(event.request) + .then(response => { + // Cache hit - return response + if (response) { + return response; + } + return fetch(event.request); + }) + ); +}); + +self.addEventListener('activate', event => { + const cacheWhitelist = [CACHE_NAME]; + event.waitUntil( + caches.keys().then(keyList => { + return Promise.all(keyList.map(key => { + if (cacheWhitelist.indexOf(key) === -1) { + return caches.delete(key); + } + })); + }) + ); +}); diff --git a/server/api/mcp/index.post.ts b/server/api/mcp/index.post.ts index 4953405..585cc0f 100644 --- a/server/api/mcp/index.post.ts +++ b/server/api/mcp/index.post.ts @@ -22,6 +22,18 @@ interface JsonRpcResponse { } export default defineEventHandler(async (event) => { + // Validar token de autenticación + const authHeader = getHeader(event, 'Authorization') + const expectedToken = process.env.MCP_AUTH_TOKEN + + if (expectedToken) { + const providedToken = authHeader?.replace('Bearer ', '') + if (!providedToken || providedToken !== expectedToken) { + setResponseStatus(event, 401) + return createJsonRpcError(null, -32000, 'Unauthorized: Invalid or missing token') + } + } + try { const body = await readBody(event) as JsonRpcRequest