Public Source Viewer
비나래아카이브 개발자 포털
실제 서비스 구조를 살펴볼 수 있는 공개용 코드 뷰어입니다. 인증, 세션, 외부 연동, 토큰, 관리자 식별 등 보안상 민감한 구현은 파일 단위 또는 줄 단위로 검열됩니다.
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