@hostlink/nuxt-light 1.18.1 → 1.18.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/module.json +1 -1
- package/dist/module.mjs +0 -5
- package/dist/runtime/components/L/ForgetPasswordDialog.vue +11 -13
- package/dist/runtime/components/L/System/Setting/modules.vue +1 -1
- package/dist/runtime/components/l-app-main.vue +22 -17
- package/dist/runtime/components/l-app.vue +6 -13
- package/dist/runtime/components/l-edit-btn.vue +1 -1
- package/dist/runtime/components/l-form-dialog.vue +1 -2
- package/dist/runtime/components/l-input.vue +14 -9
- package/dist/runtime/components/l-login.vue +22 -17
- package/dist/runtime/components/l-page.vue +2 -1
- package/dist/runtime/components/l-tab.vue +4 -1
- package/dist/runtime/components/l-tabs.vue +2 -4
- package/dist/runtime/index.d.ts +1 -100
- package/dist/runtime/index.js +1 -209
- package/dist/runtime/lib/index.d.ts +1 -1
- package/dist/runtime/light.d.ts +1224 -0
- package/dist/runtime/light.js +242 -0
- package/dist/runtime/locales/zh-hk.json +29 -1
- package/dist/runtime/pages/System/database/table.vue +8 -10
- package/dist/runtime/pages/System/setting.vue +1 -1
- package/dist/runtime/pages/System/view_as.vue +3 -3
- package/dist/runtime/pages/User/_user_id/view.vue +6 -20
- package/dist/runtime/pages/User/setting/bio-auth.vue +34 -15
- package/dist/runtime/pages/User/setting/my_favorite.vue +2 -5
- package/dist/runtime/pages/User/setting/open_id.vue +35 -20
- package/dist/runtime/pages/User/setting/two-factor-auth.vue +2 -3
- package/dist/runtime/plugin.js +4 -1
- package/package.json +1 -1
- package/dist/runtime/components/L/ForgetPasswordResetDialog.vue +0 -34
- package/dist/runtime/pages/logout.vue +0 -17
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import { useQuasar } from "quasar";
|
|
2
|
+
import packageJson from "../../package.json";
|
|
3
|
+
import { watch, reactive } from "vue";
|
|
4
|
+
import { m, q } from "./lib/index.js";
|
|
5
|
+
const COLOR_CODE = {
|
|
6
|
+
red: "#f44336",
|
|
7
|
+
pink: "#e91e63",
|
|
8
|
+
purple: "#9c27b0",
|
|
9
|
+
"deep-purple": "#673ab7",
|
|
10
|
+
indigo: "#3f51b5",
|
|
11
|
+
blue: "#2196f3",
|
|
12
|
+
"light-blue": "#03a9f4",
|
|
13
|
+
cyan: "#00bcd4",
|
|
14
|
+
teal: "#009688",
|
|
15
|
+
green: "#4caf50",
|
|
16
|
+
"light-green": "#8bc34a",
|
|
17
|
+
lime: "#cddc39",
|
|
18
|
+
yellow: "#ffeb3b",
|
|
19
|
+
amber: "#ffc107",
|
|
20
|
+
orange: "#ff9800",
|
|
21
|
+
"deep-orange": "#ff5722",
|
|
22
|
+
brown: "#795548",
|
|
23
|
+
grey: "#9e9e9e",
|
|
24
|
+
"blue-grey": "#607d8b",
|
|
25
|
+
primary: "#7367f0",
|
|
26
|
+
secondary: "#82868b",
|
|
27
|
+
accent: "#9C27B0",
|
|
28
|
+
positive: "#28c76f",
|
|
29
|
+
negative: "#ea5455",
|
|
30
|
+
info: "#00cfe8",
|
|
31
|
+
warning: "#ff9f43",
|
|
32
|
+
dark: "#4b4b4b"
|
|
33
|
+
};
|
|
34
|
+
let styles = {};
|
|
35
|
+
let defaultStyle = {
|
|
36
|
+
inputOutlined: true,
|
|
37
|
+
inputStackLabel: true,
|
|
38
|
+
tableFlat: true,
|
|
39
|
+
tableBorder: true,
|
|
40
|
+
tableSeparator: "cell"
|
|
41
|
+
};
|
|
42
|
+
const light = reactive({
|
|
43
|
+
version: packageJson.version,
|
|
44
|
+
errors: [],
|
|
45
|
+
$q: null,
|
|
46
|
+
$i18n: null,
|
|
47
|
+
company: "",
|
|
48
|
+
companyLogo: "",
|
|
49
|
+
color: "primary",
|
|
50
|
+
theme: "light",
|
|
51
|
+
isAdmin: false,
|
|
52
|
+
permissions: Array(),
|
|
53
|
+
myFavorites: Array(),
|
|
54
|
+
$t: null,
|
|
55
|
+
notify: (opts) => {
|
|
56
|
+
if (light.$q == null) {
|
|
57
|
+
throw new Error("Quasar is not initialized");
|
|
58
|
+
}
|
|
59
|
+
if (typeof opts === "string") {
|
|
60
|
+
opts = { message: opts };
|
|
61
|
+
}
|
|
62
|
+
if (opts.message !== void 0) {
|
|
63
|
+
opts.message = light.$t(opts.message);
|
|
64
|
+
}
|
|
65
|
+
if (opts.caption !== void 0) {
|
|
66
|
+
opts.caption = light.$t(opts.caption);
|
|
67
|
+
}
|
|
68
|
+
return light.$q.notify(opts);
|
|
69
|
+
},
|
|
70
|
+
dialog: (opts) => {
|
|
71
|
+
if (light.$q == null) {
|
|
72
|
+
throw new Error("Quasar is not initialized");
|
|
73
|
+
}
|
|
74
|
+
if (opts.title !== void 0) {
|
|
75
|
+
opts.title = light.$t(opts.title);
|
|
76
|
+
}
|
|
77
|
+
if (opts.message !== void 0) {
|
|
78
|
+
opts.message = light.$t(opts.message);
|
|
79
|
+
}
|
|
80
|
+
if (opts.cancel === true) {
|
|
81
|
+
opts.cancel = "Cancel";
|
|
82
|
+
}
|
|
83
|
+
if (typeof opts.cancel === "string") {
|
|
84
|
+
opts.cancel = light.$t(opts.cancel);
|
|
85
|
+
}
|
|
86
|
+
if (opts.ok === true) {
|
|
87
|
+
opts.ok = "OK";
|
|
88
|
+
}
|
|
89
|
+
if (typeof opts.ok === "string") {
|
|
90
|
+
opts.ok = light.$t(opts.ok);
|
|
91
|
+
}
|
|
92
|
+
return light.$q.dialog(opts);
|
|
93
|
+
},
|
|
94
|
+
users: {
|
|
95
|
+
create: async (user) => {
|
|
96
|
+
const u = await m("addUser", { data: user });
|
|
97
|
+
user.user_id = u;
|
|
98
|
+
return user;
|
|
99
|
+
},
|
|
100
|
+
del: async (user_id) => {
|
|
101
|
+
return await m("deleteUser", { id: user_id });
|
|
102
|
+
},
|
|
103
|
+
update: async (id, user) => {
|
|
104
|
+
return await m("updateUser", {
|
|
105
|
+
id,
|
|
106
|
+
data: user
|
|
107
|
+
});
|
|
108
|
+
},
|
|
109
|
+
list: async (args) => {
|
|
110
|
+
const { listUser } = await q({
|
|
111
|
+
listUser: {
|
|
112
|
+
data: {
|
|
113
|
+
__args: args,
|
|
114
|
+
user_id: true,
|
|
115
|
+
name: true
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
return listUser.data;
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
roles: {
|
|
123
|
+
list: async () => {
|
|
124
|
+
const { listRole } = await q({
|
|
125
|
+
listRole: {
|
|
126
|
+
name: true
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
return listRole;
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
getColorValue: () => {
|
|
133
|
+
return COLOR_CODE[light.color];
|
|
134
|
+
},
|
|
135
|
+
setMyFavorites: (favorites) => {
|
|
136
|
+
light.myFavorites = favorites;
|
|
137
|
+
},
|
|
138
|
+
reloadMyFavorites: async () => {
|
|
139
|
+
const data = await q({
|
|
140
|
+
my: {
|
|
141
|
+
myFavorites: {
|
|
142
|
+
my_favorite_id: true,
|
|
143
|
+
label: true,
|
|
144
|
+
path: true,
|
|
145
|
+
icon: true
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
light.myFavorites = data.my.myFavorites;
|
|
150
|
+
},
|
|
151
|
+
getMyFavorites: () => {
|
|
152
|
+
return light.myFavorites;
|
|
153
|
+
},
|
|
154
|
+
isDarkMode: () => {
|
|
155
|
+
return light.theme == "dark";
|
|
156
|
+
},
|
|
157
|
+
addError: (err) => {
|
|
158
|
+
light.errors.push(err);
|
|
159
|
+
},
|
|
160
|
+
removeError: (error) => {
|
|
161
|
+
const index = light.errors.indexOf(error);
|
|
162
|
+
if (index > -1) {
|
|
163
|
+
light.errors.splice(index, 1);
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
getStyle(name) {
|
|
167
|
+
if (styles[name] === void 0) {
|
|
168
|
+
if (defaultStyle[name] !== void 0) {
|
|
169
|
+
return defaultStyle[name];
|
|
170
|
+
}
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
return styles[name];
|
|
174
|
+
},
|
|
175
|
+
setStyles(s) {
|
|
176
|
+
styles = s;
|
|
177
|
+
},
|
|
178
|
+
getStyles() {
|
|
179
|
+
return styles;
|
|
180
|
+
},
|
|
181
|
+
async setStyle(name, value) {
|
|
182
|
+
await m("updateMyStyle", {
|
|
183
|
+
name,
|
|
184
|
+
value
|
|
185
|
+
});
|
|
186
|
+
styles[name] = value;
|
|
187
|
+
},
|
|
188
|
+
setCurrentRoute(to) {
|
|
189
|
+
currentRoute = to;
|
|
190
|
+
},
|
|
191
|
+
getID() {
|
|
192
|
+
if (currentRoute == null) return null;
|
|
193
|
+
let name = currentRoute.name?.toString();
|
|
194
|
+
if (name == void 0) return 0;
|
|
195
|
+
let parts = name.split("-");
|
|
196
|
+
const keyname = parts[1];
|
|
197
|
+
return parseInt(currentRoute.params[keyname]);
|
|
198
|
+
},
|
|
199
|
+
init(styles2) {
|
|
200
|
+
light.color = styles2.color || "primary";
|
|
201
|
+
light.theme = styles2.theme || "semi-dark";
|
|
202
|
+
light.setStyles(styles2);
|
|
203
|
+
watch(() => light.theme, async () => {
|
|
204
|
+
await light.setStyle("theme", light.theme);
|
|
205
|
+
});
|
|
206
|
+
},
|
|
207
|
+
isGranted(right) {
|
|
208
|
+
if (right === void 0) return true;
|
|
209
|
+
if (light.permissions.includes("*")) return true;
|
|
210
|
+
if (light.permissions.includes(right)) return true;
|
|
211
|
+
const parts = right.split(".");
|
|
212
|
+
parts.pop();
|
|
213
|
+
do {
|
|
214
|
+
if (light.permissions.includes(parts.join(".") + ".*")) return true;
|
|
215
|
+
parts.pop();
|
|
216
|
+
} while (parts.length > 0);
|
|
217
|
+
return false;
|
|
218
|
+
},
|
|
219
|
+
setPermissions(permissions) {
|
|
220
|
+
this.permissions = permissions;
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
export const createLight = (opts) => {
|
|
224
|
+
return {
|
|
225
|
+
install: (app) => {
|
|
226
|
+
app.config.globalProperties.$light = light;
|
|
227
|
+
light.$i18n = opts.i18n;
|
|
228
|
+
light.$t = opts.i18n.global.t;
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
};
|
|
232
|
+
let currentRoute = null;
|
|
233
|
+
watch(() => light.color, async () => {
|
|
234
|
+
await m("updateMyStyle", {
|
|
235
|
+
name: "color",
|
|
236
|
+
value: light.color
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
export const useLight = () => {
|
|
240
|
+
light.$q = useQuasar();
|
|
241
|
+
return light;
|
|
242
|
+
};
|
|
@@ -182,5 +182,33 @@
|
|
|
182
182
|
"Invalid email format": "電郵格式錯誤",
|
|
183
183
|
"No data available": "沒有資料",
|
|
184
184
|
"Loading...": "載入中...",
|
|
185
|
-
"input_min": "最少{0}個字"
|
|
185
|
+
"input_min": "最少{0}個字",
|
|
186
|
+
"OK": "確定",
|
|
187
|
+
"Cancel": "取消",
|
|
188
|
+
"Please enter your username and email address, we will send you a code to reset your password": "請輸入您的使用者名稱和電子郵件地址,我們將向您發送一個代碼以重置密碼",
|
|
189
|
+
"Email is required": "電郵為必填",
|
|
190
|
+
"Enter your code": "輸入您的代碼",
|
|
191
|
+
"Please enter your two factor authentication code (If you lost your authenticator, please contact your administrator)": "請輸入您的雙重認證代碼(如果您丟失了您的驗證器,請聯繫您的管理員)",
|
|
192
|
+
"Please enter your new password": "請輸入您的新密碼",
|
|
193
|
+
"Your password has been reset successfully": "您的密碼已成功重置",
|
|
194
|
+
"Please enter the code sent to your email, your code will expire in 10 minutes": "請輸入發送到您的電子郵件的代碼,您的代碼將在10分鐘內過期",
|
|
195
|
+
"Your code is invalid or expired": "您的代碼無效或已過期",
|
|
196
|
+
"Mail": "郵件",
|
|
197
|
+
"Company": "公司",
|
|
198
|
+
"Company logo": "公司標誌",
|
|
199
|
+
"Copyright name": "版權名稱",
|
|
200
|
+
"Copyright year": "版權年份",
|
|
201
|
+
"Now download the app and scan the qrcode. Input the code to the following input and submit": "現在下載應用程序並掃描二維碼。將代碼輸入到以下輸入框中並提交",
|
|
202
|
+
"Please scan the QR code with your authenticator app, and enter the code generated": "請使用您的驗證器應用程序掃描QR代碼,然後輸入生成的代碼",
|
|
203
|
+
"You have already linked your Google account.": "您已經連接了您的Google帳戶。",
|
|
204
|
+
"Are you sure you want to unlink your Google account?": "您確定要取消連接您的Google帳戶嗎?",
|
|
205
|
+
"Yes": "是",
|
|
206
|
+
"No": "否",
|
|
207
|
+
"Add to favorite": "添加到收藏夾",
|
|
208
|
+
"Enter favorite label": "輸入收藏標籤",
|
|
209
|
+
"Reset 2FA": "重置雙重認證",
|
|
210
|
+
"Are you sure you want to reset 2FA?" : "您確定要重置雙重認證嗎?",
|
|
211
|
+
"Update role": "更新角色",
|
|
212
|
+
"Overview": "概覽",
|
|
213
|
+
"Click the button below to link your Google account.": "點擊下面的按鈕連接您的Google帳戶。"
|
|
186
214
|
}
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
-
import { api, m } from '#imports';
|
|
3
|
-
import { useQuasar } from 'quasar';
|
|
2
|
+
import { api, m, useLight } from '#imports';
|
|
4
3
|
import { resolveComponent, ref, reactive } from 'vue';
|
|
5
|
-
|
|
6
|
-
const $q = useQuasar();
|
|
4
|
+
const light = useLight();
|
|
7
5
|
|
|
8
6
|
const { system: { database } } = await api.query({
|
|
9
7
|
system: {
|
|
@@ -33,7 +31,7 @@ const refresh = async () => {
|
|
|
33
31
|
|
|
34
32
|
const field_add = resolveComponent('l-dialog-database-field-add');
|
|
35
33
|
const add = async (table) => {
|
|
36
|
-
|
|
34
|
+
light.dialog({
|
|
37
35
|
component: field_add
|
|
38
36
|
}).onOk(async (data) => {
|
|
39
37
|
//data.name
|
|
@@ -48,7 +46,7 @@ const add = async (table) => {
|
|
|
48
46
|
nullable: data.nullable,
|
|
49
47
|
autoincrement: data.autoincrement
|
|
50
48
|
})
|
|
51
|
-
|
|
49
|
+
light.notify({
|
|
52
50
|
type: 'positive',
|
|
53
51
|
message: addDatabaseField
|
|
54
52
|
})
|
|
@@ -56,7 +54,7 @@ const add = async (table) => {
|
|
|
56
54
|
refresh();
|
|
57
55
|
|
|
58
56
|
} catch (e) {
|
|
59
|
-
|
|
57
|
+
light.notify({
|
|
60
58
|
type: 'negative',
|
|
61
59
|
message: e.message
|
|
62
60
|
})
|
|
@@ -75,7 +73,7 @@ for (let table of database.table) {
|
|
|
75
73
|
const removeField = async (table) => {
|
|
76
74
|
//confirmation
|
|
77
75
|
|
|
78
|
-
|
|
76
|
+
light.dialog({
|
|
79
77
|
title: 'Remove field',
|
|
80
78
|
message: 'Are you sure you want to remove the selected fields?',
|
|
81
79
|
ok: 'Yes',
|
|
@@ -92,7 +90,7 @@ const removeField = async (table) => {
|
|
|
92
90
|
table,
|
|
93
91
|
fields: fields
|
|
94
92
|
})
|
|
95
|
-
|
|
93
|
+
lgiht.notify({
|
|
96
94
|
type: 'positive',
|
|
97
95
|
message: removeDatabaseFields
|
|
98
96
|
})
|
|
@@ -101,7 +99,7 @@ const removeField = async (table) => {
|
|
|
101
99
|
|
|
102
100
|
refresh();
|
|
103
101
|
} catch (e) {
|
|
104
|
-
|
|
102
|
+
light.notify({
|
|
105
103
|
type: 'negative',
|
|
106
104
|
message: e.message
|
|
107
105
|
})
|
|
@@ -23,7 +23,7 @@ const tab = ref('general')
|
|
|
23
23
|
<q-tab name="security" icon="sym_o_security" :label="$t('Security')" />
|
|
24
24
|
<q-tab name="Modules" icon="sym_o_apps" :label="$t('Modules')" />
|
|
25
25
|
<q-tab name="mail" icon="sym_o_email" :label="$t('Mail')" />
|
|
26
|
-
<q-tab name="forget-password" icon="sym_o_lock" :label="$t('Forget
|
|
26
|
+
<q-tab name="forget-password" icon="sym_o_lock" :label="$t('Forget password')" />
|
|
27
27
|
<q-tab name="developer" icon="sym_o_code" :label="$t('Developer')" />
|
|
28
28
|
</q-tabs>
|
|
29
29
|
</template>
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
-
import { list, m } from '#imports'
|
|
2
|
+
import { list, m, useLight } from '#imports'
|
|
3
3
|
import { useRouter } from "vue-router"
|
|
4
4
|
import { ref, computed } from "vue"
|
|
5
5
|
import { useQuasar } from 'quasar'
|
|
6
|
-
const
|
|
6
|
+
const light = useLight();
|
|
7
7
|
|
|
8
8
|
let { data: users } = await list("User", null, ["user_id", "username", "name", "roles", "email"]);
|
|
9
9
|
|
|
@@ -44,7 +44,7 @@ const onCickView = async (id) => {
|
|
|
44
44
|
window.location.reload();
|
|
45
45
|
}
|
|
46
46
|
} catch (e) {
|
|
47
|
-
|
|
47
|
+
light.dialog({
|
|
48
48
|
title: "Error",
|
|
49
49
|
message: e.message,
|
|
50
50
|
color: "negative",
|
|
@@ -2,22 +2,16 @@
|
|
|
2
2
|
import { useLight, getObject, m } from "#imports";
|
|
3
3
|
import { useRoute } from "vue-router"
|
|
4
4
|
import { ref } from 'vue';
|
|
5
|
-
import { useQuasar } from 'quasar';
|
|
6
5
|
|
|
7
|
-
const $q = useQuasar();
|
|
8
6
|
const route = useRoute();
|
|
9
|
-
|
|
10
7
|
const obj = await getObject(["canUpdate"]);
|
|
11
|
-
|
|
12
8
|
const light = useLight();
|
|
13
|
-
|
|
14
9
|
const tab = ref('overview');
|
|
15
10
|
const id = route.params.user_id;
|
|
16
11
|
|
|
17
12
|
const reset2fa = async () => {
|
|
18
|
-
|
|
19
13
|
//confirm
|
|
20
|
-
await
|
|
14
|
+
await light.dialog({
|
|
21
15
|
title: "Reset 2FA",
|
|
22
16
|
message: "Are you sure you want to reset 2FA?",
|
|
23
17
|
color: "negative",
|
|
@@ -26,27 +20,19 @@ const reset2fa = async () => {
|
|
|
26
20
|
}).onOk(async () => {
|
|
27
21
|
try {
|
|
28
22
|
await m('reset2FA', { id: parseInt(id) });
|
|
29
|
-
|
|
23
|
+
light.notify({
|
|
30
24
|
type: 'positive',
|
|
31
25
|
message: '2FA reset successfully'
|
|
32
26
|
});
|
|
33
27
|
} catch (e) {
|
|
34
|
-
|
|
28
|
+
light.notify({
|
|
35
29
|
type: 'negative',
|
|
36
30
|
message: e.message
|
|
37
31
|
});
|
|
38
32
|
}
|
|
39
|
-
|
|
40
33
|
})
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
34
|
}
|
|
47
|
-
|
|
48
35
|
</script>
|
|
49
|
-
|
|
50
36
|
<template>
|
|
51
37
|
<l-page :edit-btn="obj.canUpdate">
|
|
52
38
|
|
|
@@ -59,10 +45,10 @@ const reset2fa = async () => {
|
|
|
59
45
|
|
|
60
46
|
<q-card flat bordered>
|
|
61
47
|
<q-tabs v-model="tab" :active-color="$light.color" inline-label align="justify">
|
|
62
|
-
<q-tab name="overview" icon="sym_o_person" label="Overview" />
|
|
63
|
-
<q-tab name="userlog" icon="sym_o_description" label="User log"
|
|
48
|
+
<q-tab name="overview" icon="sym_o_person" :label="$t('Overview')" />
|
|
49
|
+
<q-tab name="userlog" icon="sym_o_description" :label="$t('User log')"
|
|
64
50
|
v-if="$light.isGranted('userlog.list')" />
|
|
65
|
-
<q-tab name="eventlog" icon="sym_o_description" label="Event log"
|
|
51
|
+
<q-tab name="eventlog" icon="sym_o_description" :label="$t('Event log')"
|
|
66
52
|
v-if="$light.isGranted('eventlog.list')" />
|
|
67
53
|
</q-tabs>
|
|
68
54
|
|
|
@@ -1,20 +1,44 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import { useQuasar } from "quasar";
|
|
3
|
-
import { ref } from "vue"
|
|
3
|
+
import { ref, reactive } from "vue"
|
|
4
|
+
import { refreshNuxtData } from "#imports";
|
|
4
5
|
import { q, m, getCurrentUser, api } from '#imports'
|
|
5
|
-
const app = await q(
|
|
6
|
+
const { app, listWebAuthn: data } = reactive(await q({
|
|
7
|
+
app: {
|
|
8
|
+
hasBioAuth: true
|
|
9
|
+
},
|
|
10
|
+
listWebAuthn: {
|
|
11
|
+
uuid: true,
|
|
12
|
+
ip: true,
|
|
13
|
+
user_agent: true,
|
|
14
|
+
createdTime: true
|
|
15
|
+
}
|
|
16
|
+
}));
|
|
6
17
|
|
|
7
18
|
|
|
8
19
|
const quasar = useQuasar();
|
|
9
20
|
|
|
10
|
-
const data = ref(await q("listWebAuthn", ["uuid", "ip", "user_agent", "createdTime"]));
|
|
11
|
-
|
|
12
21
|
const register = async () => {
|
|
13
22
|
try {
|
|
14
23
|
await api.auth.WebAuthn.register();
|
|
15
|
-
data.value = await q("listWebAuthn", ["uuid", "ip", "user_agent", "createdTime"]);
|
|
16
24
|
const user = await getCurrentUser();
|
|
17
25
|
localStorage.setItem("username", user.username);
|
|
26
|
+
|
|
27
|
+
//refresh data
|
|
28
|
+
const { listWebAuthn: d } = await q({
|
|
29
|
+
listWebAuthn: {
|
|
30
|
+
uuid: true,
|
|
31
|
+
ip: true,
|
|
32
|
+
user_agent: true,
|
|
33
|
+
createdTime: true
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
//clear data
|
|
38
|
+
data.splice(0, data.length);
|
|
39
|
+
|
|
40
|
+
data.push(...d);
|
|
41
|
+
|
|
18
42
|
} catch (e) {
|
|
19
43
|
quasar.dialog({
|
|
20
44
|
title: "Error",
|
|
@@ -58,10 +82,9 @@ const deleteItem = async (uuid) => {
|
|
|
58
82
|
cancel: "No",
|
|
59
83
|
}).onOk(async () => {
|
|
60
84
|
await m("deleteWebAuthn", { uuid });
|
|
61
|
-
|
|
85
|
+
//remove from data
|
|
86
|
+
data.splice(data.findIndex(item => item.uuid === uuid), 1);
|
|
62
87
|
})
|
|
63
|
-
|
|
64
|
-
|
|
65
88
|
}
|
|
66
89
|
|
|
67
90
|
</script>
|
|
@@ -72,9 +95,10 @@ const deleteItem = async (uuid) => {
|
|
|
72
95
|
</q-card-section>
|
|
73
96
|
|
|
74
97
|
<template v-else>
|
|
75
|
-
<q-
|
|
98
|
+
<q-toolbar>
|
|
99
|
+
<q-toolbar-title>Biometric authentication</q-toolbar-title>
|
|
76
100
|
<l-btn label="Register" @click="register" icon="sym_o_add"></l-btn>
|
|
77
|
-
</q-
|
|
101
|
+
</q-toolbar>
|
|
78
102
|
|
|
79
103
|
<q-table :rows="data" :columns="columns" dense :rows-per-page-options="[0]">
|
|
80
104
|
<template #body-cell-action="props">
|
|
@@ -82,14 +106,9 @@ const deleteItem = async (uuid) => {
|
|
|
82
106
|
<q-btn @click="deleteItem(props.row.uuid)" icon="sym_o_delete" round flat dense></q-btn>
|
|
83
107
|
</q-td>
|
|
84
108
|
</template>
|
|
85
|
-
|
|
86
|
-
|
|
87
109
|
</q-table>
|
|
88
110
|
|
|
89
111
|
</template>
|
|
90
112
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
113
|
</l-card>
|
|
95
114
|
</template>
|
|
@@ -3,7 +3,7 @@ import { useLight, model, q } from "#imports"
|
|
|
3
3
|
import { ref } from "vue"
|
|
4
4
|
import { useQuasar } from "quasar";
|
|
5
5
|
const light = useLight()
|
|
6
|
-
const
|
|
6
|
+
const $q = useQuasar()
|
|
7
7
|
|
|
8
8
|
const columns = [
|
|
9
9
|
{ name: "_delete", align: "center" },
|
|
@@ -17,15 +17,12 @@ const onSave = async (id, data) => {
|
|
|
17
17
|
await model("MyFavorite").update(id, data);
|
|
18
18
|
|
|
19
19
|
//show success message
|
|
20
|
-
|
|
20
|
+
$q.notify({
|
|
21
21
|
message: "Updated successfully",
|
|
22
22
|
color: "positive",
|
|
23
23
|
icon: "check"
|
|
24
24
|
});
|
|
25
|
-
|
|
26
|
-
|
|
27
25
|
await light.reloadMyFavorites()
|
|
28
|
-
|
|
29
26
|
}
|
|
30
27
|
|
|
31
28
|
const onRemove = async (id) => {
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import { reactive, onMounted, nextTick } from "vue"
|
|
3
3
|
import { useQuasar } from "quasar";
|
|
4
|
-
import { q, m, api } from '#imports'
|
|
4
|
+
import { q, m, api, useLight } from '#imports'
|
|
5
|
+
import { useI18n } from 'vue-i18n'
|
|
6
|
+
|
|
7
|
+
const light = useLight()
|
|
8
|
+
const { t } = useI18n()
|
|
5
9
|
const quasar = useQuasar();
|
|
6
10
|
let { app, my } = await q({ app: ['googleClientId'], my: ["gmail"] })
|
|
7
11
|
|
|
12
|
+
const $q = useQuasar();
|
|
8
13
|
my = reactive(my);
|
|
9
14
|
|
|
10
15
|
const handleGoogleCredentialResponse = async (response) => {
|
|
@@ -14,11 +19,9 @@ const handleGoogleCredentialResponse = async (response) => {
|
|
|
14
19
|
const resp = await q("my", ["gmail"]);
|
|
15
20
|
my.gmail = resp.gmail;
|
|
16
21
|
|
|
17
|
-
|
|
22
|
+
light.notify({
|
|
18
23
|
message: "Google account linked",
|
|
19
24
|
color: "positive",
|
|
20
|
-
position: "top",
|
|
21
|
-
timeout: 2000
|
|
22
25
|
});
|
|
23
26
|
} catch (e) {
|
|
24
27
|
console.log(e)
|
|
@@ -67,28 +70,41 @@ onMounted(() => {
|
|
|
67
70
|
|
|
68
71
|
});
|
|
69
72
|
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
73
|
+
const onUnlink = async () => {
|
|
74
|
+
|
|
75
|
+
//confirm
|
|
76
|
+
light.dialog({
|
|
77
|
+
title: "Unlink",
|
|
78
|
+
color: "negative",
|
|
79
|
+
message: "Are you sure you want to unlink your Google account?",
|
|
80
|
+
ok: "Yes",
|
|
81
|
+
cancel: "No",
|
|
82
|
+
}).onOk(async () => {
|
|
83
|
+
await m("unlinkGoogle");
|
|
84
|
+
my.gmail = null;
|
|
85
|
+
window.location.reload();
|
|
86
|
+
})
|
|
87
|
+
|
|
74
88
|
}
|
|
75
89
|
|
|
76
90
|
</script>
|
|
77
91
|
<template>
|
|
78
|
-
<
|
|
79
|
-
|
|
92
|
+
<q-card>
|
|
80
93
|
<template v-if="app.googleClientId">
|
|
81
|
-
<
|
|
82
|
-
<
|
|
83
|
-
You have already linked your Google account
|
|
94
|
+
<template v-if="my.gmail">
|
|
95
|
+
<q-card-section>
|
|
96
|
+
{{ $t('You have already linked your Google account.') }}<br />
|
|
97
|
+
Your gmail id is {{ my.gmail }}
|
|
98
|
+
</q-card-section>
|
|
84
99
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
100
|
+
<q-card-actions>
|
|
101
|
+
<l-btn label="Unlink" @click="onUnlink" icon="sym_o_delete"></l-btn>
|
|
102
|
+
</q-card-actions>
|
|
103
|
+
|
|
104
|
+
</template>
|
|
89
105
|
|
|
90
106
|
<q-card-section v-else>
|
|
91
|
-
Click the button below to link your Google account.
|
|
107
|
+
{{ $t('Click the button below to link your Google account.') }}
|
|
92
108
|
<div id="g_id_signin"></div>
|
|
93
109
|
</q-card-section>
|
|
94
110
|
</template>
|
|
@@ -96,6 +112,5 @@ const unlink = async () => {
|
|
|
96
112
|
<q-card-section v-else>
|
|
97
113
|
Google login is not available. Please set GOOGLE_CLIENT_ID in server settings.
|
|
98
114
|
</q-card-section>
|
|
99
|
-
|
|
100
|
-
</l-card>
|
|
115
|
+
</q-card>
|
|
101
116
|
</template>
|
|
@@ -48,8 +48,7 @@ const onCopy = () => {
|
|
|
48
48
|
<l-form @submit="save" :bordered="false" v-if="show">
|
|
49
49
|
<div>
|
|
50
50
|
<p>
|
|
51
|
-
Now download the app and scan the qrcode. Input the code to the
|
|
52
|
-
following input and submit
|
|
51
|
+
{{ $t('Now download the app and scan the qrcode. Input the code to the following input and submit') }}
|
|
53
52
|
</p>
|
|
54
53
|
<p>
|
|
55
54
|
For Android user, install
|
|
@@ -84,7 +83,7 @@ const onCopy = () => {
|
|
|
84
83
|
</p>
|
|
85
84
|
|
|
86
85
|
<l-input v-model="obj.code" label="Code"
|
|
87
|
-
hint="Please scan the QR code with your authenticator app, and enter the code" required />
|
|
86
|
+
hint="Please scan the QR code with your authenticator app, and enter the code generated" required />
|
|
88
87
|
</l-form>
|
|
89
88
|
<div v-else>
|
|
90
89
|
<q-card-section>
|