sw.js.php 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. <?php
  2. /**
  3. * Copyright (c) UNA, Inc - https://una.io
  4. * MIT License - https://opensource.org/licenses/MIT
  5. *
  6. * @defgroup UnaCore UNA Core
  7. * @{
  8. */
  9. define('BX_SERVICE_WORKER', true);
  10. require_once('./inc/header.inc.php');
  11. $oTemplate = BxDolTemplate::getInstance();
  12. $sCacheName = 'app' . $oTemplate->getRevision();
  13. $aAssets = [];
  14. //--- CSS/JS files
  15. foreach(['css' , 'js'] as $sType) {
  16. $aFiles = $oTemplate->includeFiles($sType, true, false);
  17. if(!is_array($aFiles))
  18. $aFiles = [$aFiles];
  19. $aAssets = array_merge($aAssets, $aFiles);
  20. }
  21. if(($sCache = getParam('sys_pwa_sw_cache')) != '') {
  22. $aItems = preg_split("/\\r\\n|\\r|\\n/", $sCache);
  23. if(!empty($aItems) && is_array($aItems))
  24. foreach($aItems as $sItem) {
  25. if(strpos($sItem, BX_DOL_URL_ROOT) === false)
  26. $sItem = BX_DOL_URL_ROOT . $sItem;
  27. $aAssets[] = $sItem;
  28. }
  29. }
  30. //--- Offline page
  31. $sOffline = '';
  32. if(($sOffline = getParam('sys_pwa_sw_offline')) != '') {
  33. $sOffline = BX_DOL_URL_ROOT . $sOffline;
  34. $aAssets[] = $sOffline;
  35. }
  36. //--- Favicon or SVG icon
  37. $sIconUrl = '';
  38. if(($iId = (int)getParam('sys_site_icon')) != 0)
  39. $sIconUrl = BxDolStorage::getObjectInstance(BX_DOL_STORAGE_OBJ_FILES)->getFileUrlById($iId);
  40. if(!$sIconUrl && ($iId = (int)getParam('sys_site_icon_svg')) != 0)
  41. $sIconUrl = BxDolStorage::getObjectInstance(BX_DOL_STORAGE_OBJ_IMAGES)->getFileUrlById($iId);
  42. if(!empty($sIconUrl))
  43. $aAssets[] = $sIconUrl;
  44. //--- Fonts
  45. if(getParam('sys_css_icons_default') != '') {
  46. $sHomeUrl = BX_DOL_URL_ROOT;
  47. if(BxDolModuleQuery::getInstance()->isEnabledByName('bx_fontawesome'))
  48. $sHomeUrl = BxDolModule::getInstance('bx_fontawesome')->_oConfig->getHomeUrl();
  49. $aAssets[] = $sHomeUrl . 'template/fonts/fa-solid-900.woff2';
  50. $aAssets[] = $sHomeUrl . 'template/fonts/fa-regular-400.woff2';
  51. }
  52. header("Content-Type: text/javascript");
  53. ?>
  54. // Core assets
  55. let coreAssets = <?php echo json_encode($aAssets); ?>;
  56. // On install, cache core assets
  57. self.addEventListener('install', function (event) {
  58. self.skipWaiting();
  59. // Cache core assets
  60. event.waitUntil((async () => {
  61. caches.open('<?php echo $sCacheName; ?>').then(function (cache) {
  62. for (let asset of coreAssets) {
  63. cache.add(new Request(asset));
  64. }
  65. return cache;
  66. });
  67. })());
  68. });
  69. const deleteCache = async (key) => {
  70. await caches.delete(key);
  71. };
  72. const deleteOldCaches = async () => {
  73. const cacheKeepList = ["<?php echo $sCacheName; ?>"];
  74. const keyList = await caches.keys();
  75. const cachesToDelete = keyList.filter((key) => !cacheKeepList.includes(key));
  76. await Promise.all(cachesToDelete.map(deleteCache));
  77. };
  78. // delete old cache on activate
  79. self.addEventListener("activate", (event) => {
  80. event.waitUntil(deleteOldCaches());
  81. });
  82. // Listen for request events
  83. self.addEventListener('fetch', function (event) {
  84. // Get the request
  85. let request = event.request;
  86. // Bug fix
  87. // https://stackoverflow.com/a/49719964
  88. if (event.request.cache === 'only-if-cached' && event.request.mode !== 'same-origin') return;
  89. // regular pages and static assets
  90. if (request.mode === 'navigate' || request.url.match(/\.(css|js|woff2|svg|png|jpg)$/)) {
  91. const failedResponse = async (err) => {
  92. let reponse = null;
  93. const sOfflinePage = '<?php echo $sOffline; ?>';
  94. if (request.mode === 'navigate' && sOfflinePage.length != 0) {
  95. const cache = await caches.open('<?php echo $sCacheName; ?>');
  96. reponse = await cache.match(sOfflinePage);
  97. }
  98. return reponse || new Response("Network error happened", {
  99. status: 408,
  100. headers: { "Content-Type": "text/plain" },
  101. });
  102. }
  103. event.respondWith(
  104. caches.match(request).then((response) => {
  105. try {
  106. return response || fetch(request)
  107. .then((response) => {
  108. return response;
  109. })
  110. .catch((err) => {
  111. return failedResponse(err);
  112. });
  113. } catch (error) {
  114. return failedResponse(err);
  115. }
  116. })
  117. );
  118. }
  119. });