Compare commits
6 Commits
4887bc261b
...
d82e025700
Author | SHA1 | Date |
---|---|---|
Tim Gröger | d82e025700 | |
Tim Gröger | 07e1966471 | |
Ferdinand Thiessen | 29c085bd2c | |
Ferdinand Thiessen | 1b152b52f5 | |
Ferdinand Thiessen | 6769e18ffa | |
Ferdinand Thiessen | 88dd96c937 |
|
@ -18,6 +18,8 @@ yarn.lock
|
||||||
|
|
||||||
# Capacitor related directories and files
|
# Capacitor related directories and files
|
||||||
/src-capacitor/www
|
/src-capacitor/www
|
||||||
|
/src-capacitor/android
|
||||||
|
/src-capacitor/ios
|
||||||
/src-capacitor/node_modules
|
/src-capacitor/node_modules
|
||||||
|
|
||||||
# BEX related directories and files
|
# BEX related directories and files
|
||||||
|
|
|
@ -4,5 +4,6 @@ export * from './src/stores/';
|
||||||
|
|
||||||
export * from './src/utils/datetime';
|
export * from './src/utils/datetime';
|
||||||
export * from './src/utils/permission';
|
export * from './src/utils/permission';
|
||||||
|
export * from './src/utils/persistent';
|
||||||
export * from './src/utils/validators';
|
export * from './src/utils/validators';
|
||||||
export * from './src/utils/misc';
|
export * from './src/utils/misc';
|
||||||
|
|
|
@ -1,28 +1,31 @@
|
||||||
import { LocalStorage, SessionStorage } from 'quasar';
|
|
||||||
import { FG_Plugin } from '@flaschengeist/types';
|
import { FG_Plugin } from '@flaschengeist/types';
|
||||||
import { fixSession, useSessionStore, useUserStore } from '.';
|
import { fixSession, useSessionStore, useUserStore } from '.';
|
||||||
import { AxiosResponse } from 'axios';
|
import { AxiosResponse } from 'axios';
|
||||||
import { api } from '../internal';
|
import { api } from '../internal';
|
||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia';
|
||||||
|
import { PersistentStorage } from '../utils/persistent';
|
||||||
|
|
||||||
function loadCurrentSession() {
|
function loadToken() {
|
||||||
const session = LocalStorage.getItem<FG.Session>('session');
|
return PersistentStorage.get<string>('fg_token');
|
||||||
if (session) session.expires = new Date(session.expires);
|
|
||||||
return session || undefined;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadUser() {
|
function clearToken() {
|
||||||
const user = SessionStorage.getItem<FG.User>('user');
|
void PersistentStorage.remove('fg_token');
|
||||||
if (user && user.birthday) user.birthday = new Date(user.birthday);
|
}
|
||||||
return user || undefined;
|
|
||||||
|
export function saveToken(token?: string) {
|
||||||
|
if (token === undefined) return clearToken();
|
||||||
|
PersistentStorage.set('fg_token', token).catch(() =>
|
||||||
|
console.error('Could not save token to storage')
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useMainStore = defineStore({
|
export const useMainStore = defineStore({
|
||||||
id: 'main',
|
id: 'main',
|
||||||
|
|
||||||
state: () => ({
|
state: () => ({
|
||||||
session: loadCurrentSession(),
|
session: undefined as FG.Session | undefined,
|
||||||
user: loadUser(),
|
user: undefined as FG.User | undefined,
|
||||||
notifications: [] as Array<FG_Plugin.Notification>,
|
notifications: [] as Array<FG_Plugin.Notification>,
|
||||||
shortcuts: [] as Array<FG_Plugin.MenuLink>,
|
shortcuts: [] as Array<FG_Plugin.MenuLink>,
|
||||||
}),
|
}),
|
||||||
|
@ -45,18 +48,17 @@ export const useMainStore = defineStore({
|
||||||
* Updates session and loads current user
|
* Updates session and loads current user
|
||||||
*/
|
*/
|
||||||
async init() {
|
async init() {
|
||||||
if (this.session) {
|
const sessionStore = useSessionStore();
|
||||||
const sessionStore = useSessionStore();
|
const userStore = useUserStore();
|
||||||
const session = await sessionStore.getSession(this.session.token);
|
|
||||||
if (session) {
|
try {
|
||||||
this.session = session;
|
const token = await loadToken();
|
||||||
const userStore = useUserStore();
|
if (token !== null) {
|
||||||
const user = await userStore.getUser(this.session.userid);
|
this.session = await sessionStore.getSession(token);
|
||||||
if (user) {
|
if (this.session !== undefined) this.user = await userStore.getUser(this.session.userid);
|
||||||
this.user = user;
|
|
||||||
SessionStorage.set('user', user);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('Could not load token from storage', error);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -141,14 +143,10 @@ export const useMainStore = defineStore({
|
||||||
async setShortcuts() {
|
async setShortcuts() {
|
||||||
await api.put(`users/${this.currentUser.userid}/shortcuts`, this.shortcuts);
|
await api.put(`users/${this.currentUser.userid}/shortcuts`, this.shortcuts);
|
||||||
},
|
},
|
||||||
handleLoggedOut() {
|
|
||||||
LocalStorage.clear();
|
|
||||||
|
|
||||||
this.$patch({
|
handleLoggedOut() {
|
||||||
session: undefined,
|
this.$reset();
|
||||||
user: undefined,
|
void clearToken();
|
||||||
});
|
|
||||||
SessionStorage.clear();
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
import { LocalStorage, Platform } from 'quasar';
|
||||||
|
import { Storage } from '@capacitor/storage';
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
type PersitentTypes = Date | RegExp | number | boolean | string | object;
|
||||||
|
|
||||||
|
export class PersistentStorage {
|
||||||
|
static clear() {
|
||||||
|
if (Platform.is.capacitor) return Storage.clear();
|
||||||
|
else return Promise.resolve(LocalStorage.clear());
|
||||||
|
}
|
||||||
|
|
||||||
|
static remove(key: string) {
|
||||||
|
if (Platform.is.capacitor) return Storage.remove({ key: key });
|
||||||
|
else return Promise.resolve(LocalStorage.remove(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
static set(key: string, value: PersitentTypes) {
|
||||||
|
if (Platform.is.capacitor) return Storage.set({ key, value: JSON.stringify(value) });
|
||||||
|
else return Promise.resolve(LocalStorage.set(key, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
static get<T extends PersitentTypes>(key: string) {
|
||||||
|
if (Platform.is.capacitor)
|
||||||
|
return Storage.get({ key }).then((v) =>
|
||||||
|
v.value === null ? null : (JSON.parse(v.value) as T)
|
||||||
|
);
|
||||||
|
else return Promise.resolve(LocalStorage.getItem<T>(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
static keys() {
|
||||||
|
if (Platform.is.capacitor) return Storage.keys().then((v) => v.keys);
|
||||||
|
else return Promise.resolve(LocalStorage.getAllKeys());
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,10 +15,10 @@
|
||||||
"lint": "eslint --ext .js,.ts,.vue ./src ./api"
|
"lint": "eslint --ext .js,.ts,.vue ./src ./api"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@capacitor/storage": "^1.2.3",
|
||||||
"@flaschengeist/api": "file:./api",
|
"@flaschengeist/api": "file:./api",
|
||||||
"@flaschengeist/users": "^1.0.0-alpha.1",
|
"@flaschengeist/users": "^1.0.0-alpha.1",
|
||||||
"axios": "^0.24.0",
|
"axios": "^0.24.0",
|
||||||
"cordova": "^10.0.0",
|
|
||||||
"pinia": "^2.0.4",
|
"pinia": "^2.0.4",
|
||||||
"quasar": "^2.3.3"
|
"quasar": "^2.3.3"
|
||||||
},
|
},
|
||||||
|
@ -51,11 +51,11 @@
|
||||||
"Firefox esr",
|
"Firefox esr",
|
||||||
"last 6 Chrome versions",
|
"last 6 Chrome versions",
|
||||||
"last 4 Firefox versions",
|
"last 4 Firefox versions",
|
||||||
"last 4 Edge versions",
|
"last 4 Edge versions",
|
||||||
"last 4 Safari versions",
|
"last 4 Safari versions",
|
||||||
"last 4 ChromeAndroid versions",
|
"last 4 ChromeAndroid versions",
|
||||||
"last 1 FirefoxAndroid versions"
|
"last 1 FirefoxAndroid versions"
|
||||||
],
|
],
|
||||||
"cordova": [
|
"cordova": [
|
||||||
"iOS >= 13.0",
|
"iOS >= 13.0",
|
||||||
"Android >= 76"
|
"Android >= 76"
|
||||||
|
|
|
@ -75,7 +75,7 @@ module.exports = configure(function (/* ctx */) {
|
||||||
chain.plugin('eslint-webpack-plugin')
|
chain.plugin('eslint-webpack-plugin')
|
||||||
.use(ESLintPlugin, [{
|
.use(ESLintPlugin, [{
|
||||||
extensions: [ 'ts', 'js', 'vue' ],
|
extensions: [ 'ts', 'js', 'vue' ],
|
||||||
exclude: 'node_modules'
|
exclude: ['node_modules', 'src-capacitor']
|
||||||
}])
|
}])
|
||||||
chain.plugin('modify-source-webpack-plugin')
|
chain.plugin('modify-source-webpack-plugin')
|
||||||
.use(ModifySourcePlugin, [{
|
.use(ModifySourcePlugin, [{
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
// THIS FEATURE-FLAG FILE IS AUTOGENERATED,
|
// THIS FEATURE-FLAG FILE IS AUTOGENERATED,
|
||||||
// REMOVAL OR CHANGES WILL CAUSE RELATED TYPES TO STOP WORKING
|
// REMOVAL OR CHANGES WILL CAUSE RELATED TYPES TO STOP WORKING
|
||||||
import 'quasar/dist/types/feature-flag';
|
import "quasar/dist/types/feature-flag";
|
||||||
|
|
||||||
declare module 'quasar/dist/types/feature-flag' {
|
declare module "quasar/dist/types/feature-flag" {
|
||||||
interface QuasarFeatureFlags {
|
interface QuasarFeatureFlags {
|
||||||
cordova: true;
|
capacitor: true;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"appId": "dev.flaschengeist",
|
||||||
|
"appName": "flaschengeist-frontend",
|
||||||
|
"bundledWebRuntime": false,
|
||||||
|
"npmClient": "yarn",
|
||||||
|
"webDir": "www",
|
||||||
|
"ios": {
|
||||||
|
"allowsLinkPreview": false
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"name": "flaschengeist",
|
||||||
|
"version": "2.0.0-alpha.1",
|
||||||
|
"description": "Modular student club administration system",
|
||||||
|
"author": "Tim Gröger <flaschengeist@wu5.de>",
|
||||||
|
"private": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@capacitor/android": "^3.3.2",
|
||||||
|
"@capacitor/app": "^1.0.0",
|
||||||
|
"@capacitor/cli": "^3.0.0",
|
||||||
|
"@capacitor/core": "^3.0.0",
|
||||||
|
"@capacitor/ios": "^3.0.0-beta.0",
|
||||||
|
"@capacitor/splash-screen": "^1.0.0",
|
||||||
|
"@capacitor/storage": "^1.2.3"
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +0,0 @@
|
||||||
.DS_Store
|
|
||||||
|
|
||||||
# Generated by package manager
|
|
||||||
node_modules/
|
|
||||||
|
|
||||||
# Generated by Cordova
|
|
||||||
/plugins/
|
|
||||||
/platforms/
|
|
|
@ -1,76 +0,0 @@
|
||||||
<?xml version='1.0' encoding='utf-8'?>
|
|
||||||
<widget id="de.wu5.flaschengeist" version="2.0.0-alpha.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
|
|
||||||
<name>Flaschengeist</name>
|
|
||||||
<description>Modular student club administration system</description>
|
|
||||||
<author email="dev@cordova.apache.org" href="http://cordova.io">
|
|
||||||
Apache Cordova Team
|
|
||||||
</author>
|
|
||||||
<content src="index.html" />
|
|
||||||
<access origin="*" />
|
|
||||||
<allow-intent href="http://*/*" />
|
|
||||||
<allow-intent href="https://*/*" />
|
|
||||||
<allow-intent href="tel:*" />
|
|
||||||
<allow-intent href="sms:*" />
|
|
||||||
<allow-intent href="mailto:*" />
|
|
||||||
<allow-intent href="geo:*" />
|
|
||||||
<platform name="android">
|
|
||||||
<allow-intent href="market:*" />
|
|
||||||
<icon density="ldpi" src="res/android/ldpi.png" />
|
|
||||||
<icon density="mdpi" src="res/android/mdpi.png" />
|
|
||||||
<icon density="hdpi" src="res/android/hdpi.png" />
|
|
||||||
<icon density="xhdpi" src="res/android/xhdpi.png" />
|
|
||||||
<icon density="xxhdpi" src="res/android/xxhdpi.png" />
|
|
||||||
<icon density="xxxhdpi" src="res/android/xxxhdpi.png" />
|
|
||||||
<splash density="land-ldpi" src="res/screen/android/splash-land-ldpi.png" />
|
|
||||||
<splash density="port-ldpi" src="res/screen/android/splash-port-ldpi.png" />
|
|
||||||
<splash density="land-mdpi" src="res/screen/android/splash-land-mdpi.png" />
|
|
||||||
<splash density="port-mdpi" src="res/screen/android/splash-port-mdpi.png" />
|
|
||||||
<splash density="land-hdpi" src="res/screen/android/splash-land-hdpi.png" />
|
|
||||||
<splash density="port-hdpi" src="res/screen/android/splash-port-hdpi.png" />
|
|
||||||
<splash density="land-xhdpi" src="res/screen/android/splash-land-xhdpi.png" />
|
|
||||||
<splash density="port-xhdpi" src="res/screen/android/splash-port-xhdpi.png" />
|
|
||||||
<splash density="land-xxhdpi" src="res/screen/android/splash-land-xxhdpi.png" />
|
|
||||||
<splash density="port-xxhdpi" src="res/screen/android/splash-port-xxhdpi.png" />
|
|
||||||
<splash density="land-xxxhdpi" src="res/screen/android/splash-land-xxxhdpi.png" />
|
|
||||||
<splash density="port-xxxhdpi" src="res/screen/android/splash-port-xxxhdpi.png" />
|
|
||||||
</platform>
|
|
||||||
<platform name="ios">
|
|
||||||
<allow-intent href="itms:*" />
|
|
||||||
<allow-intent href="itms-apps:*" />
|
|
||||||
<icon height="57" src="res/ios/icon.png" width="57" />
|
|
||||||
<icon height="114" src="res/ios/icon@2x.png" width="114" />
|
|
||||||
<icon height="40" src="res/ios/icon-20@2x.png" width="40" />
|
|
||||||
<icon height="60" src="res/ios/icon-20@3x.png" width="60" />
|
|
||||||
<icon height="29" src="res/ios/icon-29.png" width="29" />
|
|
||||||
<icon height="58" src="res/ios/icon-29@2x.png" width="58" />
|
|
||||||
<icon height="87" src="res/ios/icon-29@3x.png" width="87" />
|
|
||||||
<icon height="80" src="res/ios/icon-40@2x.png" width="80" />
|
|
||||||
<icon height="120" src="res/ios/icon-60@2x.png" width="120" />
|
|
||||||
<icon height="180" src="res/ios/icon-60@3x.png" width="180" />
|
|
||||||
<icon height="20" src="res/ios/icon-20.png" width="20" />
|
|
||||||
<icon height="40" src="res/ios/icon-40.png" width="40" />
|
|
||||||
<icon height="50" src="res/ios/icon-50.png" width="50" />
|
|
||||||
<icon height="100" src="res/ios/icon-50@2x.png" width="100" />
|
|
||||||
<icon height="72" src="res/ios/icon-72.png" width="72" />
|
|
||||||
<icon height="144" src="res/ios/icon-72@2x.png" width="144" />
|
|
||||||
<icon height="76" src="res/ios/icon-76.png" width="76" />
|
|
||||||
<icon height="152" src="res/ios/icon-76@2x.png" width="152" />
|
|
||||||
<icon height="167" src="res/ios/icon-83.5@2x.png" width="167" />
|
|
||||||
<icon height="1024" src="res/ios/icon-1024.png" width="1024" />
|
|
||||||
<icon height="48" src="res/ios/icon-24@2x.png" width="48" />
|
|
||||||
<icon height="55" src="res/ios/icon-27.5@2x.png" width="55" />
|
|
||||||
<icon height="88" src="res/ios/icon-44@2x.png" width="88" />
|
|
||||||
<icon height="172" src="res/ios/icon-86@2x.png" width="172" />
|
|
||||||
<icon height="196" src="res/ios/icon-98@2x.png" width="196" />
|
|
||||||
<splash src="res/screen/ios/Default@2x~iphone~anyany.png" />
|
|
||||||
<splash src="res/screen/ios/Default@2x~iphone~comany.png" />
|
|
||||||
<splash src="res/screen/ios/Default@2x~iphone~comcom.png" />
|
|
||||||
<splash src="res/screen/ios/Default@3x~iphone~anyany.png" />
|
|
||||||
<splash src="res/screen/ios/Default@3x~iphone~anycom.png" />
|
|
||||||
<splash src="res/screen/ios/Default@3x~iphone~comany.png" />
|
|
||||||
<splash src="res/screen/ios/Default@2x~ipad~anyany.png" />
|
|
||||||
<splash src="res/screen/ios/Default@2x~ipad~comany.png" />
|
|
||||||
</platform>
|
|
||||||
<allow-navigation href="about:*" />
|
|
||||||
<preference name="SplashMaintainAspectRatio" value="true" />
|
|
||||||
</widget>
|
|
|
@ -1,31 +0,0 @@
|
||||||
{
|
|
||||||
"name": "de.wu5.flaschengeist",
|
|
||||||
"displayName": "Flaschengeist",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"description": "A sample Apache Cordova application that responds to the deviceready event.",
|
|
||||||
"main": "index.js",
|
|
||||||
"scripts": {
|
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
|
||||||
},
|
|
||||||
"keywords": [
|
|
||||||
"ecosystem:cordova"
|
|
||||||
],
|
|
||||||
"author": "Apache Cordova Team",
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"devDependencies": {
|
|
||||||
"cordova-android": "^9.0.0",
|
|
||||||
"cordova-ios": "^6.1.1",
|
|
||||||
"cordova-plugin-splashscreen": "^6.0.0",
|
|
||||||
"cordova-plugin-whitelist": "^1.3.4"
|
|
||||||
},
|
|
||||||
"cordova": {
|
|
||||||
"plugins": {
|
|
||||||
"cordova-plugin-whitelist": {},
|
|
||||||
"cordova-plugin-splashscreen": {}
|
|
||||||
},
|
|
||||||
"platforms": [
|
|
||||||
"ios",
|
|
||||||
"android"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
Before Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 913 B |
Before Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 4.3 KiB |
Before Width: | Height: | Size: 5.4 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 370 B |
Before Width: | Height: | Size: 702 B |
Before Width: | Height: | Size: 1022 B |
Before Width: | Height: | Size: 801 B |
Before Width: | Height: | Size: 969 B |
Before Width: | Height: | Size: 529 B |
Before Width: | Height: | Size: 1015 B |
Before Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 702 B |
Before Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 885 B |
Before Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 996 B |
Before Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 5.3 KiB |
Before Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 5.6 KiB |
Before Width: | Height: | Size: 5.9 KiB |
Before Width: | Height: | Size: 6.5 KiB |
Before Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 6.0 KiB |
Before Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 4.0 KiB |
Before Width: | Height: | Size: 5.1 KiB |
Before Width: | Height: | Size: 6.1 KiB |
Before Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 9.9 KiB |
Before Width: | Height: | Size: 9.3 KiB |
Before Width: | Height: | Size: 5.4 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 9.7 KiB |
Before Width: | Height: | Size: 8.5 KiB |
|
@ -1,5 +1,4 @@
|
||||||
import { useMainStore, api } from '@flaschengeist/api';
|
import { useMainStore, api } from '@flaschengeist/api';
|
||||||
import { LocalStorage, Notify } from 'quasar';
|
|
||||||
import { AxiosError } from 'axios';
|
import { AxiosError } from 'axios';
|
||||||
import { boot } from 'quasar/wrappers';
|
import { boot } from 'quasar/wrappers';
|
||||||
import config from 'src/config';
|
import config from 'src/config';
|
||||||
|
@ -31,7 +30,8 @@ function minify(o: unknown, cloned = false) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default boot(({ router }) => {
|
export default boot(({ router }) => {
|
||||||
api.defaults.baseURL = LocalStorage.getItem<string>('baseURL') || config.baseURL;
|
// Persisted value is read in plugins.ts boot file!
|
||||||
|
api.defaults.baseURL = config.baseURL;
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* Intercept requests
|
* Intercept requests
|
||||||
|
@ -95,17 +95,3 @@ export default boot(({ router }) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
export { api };
|
export { api };
|
||||||
|
|
||||||
export const setBaseURL = (url: string) => {
|
|
||||||
LocalStorage.set('baseURL', url);
|
|
||||||
api.defaults.baseURL = url;
|
|
||||||
Notify.create({
|
|
||||||
message: 'Serveraddresse gespeichert',
|
|
||||||
position: 'bottom',
|
|
||||||
caption: `${url}`,
|
|
||||||
color: 'positive',
|
|
||||||
});
|
|
||||||
setTimeout(() => {
|
|
||||||
window.location.reload();
|
|
||||||
}, 5000);
|
|
||||||
};
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import { Notify } from 'quasar';
|
import { Notify, Platform } from 'quasar';
|
||||||
import { api } from 'src/boot/axios';
|
import { api } from 'src/boot/axios';
|
||||||
import { boot } from 'quasar/wrappers';
|
import { boot } from 'quasar/wrappers';
|
||||||
import routes from 'src/router/routes';
|
import routes from 'src/router/routes';
|
||||||
import { AxiosResponse } from 'axios';
|
|
||||||
import { RouteRecordRaw } from 'vue-router';
|
import { RouteRecordRaw } from 'vue-router';
|
||||||
import { FG_Plugin } from '@flaschengeist/types';
|
import { FG_Plugin } from '@flaschengeist/types';
|
||||||
|
import { PersistentStorage } from '@flaschengeist/api';
|
||||||
|
|
||||||
/****************************************************
|
/****************************************************
|
||||||
******** Internal area for some magic **************
|
******** Internal area for some magic **************
|
||||||
|
@ -264,16 +264,24 @@ function loadPlugin(
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function loadBaseUrl() {
|
||||||
|
return PersistentStorage.get<string>('baseURL').then((url) => {
|
||||||
|
if (url !== null) api.defaults.baseURL = url;
|
||||||
|
console.log('loaded: ', url, api.defaults.baseURL);
|
||||||
|
});
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Loading backend information
|
* Loading backend information
|
||||||
* @returns Backend object or null
|
* @returns Backend object or null
|
||||||
*/
|
*/
|
||||||
async function getBackend() {
|
async function getBackend() {
|
||||||
try {
|
try {
|
||||||
const { data }: AxiosResponse<Backend> = await api.get('/');
|
const { data } = await api.get<Backend>('/');
|
||||||
|
if (!data || typeof data !== 'object' || !('plugins' in data))
|
||||||
|
throw Error('Invalid backend response received');
|
||||||
return data;
|
return data;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn(e);
|
console.error('Loading backend', e);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -282,10 +290,13 @@ async function getBackend() {
|
||||||
* Boot file, load all required plugins, check for dependencies
|
* Boot file, load all required plugins, check for dependencies
|
||||||
*/
|
*/
|
||||||
export default boot(async ({ router, app }) => {
|
export default boot(async ({ router, app }) => {
|
||||||
|
await loadBaseUrl();
|
||||||
const backend = await getBackend();
|
const backend = await getBackend();
|
||||||
if (!backend || typeof backend !== 'object' || !('plugins' in backend)) {
|
if (backend === null) {
|
||||||
console.log('Backend error');
|
router.isReady().finally(() => {
|
||||||
router.isReady().finally(() => void router.push({ name: 'offline', params: { refresh: 1 } }));
|
if (Platform.is.capacitor) void router.push({ name: 'setup_backend' });
|
||||||
|
else void router.push({ name: 'offline', params: { refresh: 1 } });
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,14 @@
|
||||||
import { useMainStore, pinia } from '@flaschengeist/api';
|
import { useMainStore, pinia } from '@flaschengeist/api';
|
||||||
|
import { saveToken } from 'app/api';
|
||||||
import { boot } from 'quasar/wrappers';
|
import { boot } from 'quasar/wrappers';
|
||||||
|
|
||||||
export default boot(({ app }) => {
|
export default boot(({ app }) => {
|
||||||
app.use(pinia);
|
app.use(pinia);
|
||||||
|
|
||||||
const store = useMainStore();
|
const store = useMainStore();
|
||||||
void store.init();
|
store.init().finally(() => {
|
||||||
|
store.$subscribe((mutation, state) => {
|
||||||
|
saveToken(state.session?.token);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
<template>
|
||||||
|
<div class="row justify-center items-center content-center q-pa-md">
|
||||||
|
<q-card class="col-xs-11 col-sm-8 col-md-6 col-lg-4 justify-center items-center content-center">
|
||||||
|
<q-toolbar class="bg-primary text-white">
|
||||||
|
<q-toolbar-title>Servereinstellung</q-toolbar-title>
|
||||||
|
</q-toolbar>
|
||||||
|
<q-card-section>
|
||||||
|
<q-form class="q-gutter-md" @submit="changeUrl">
|
||||||
|
<q-input v-model="server" filled label="Server" dense />
|
||||||
|
<q-btn dense color="primary" label="Speichern" type="submit" />
|
||||||
|
</q-form>
|
||||||
|
</q-card-section>
|
||||||
|
</q-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
import { notEmpty, PersistentStorage } from '@flaschengeist/api';
|
||||||
|
import { defineComponent, ref } from 'vue';
|
||||||
|
import { api } from 'boot/axios';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'PageBackend',
|
||||||
|
setup() {
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const server = ref(api.defaults.baseURL);
|
||||||
|
|
||||||
|
function changeUrl() {
|
||||||
|
if (server.value) {
|
||||||
|
void PersistentStorage.set('baseURL', server.value).then(() => {
|
||||||
|
console.log('uiuiui');
|
||||||
|
void router.push({ name: 'login' }).then(() => router.go(0));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
changeUrl,
|
||||||
|
notEmpty,
|
||||||
|
server,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -44,25 +44,13 @@
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
<div class="row justify-end">
|
<div class="row justify-end">
|
||||||
<q-btn
|
<q-btn
|
||||||
v-if="quasar.platform.is.cordova || quasar.platform.is.electron"
|
v-if="$q.platform.is.capacitor || $q.platform.is.electron"
|
||||||
flat
|
flat
|
||||||
round
|
round
|
||||||
icon="mdi-menu-down"
|
icon="mdi-menu-down"
|
||||||
@click="openServerSettings"
|
:to="{ name: 'setup_backend' }"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<q-slide-transition v-if="quasar.platform.is.cordova || quasar.platform.is.electron">
|
|
||||||
<div v-show="visible">
|
|
||||||
<q-separator />
|
|
||||||
<q-card-section>
|
|
||||||
<q-form ref="ServerSettingsForm" class="q-gutter-md" @submit="changeUrl">
|
|
||||||
<div class="text-h6">Servereinstellung</div>
|
|
||||||
<q-input v-model="server" filled label="Server" dense />
|
|
||||||
<q-btn size="xs" dense color="primary" label="Speichern" type="submit" />
|
|
||||||
</q-form>
|
|
||||||
</q-card-section>
|
|
||||||
</div>
|
|
||||||
</q-slide-transition>
|
|
||||||
</q-card>
|
</q-card>
|
||||||
</q-page>
|
</q-page>
|
||||||
</template>
|
</template>
|
||||||
|
@ -73,34 +61,20 @@ import { Loading, Notify } from 'quasar';
|
||||||
import { notEmpty, useMainStore, useUserStore } from '@flaschengeist/api';
|
import { notEmpty, useMainStore, useUserStore } from '@flaschengeist/api';
|
||||||
import { PasswordInput } from '@flaschengeist/api/components';
|
import { PasswordInput } from '@flaschengeist/api/components';
|
||||||
import { defineComponent, ref } from 'vue';
|
import { defineComponent, ref } from 'vue';
|
||||||
import { setBaseURL, api } from 'boot/axios';
|
|
||||||
import { useQuasar } from 'quasar';
|
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'PageLogin',
|
name: 'PageLogin',
|
||||||
components: { PasswordInput },
|
components: { PasswordInput },
|
||||||
setup() {
|
setup() {
|
||||||
|
const mainRoute = { name: 'dashboard' };
|
||||||
|
|
||||||
const mainStore = useMainStore();
|
const mainStore = useMainStore();
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
const mainRoute = { name: 'dashboard' };
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
/* Stuff for the real login page */
|
/* Stuff for the real login page */
|
||||||
const userid = ref('');
|
const userid = ref('');
|
||||||
const password = ref('');
|
const password = ref('');
|
||||||
const server = ref<string | undefined>(api.defaults.baseURL);
|
|
||||||
const visible = ref(false);
|
|
||||||
const quasar = useQuasar();
|
|
||||||
|
|
||||||
function openServerSettings() {
|
|
||||||
visible.value = !visible.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
function changeUrl() {
|
|
||||||
if (server.value) {
|
|
||||||
setBaseURL(server.value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function doLogin() {
|
async function doLogin() {
|
||||||
Loading.show({ message: 'Du wirst angemeldet' });
|
Loading.show({ message: 'Du wirst angemeldet' });
|
||||||
|
@ -155,16 +129,11 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
changeUrl,
|
|
||||||
doLogin,
|
doLogin,
|
||||||
doReset,
|
doReset,
|
||||||
notEmpty,
|
notEmpty,
|
||||||
openServerSettings,
|
|
||||||
password,
|
password,
|
||||||
server,
|
|
||||||
userid,
|
userid,
|
||||||
visible,
|
|
||||||
quasar,
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -53,6 +53,11 @@ const routes: RouteRecordRaw[] = [
|
||||||
name: 'offline',
|
name: 'offline',
|
||||||
component: () => import('pages/Offline.vue'),
|
component: () => import('pages/Offline.vue'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/setup-backend',
|
||||||
|
name: 'setup_backend',
|
||||||
|
component: () => import('pages/Backend.vue'),
|
||||||
|
},
|
||||||
// Always leave this as last one,
|
// Always leave this as last one,
|
||||||
// but you can also remove it
|
// but you can also remove it
|
||||||
{
|
{
|
||||||
|
|