Add service worker
parent
996be0f3ad
commit
d3c771fb31
|
@ -32,5 +32,8 @@ return [
|
||||||
|
|
||||||
// Config API
|
// Config API
|
||||||
['name' => 'api#setUserConfig', 'url' => '/api/config/{key}', 'verb' => 'PUT'],
|
['name' => 'api#setUserConfig', 'url' => '/api/config/{key}', 'verb' => 'PUT'],
|
||||||
|
|
||||||
|
// Service worker
|
||||||
|
['name' => 'api#serviceWorker', 'url' => '/service-worker.js', 'verb' => 'GET'],
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
|
@ -33,6 +33,8 @@ use OCP\App\IAppManager;
|
||||||
use OCP\AppFramework\Controller;
|
use OCP\AppFramework\Controller;
|
||||||
use OCP\AppFramework\Http;
|
use OCP\AppFramework\Http;
|
||||||
use OCP\AppFramework\Http\JSONResponse;
|
use OCP\AppFramework\Http\JSONResponse;
|
||||||
|
use OCP\AppFramework\Http\StreamResponse;
|
||||||
|
use OCP\AppFramework\Http\ContentSecurityPolicy;
|
||||||
use OCP\Files\IRootFolder;
|
use OCP\Files\IRootFolder;
|
||||||
use OCP\IConfig;
|
use OCP\IConfig;
|
||||||
use OCP\IDBConnection;
|
use OCP\IDBConnection;
|
||||||
|
@ -637,4 +639,22 @@ class ApiController extends Controller {
|
||||||
$this->config->setUserValue($userId, Application::APPNAME, $key, $value);
|
$this->config->setUserValue($userId, Application::APPNAME, $key, $value);
|
||||||
return new JSONResponse([], Http::STATUS_OK);
|
return new JSONResponse([], Http::STATUS_OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @NoAdminRequired
|
||||||
|
* @NoCSRFRequired
|
||||||
|
*/
|
||||||
|
public function serviceWorker(): StreamResponse {
|
||||||
|
$response = new StreamResponse(__DIR__.'/../../js/memories-service-worker.js');
|
||||||
|
$response->setHeaders([
|
||||||
|
'Content-Type' => 'application/javascript',
|
||||||
|
'Service-Worker-Allowed' => '/'
|
||||||
|
]);
|
||||||
|
$policy = new ContentSecurityPolicy();
|
||||||
|
$policy->addAllowedWorkerSrcDomain("'self'");
|
||||||
|
$policy->addAllowedScriptDomain("'self'");
|
||||||
|
$policy->addAllowedConnectDomain("'self'");
|
||||||
|
$response->setContentSecurityPolicy($policy);
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -9,6 +9,7 @@ use OCA\Files\Event\LoadSidebar;
|
||||||
use OCP\AppFramework\Controller;
|
use OCP\AppFramework\Controller;
|
||||||
use OCP\App\IAppManager;
|
use OCP\App\IAppManager;
|
||||||
use OCP\EventDispatcher\IEventDispatcher;
|
use OCP\EventDispatcher\IEventDispatcher;
|
||||||
|
use OCP\AppFramework\Http\ContentSecurityPolicy;
|
||||||
use OCP\IConfig;
|
use OCP\IConfig;
|
||||||
use OCP\IUserSession;
|
use OCP\IUserSession;
|
||||||
use OCP\Util;
|
use OCP\Util;
|
||||||
|
@ -71,7 +72,12 @@ class PageController extends Controller {
|
||||||
$this->initialState->provideInitialState('recognize', $this->appManager->isEnabledForUser('recognize') === true);
|
$this->initialState->provideInitialState('recognize', $this->appManager->isEnabledForUser('recognize') === true);
|
||||||
$this->initialState->provideInitialState('version', $this->appManager->getAppInfo('memories')["version"]);
|
$this->initialState->provideInitialState('version', $this->appManager->getAppInfo('memories')["version"]);
|
||||||
|
|
||||||
|
$policy = new ContentSecurityPolicy();
|
||||||
|
$policy->addAllowedWorkerSrcDomain("'self'");
|
||||||
|
$policy->addAllowedScriptDomain("'self'");
|
||||||
|
|
||||||
$response = new TemplateResponse($this->appName, 'main');
|
$response = new TemplateResponse($this->appName, 'main');
|
||||||
|
$response->setContentSecurityPolicy($policy);
|
||||||
return $response;
|
return $response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -65,6 +65,7 @@
|
||||||
"@types/url-parse": "^1.4.8",
|
"@types/url-parse": "^1.4.8",
|
||||||
"playwright": "^1.27.1",
|
"playwright": "^1.27.1",
|
||||||
"ts-loader": "^9.4.1",
|
"ts-loader": "^9.4.1",
|
||||||
"typescript": "^4.8.4"
|
"typescript": "^4.8.4",
|
||||||
|
"workbox-webpack-plugin": "^6.5.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
18
src/App.vue
18
src/App.vue
|
@ -57,6 +57,7 @@ import {
|
||||||
NcContent, NcAppContent, NcAppNavigation,
|
NcContent, NcAppContent, NcAppNavigation,
|
||||||
NcAppNavigationItem, NcAppNavigationSettings,
|
NcAppNavigationItem, NcAppNavigationSettings,
|
||||||
} from '@nextcloud/vue';
|
} from '@nextcloud/vue';
|
||||||
|
import { generateUrl } from '@nextcloud/router'
|
||||||
|
|
||||||
import Timeline from './components/Timeline.vue'
|
import Timeline from './components/Timeline.vue'
|
||||||
import Settings from './components/Settings.vue'
|
import Settings from './components/Settings.vue'
|
||||||
|
@ -104,6 +105,23 @@ export default class App extends Mixins(GlobalMixin, UserConfig) {
|
||||||
contentVue.classList.add('nextcloud-major-' + version[0]);
|
contentVue.classList.add('nextcloud-major-' + version[0]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async beforeMount() {
|
||||||
|
if ('serviceWorker' in navigator) {
|
||||||
|
// Use the window load event to keep the page load performant
|
||||||
|
window.addEventListener('load', async () => {
|
||||||
|
try {
|
||||||
|
const url = generateUrl('/apps/memories/service-worker.js');
|
||||||
|
const registration = await navigator.serviceWorker.register(url, { scope: generateUrl('/apps/memories') });
|
||||||
|
console.log('SW registered: ', registration);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('SW registration failed: ', error);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
console.debug('Service Worker is not enabled on this browser.')
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
43
webpack.js
43
webpack.js
|
@ -1,6 +1,10 @@
|
||||||
const webpackConfig = require('@nextcloud/webpack-vue-config')
|
const webpackConfig = require('@nextcloud/webpack-vue-config')
|
||||||
|
const WorkboxPlugin = require('workbox-webpack-plugin')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
|
|
||||||
|
const buildMode = process.env.NODE_ENV
|
||||||
|
const isDev = buildMode === 'development'
|
||||||
|
|
||||||
webpackConfig.module.rules.push({
|
webpackConfig.module.rules.push({
|
||||||
test: /\.ts?$/,
|
test: /\.ts?$/,
|
||||||
loader: 'ts-loader',
|
loader: 'ts-loader',
|
||||||
|
@ -21,4 +25,43 @@ webpackConfig.watchOptions = {
|
||||||
aggregateTimeout: 300,
|
aggregateTimeout: 300,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (!isDev) {
|
||||||
|
webpackConfig.plugins.push(
|
||||||
|
new WorkboxPlugin.GenerateSW({
|
||||||
|
swDest: 'memories-service-worker.js',
|
||||||
|
clientsClaim: true,
|
||||||
|
skipWaiting: true,
|
||||||
|
exclude: [new RegExp('.*')], // don't do precaching
|
||||||
|
inlineWorkboxRuntime: true,
|
||||||
|
sourcemap: false,
|
||||||
|
|
||||||
|
// Define runtime caching rules.
|
||||||
|
runtimeCaching: [{
|
||||||
|
// Match any preview file request
|
||||||
|
urlPattern: /^.*\/core\/preview\?fileId=.*/,
|
||||||
|
handler: 'CacheFirst',
|
||||||
|
|
||||||
|
options: {
|
||||||
|
cacheName: 'images',
|
||||||
|
expiration: {
|
||||||
|
maxAgeSeconds: 3600 * 24 * 7, // one week
|
||||||
|
maxEntries: 20000, // 20k images
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
// Match page requests
|
||||||
|
urlPattern: /^.*\/.*$/,
|
||||||
|
handler: 'NetworkFirst',
|
||||||
|
options: {
|
||||||
|
cacheName: 'pages',
|
||||||
|
expiration: {
|
||||||
|
maxAgeSeconds: 3600 * 24 * 7, // one week
|
||||||
|
maxEntries: 2000, // assets
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = webpackConfig
|
module.exports = webpackConfig
|
||||||
|
|
Loading…
Reference in New Issue