mirror of
https://git.hmsn.ink/kospo/helptalk/api.git
synced 2026-03-20 01:02:21 +09:00
first
This commit is contained in:
290
sample/dev/service-worker.js
Normal file
290
sample/dev/service-worker.js
Normal file
@@ -0,0 +1,290 @@
|
||||
const CACHE_NAME = 'v1';
|
||||
importScripts('http://devtalk.kospo.co.kr:3000/static/js/module/stomp.js');
|
||||
|
||||
let sockJS = null;
|
||||
let stompClient = null;
|
||||
let reConnectAttempts = 0
|
||||
let maxReconnectAttempts = 3
|
||||
// 마스터 탭 변수
|
||||
let connTab = null
|
||||
|
||||
let messageList = [];
|
||||
let forceConnect = false;
|
||||
// 서비스워커 체커 마지막 시간 변수
|
||||
let lastHeartbeat = null;
|
||||
|
||||
/*재귀 함수 변수*/
|
||||
let checker;
|
||||
let connectLock = false;
|
||||
|
||||
let subscribe = null;
|
||||
|
||||
// indexeddb 데이터 조회
|
||||
function getData(col, val) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (connTab[col] === val) {
|
||||
resolve(connTab);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// indexeddb 데이터 추가
|
||||
function addData(obj) {
|
||||
connTab = obj;
|
||||
}
|
||||
|
||||
function delData(tabId) {
|
||||
return new Promise((resolve, reject) => {
|
||||
messageList.forEach((message, idx) => {
|
||||
if (message.tabId === tabId) {
|
||||
messageList.splice(idx, 1);
|
||||
}
|
||||
})
|
||||
if (connTab) {
|
||||
if (connTab.tabId === tabId) {
|
||||
connTab = null
|
||||
}
|
||||
}
|
||||
|
||||
resolve();
|
||||
})
|
||||
}
|
||||
|
||||
async function getClientCount(url) {
|
||||
const allClients = await clients.matchAll({
|
||||
includeUncontrolled: true,
|
||||
type: 'window',
|
||||
});
|
||||
|
||||
|
||||
const cc = allClients.filter(client => client.url === url);
|
||||
console.log(cc, '해당 클라이언트가 하나만 나와야함')
|
||||
return cc.length;
|
||||
}
|
||||
|
||||
// 클라이언트 에게 메시지 전달
|
||||
async function hubToClients(message) {
|
||||
const allClients = await clients.matchAll({
|
||||
includeUncontrolled: true,
|
||||
type: 'window',
|
||||
});
|
||||
|
||||
allClients.forEach(client => {
|
||||
client.postMessage(message);
|
||||
});
|
||||
}
|
||||
|
||||
// 브라우저 종료시 소켓 연결 해지
|
||||
async function disConnectWebSocket(message) {
|
||||
delData(message.tabId).then(() => {
|
||||
console.log('disConnect', connTab, message.tabId)
|
||||
if (connTab === null) {
|
||||
try {
|
||||
allClose();
|
||||
if (messageList.length !== 0) connectWebSocket(messageList[0])
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// 모든 클라이언트가 종료시 서비스워커 heartbeat 제거
|
||||
const allClients = await clients.matchAll({
|
||||
includeUncontrolled: true,
|
||||
type: 'window',
|
||||
});
|
||||
|
||||
if(allClients.length === 1) {
|
||||
clearInterval(checker)
|
||||
checker = null;
|
||||
}
|
||||
}
|
||||
|
||||
// 브라우저 종료시 소켓 연결 해지
|
||||
async function kill() {
|
||||
console.log('service worker kill');
|
||||
connTab = null;
|
||||
messageList = [];
|
||||
clearInterval(checker)
|
||||
checker = null;
|
||||
allClose();
|
||||
}
|
||||
|
||||
// 탭간 공유 웹소켓 연결 관리
|
||||
async function connectWebSocket(message) {
|
||||
getClientCount(message.site).then((windowClientCount) => {
|
||||
if(windowClientCount === 1) {
|
||||
connTab = null
|
||||
messageList = []
|
||||
}
|
||||
|
||||
lastHeartbeat = Date.now();
|
||||
|
||||
if (connTab === null || sockJS.readyState !== 1) {
|
||||
|
||||
if(sockJS !== null) {
|
||||
if(sockJS.readyState === WebSocket.OPEN) {
|
||||
allClose();
|
||||
}
|
||||
}
|
||||
|
||||
sockJS = new WebSocket(`ws://devtalk.kospo.co.kr:3000/stomp/ws`);
|
||||
// sockJS = new SockJS(
|
||||
// `http://devtalk.kospo.co.kr:3000/stomp/talk`, {
|
||||
// Authentication: message.encSabun
|
||||
// }, {
|
||||
// transports: ['websocket'],
|
||||
// reconnectInterval: 500,
|
||||
// reconnectDelay: 500,
|
||||
// },
|
||||
// );
|
||||
|
||||
if(sockJS !== WebSocket.CLOSED && sockJS !== WebSocket.CLOSING) {
|
||||
stompClient = Stomp.over(sockJS);
|
||||
stompClient.heartbeat.outgoing = 20000;
|
||||
stompClient.heartbeat.incoming = 20000;
|
||||
stompClient.debug = (e) => {
|
||||
if (e.includes('ERROR') || e.includes('Exception') || e.includes('failed')) {
|
||||
console.log(new Date().toString())
|
||||
console.error('STOMP error:', e);
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
stompClient.connect({Authentication: message.encSabun, location: 'SW'}, function (frame) {
|
||||
console.log('hub connect', message.tabId)
|
||||
subscribe = stompClient.subscribe(`/exchange/hub.exchange/hub.${message.tabId}`, async function (content) {
|
||||
const payload = JSON.parse(content.body);
|
||||
hubToClients(payload)
|
||||
|
||||
})
|
||||
|
||||
/*사이트 정보 탭 아이디 저장*/
|
||||
addData({
|
||||
url: message.site,
|
||||
tabId: message.tabId
|
||||
})
|
||||
|
||||
/* 여러개 탭이 활성화시 방지용 서비스워커 체커*/
|
||||
if(checker == null) {
|
||||
/*테스트*/
|
||||
checker = setInterval(() => {
|
||||
// lastHeartbeat = Date.now();
|
||||
// console.log('서비스워커 실행중', new Date())
|
||||
hubToClients({type:"PING"})
|
||||
}, 10000)
|
||||
} else {
|
||||
console.log('checker and reset is exist')
|
||||
}
|
||||
|
||||
|
||||
// 재연결 시도 초기화
|
||||
reConnectAttempts = 0;
|
||||
})
|
||||
}
|
||||
|
||||
const baseDelay = 500
|
||||
sockJS.onclose = (e) => {
|
||||
if(e.code !== 1000) {
|
||||
const delay = Math.min(baseDelay * Math.pow(2, reConnectAttempts), 5000)
|
||||
setTimeout(() => {
|
||||
hubReconnect()
|
||||
if(reConnectAttempts < maxReconnectAttempts) {
|
||||
reConnectAttempts++;
|
||||
} else {
|
||||
console.log('[Service Worker Hub] Max Reconnect attempts reached.')
|
||||
}
|
||||
}, delay)
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
console.log('hub socket is connected')
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const hubReconnect = () => {
|
||||
allClose()
|
||||
clearInterval(checker)
|
||||
checker = null;
|
||||
hubToClients({type:'HUB_RECONNECT'});
|
||||
lastHeartbeat = Date.now();
|
||||
}
|
||||
|
||||
// 서비스워커 설치
|
||||
self.addEventListener('install', (event) => {
|
||||
console.log('SW: Installing...');
|
||||
event.waitUntil(
|
||||
self.skipWaiting(),
|
||||
);
|
||||
});
|
||||
|
||||
// 서비스워커 활성화
|
||||
self.addEventListener('activate', function activator(event) {
|
||||
// delData();
|
||||
event.waitUntil(
|
||||
self.clients.claim()
|
||||
);
|
||||
})
|
||||
|
||||
// 클라이언트로부터의 메시지 처리 (연결 및 해지)
|
||||
self.addEventListener('message', async (event) => {
|
||||
const message = event.data;
|
||||
switch (message.type) {
|
||||
case 'CONNECT':
|
||||
connectLock = false;
|
||||
messageList.push(message);
|
||||
connectWebSocket(message);
|
||||
break;
|
||||
case 'TAB_CLOSE':
|
||||
disConnectWebSocket(message);
|
||||
break;
|
||||
case "KILL" :
|
||||
kill();
|
||||
break;
|
||||
case 'PONG' :
|
||||
if (stompClient === null) {
|
||||
hubReconnect()
|
||||
}
|
||||
break;
|
||||
case "WAKEUP" :
|
||||
if (stompClient === null) {
|
||||
if (!connectLock) {
|
||||
connectLock = true;
|
||||
hubReconnect()
|
||||
}
|
||||
} else {
|
||||
if (!connectLock) {
|
||||
hubToClients({type: "GETUP"})
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
case 'HUB_RECONNECT_SUCCESS' :
|
||||
let suc = true;
|
||||
while (suc) {
|
||||
if (stompClient.connected) {
|
||||
connectLock = false;
|
||||
suc = false;
|
||||
}
|
||||
await sleep(100)
|
||||
}
|
||||
break;
|
||||
case 'FONT_CHANGE':
|
||||
hubToClients(message)
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
const sleep = (delay) => {
|
||||
return new Promise(resolve => setTimeout(resolve, delay));
|
||||
}
|
||||
|
||||
const allClose = () => {
|
||||
try{subscribe.unsubscribe()} catch(e) {}
|
||||
try{stompClient.disconnect()} catch(e) {}
|
||||
try{sockJS.close();} catch(e) {}
|
||||
stompClient = null;
|
||||
sockJS = null;
|
||||
subscribe = null;
|
||||
}
|
||||
Reference in New Issue
Block a user