최초 배포

This commit is contained in:
2026-01-27 16:55:03 +09:00
commit 6ebf8e8df7
76 changed files with 5990 additions and 0 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

BIN
frontend/public/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

47
frontend/public/icon.svg Normal file
View File

@@ -0,0 +1,47 @@
<svg width="512" height="512" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="paint0_linear" x1="0" y1="0" x2="512" y2="512" gradientUnits="userSpaceOnUse">
<stop stop-color="#764ba2"/>
<stop offset="1" stop-color="#667eea"/>
</linearGradient>
<filter id="dropshadow" height="130%">
<feGaussianBlur in="SourceAlpha" stdDeviation="15"/>
<feOffset dx="0" dy="10" result="offsetblur"/>
<feComponentTransfer>
<feFuncA type="linear" slope="0.3"/>
</feComponentTransfer>
<feMerge>
<feMergeNode/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
</defs>
<!-- Safe Area Padding applied (approx 10-15%) -->
<g filter="url(#dropshadow)">
<!-- Main Hexagon Shape -->
<!-- Points calculated for 512x512 canvas with padding -->
<path d="M256 50
L433 152
L433 357
L256 460
L79 357
L79 152
Z"
fill="url(#paint0_linear)"
stroke="white"
stroke-width="20"
stroke-linejoin="round"/>
<!-- Internal Cube Lines (Y shape) -->
<path d="M256 460 L256 255" stroke="white" stroke-width="20" stroke-linecap="round"/>
<path d="M256 255 L433 152" stroke="white" stroke-width="20" stroke-linecap="round"/>
<path d="M79 152 L256 255" stroke="white" stroke-width="20" stroke-linecap="round"/>
<!-- Tech Accent Dots -->
<circle cx="256" cy="255" r="30" fill="white"/>
<circle cx="256" cy="110" r="15" fill="white" fill-opacity="0.6"/>
<circle cx="380" cy="320" r="15" fill="white" fill-opacity="0.6"/>
<circle cx="132" cy="320" r="15" fill="white" fill-opacity="0.6"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -0,0 +1,66 @@
// Push notification event handler
// This will be injected into the Service Worker
self.addEventListener('push', function (event) {
console.log('[SW Push] Push event received');
let notificationData = {
title: 'snStatus Alert',
body: 'System alert',
icon: '/pwa-192x192.png',
badge: '/pwa-192x192.png',
vibrate: [200, 100, 200],
requireInteraction: true,
tag: 'snstatus-alert',
data: {
url: '/'
}
};
if (event.data) {
try {
const data = event.data.json();
console.log('[SW Push] Data:', data);
if (data.title) notificationData.title = data.title;
if (data.body) notificationData.body = data.body;
if (data.icon) notificationData.icon = data.icon;
} catch (e) {
console.error('[SW Push] Parse error:', e);
notificationData.body = event.data.text();
}
}
event.waitUntil(
self.registration.showNotification(notificationData.title, {
body: notificationData.body,
icon: notificationData.icon,
badge: notificationData.badge,
vibrate: notificationData.vibrate,
requireInteraction: notificationData.requireInteraction,
tag: notificationData.tag,
data: notificationData.data
})
);
});
self.addEventListener('notificationclick', function (event) {
console.log('[SW] Notification clicked');
event.notification.close();
event.waitUntil(
clients.matchAll({ type: 'window', includeUncontrolled: true })
.then(function (clientList) {
// Focus existing window if available
for (let i = 0; i < clientList.length; i++) {
const client = clientList[i];
if (client.url === '/' && 'focus' in client) {
return client.focus();
}
}
// Open new window
if (clients.openWindow) {
return clients.openWindow('/');
}
})
);
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View File

@@ -0,0 +1,50 @@
// Custom Service Worker for Push Notifications
self.addEventListener('push', function (event) {
console.log('[SW] Push event received:', event);
let notificationData = {
title: 'snStatus Alert',
body: 'System alert triggered',
icon: '/pwa-192x192.png',
badge: '/pwa-192x192.png',
vibrate: [200, 100, 200],
requireInteraction: true,
tag: 'snstatus-alert'
};
if (event.data) {
try {
const data = event.data.json();
console.log('[SW] Push data:', data);
notificationData.title = data.title || notificationData.title;
notificationData.body = data.body || notificationData.body;
notificationData.icon = data.icon || notificationData.icon;
} catch (e) {
console.error('[SW] Error parsing push data:', e);
notificationData.body = event.data.text();
}
}
const promiseChain = self.registration.showNotification(
notificationData.title,
{
body: notificationData.body,
icon: notificationData.icon,
badge: notificationData.badge,
vibrate: notificationData.vibrate,
requireInteraction: notificationData.requireInteraction,
tag: notificationData.tag
}
);
event.waitUntil(promiseChain);
});
self.addEventListener('notificationclick', function (event) {
console.log('[SW] Notification clicked');
event.notification.close();
event.waitUntil(
clients.openWindow('/')
);
});