Public Source Viewer

비나래아카이브 개발자 포털

실제 서비스 구조를 살펴볼 수 있는 공개용 코드 뷰어입니다. 인증, 세션, 외부 연동, 토큰, 관리자 식별 등 보안상 민감한 구현은 파일 단위 또는 줄 단위로 검열됩니다.

Redacted View
public/sw.js
공개 가능
1 const CACHE_NAME = 'hinana-v2';
2
3 // 앱 설치만 지원하고 캐시는 최소화 (세션 기반 동적 콘텐츠 보호)
4 const STATIC_ASSETS = [
5 '/manifest.json',
6 '/image/title.png'
7 ];
8
9 self.addEventListener('install', (e) => {
10 e.waitUntil(
11 caches.open(CACHE_NAME).then((cache) => cache.addAll(STATIC_ASSETS))
12 );
13 self.skipWaiting();
14 });
15
16 self.addEventListener('activate', (e) => {
17 e.waitUntil(
18 caches.keys().then((keys) =>
19 Promise.all(keys.filter(k => k !== CACHE_NAME).map(k => caches.delete(k)))
20 )
21 );
22 e.waitUntil(clients.claim());
23 });
24
25 // 네트워크 우선: 항상 서버에서 최신 콘텐츠를 가져옴 (로그인 세션 등 보호)
26 self.addEventListener('fetch', (e) => {
27 e.respondWith(fetch(e.request).catch(() => caches.match(e.request)));
28 });
29
30 // 푸시 알림 수신
31 self.addEventListener('push', (e) => {
32 let data = {};
33 try { data = e.data ? e.data.json() : {}; } catch { data = { body: e.data ? e.data.text() : '' }; }
34
35 const title = data.title || '비나래 아카이브';
36 const options = {
37 body: data.body || '',
38 icon: data.icon || '/image/title.png',
39 badge: '/image/title.png',
40 data: { url: data.url || '/hinana/index' }
41 };
42 if (data.tag) options.tag = data.tag;
43 if (typeof data.renotify === 'boolean') options.renotify = data.renotify;
44 if (typeof data.silent === 'boolean') options.silent = data.silent;
45 if (typeof data.requireInteraction === 'boolean') options.requireInteraction = data.requireInteraction;
46 if (!options.silent) options.vibrate = [200, 100, 200];
47 e.waitUntil(self.registration.showNotification(title, options));
48 });
49
50 // 알림 클릭 시 해당 게시글로 이동
51 self.addEventListener('notificationclick', (e) => {
52 e.notification.close();
53 const targetUrl = (e.notification.data && e.notification.data.url) || '/hinana/index';
54 e.waitUntil(
55 clients.matchAll({ type: 'window', includeUncontrolled: true }).then((clientList) => {
56 for (const client of clientList) {
57 if (client.url.includes('/hinana') && 'focus' in client) {
58 client.navigate(targetUrl);
59 return client.focus();
60 }
61 }
62 if (clients.openWindow) return clients.openWindow(targetUrl);
63 })
64 );
65 });
66