@hostlink/nuxt-light 1.15.4 → 1.17.0

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 CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "light",
3
3
  "configKey": "light",
4
- "version": "1.15.4",
4
+ "version": "1.17.0",
5
5
  "builder": {
6
- "@nuxt/module-builder": "0.8.3",
6
+ "@nuxt/module-builder": "0.8.4",
7
7
  "unbuild": "2.0.0"
8
8
  }
9
9
  }
@@ -0,0 +1,68 @@
1
+ <script setup>
2
+ import { ref } from 'vue'
3
+ import { useQuasar, useDialogPluginComponent } from 'quasar'
4
+ import { m, api } from '#imports';
5
+ const $q = useQuasar()
6
+
7
+
8
+ const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } = useDialogPluginComponent()
9
+
10
+ const username = ref('')
11
+ const email = ref('')
12
+ const onOKClick = async () => {
13
+ if (username.value == '' || email.value == '') {
14
+ $q.notify({
15
+ type: 'negative',
16
+ message: 'Username and Email is required'
17
+ })
18
+ return;
19
+ }
20
+
21
+ try {
22
+ $q.loading.show();
23
+ await api.auth.forgetPassword(username.value, email.value);
24
+ onDialogOK({
25
+ username: username.value,
26
+ email: email.value
27
+ })
28
+ } catch (e) {
29
+ $q.notify({
30
+ type: 'negative',
31
+ message: e.message
32
+ })
33
+ onDialogCancel()
34
+ } finally {
35
+ $q.loading.hide();
36
+ }
37
+
38
+ }
39
+
40
+ </script>
41
+
42
+ <template>
43
+ <q-dialog ref="dialogRef">
44
+ <q-card class="q-dialog-plugin">
45
+
46
+ <q-card-section class="q-dialog__title">
47
+ Forget Password
48
+ </q-card-section>
49
+
50
+ <q-card-section class="q-dialog__message">
51
+ Please enter your username and email address, we will send you a code to reset your password
52
+ </q-card-section>
53
+
54
+ <q-card-section>
55
+ <q-input v-model="username" label="Username" stack-label
56
+ :rules="[val => !!val || 'Username is required']" hide-bottom-space />
57
+ <q-input v-model="email" label="Email" stack-label type="email" hide-bottom-space
58
+ :rules="[val => !!val || 'Email is required']" />
59
+ </q-card-section>
60
+
61
+
62
+ <q-card-actions align="right">
63
+ <q-btn label="Cancel" @click="onDialogCancel" flat color="primary" />
64
+ <q-btn label="OK" @click="onOKClick" flat color="primary" />
65
+ </q-card-actions>
66
+ </q-card>
67
+ </q-dialog>
68
+ </template>
@@ -0,0 +1,34 @@
1
+ <script setup lang="ts">
2
+ import { useQuasar, useDialogPluginComponent } from 'quasar'
3
+ import { m, api } from '#imports';
4
+ const $q = useQuasar()
5
+ const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } = useDialogPluginComponent()
6
+
7
+ defineProps({
8
+ username: String,
9
+ code: String
10
+ })
11
+
12
+ </script>
13
+ <template>
14
+ <q-dialog ref="dialogRef">
15
+ <q-card class="q-dialog-plugin">
16
+ <q-card-section class="q-dialog__title">
17
+ Reset password
18
+ </q-card-section>
19
+
20
+ <q-card-section class="q-dialog__message">
21
+ Please enter your new password
22
+ </q-card-section>
23
+
24
+ <q-card-section>
25
+ <form-kit type="form" :actions="false">
26
+ <form-kit type="l-input" name="password" :stack-label="true"></form-kit>
27
+ </form-kit>
28
+ </q-card-section>
29
+
30
+
31
+
32
+ </q-card>
33
+ </q-dialog>
34
+ </template>
@@ -0,0 +1,48 @@
1
+ <script setup lang="ts">
2
+ import { q, m } from '#imports'
3
+ import { useQuasar } from 'quasar'
4
+
5
+ const $q = useQuasar()
6
+
7
+ const modelValue = defineModel<{
8
+ mode: string
9
+ }>()
10
+
11
+
12
+
13
+ if (modelValue.value.mode != 'prod') {
14
+ modelValue.value.mode = 'dev'
15
+ } else {
16
+ modelValue.value.mode = 'prod'
17
+ }
18
+
19
+ const onSubmit = async (d, form) => {
20
+ let data = [];
21
+ Object.keys(d).forEach((key) => {
22
+ data.push({
23
+ name: key,
24
+ value: d[key]
25
+ })
26
+ })
27
+ await m("updateAppConfigs", { data })
28
+ //update the modelValue
29
+ Object.keys(d).forEach((key) => {
30
+ modelValue.value[key] = d[key]
31
+ })
32
+
33
+ $q.notify({ message: "Settings saved", color: "positive" })
34
+ }
35
+
36
+ </script>
37
+ <template>
38
+ <FormKit type="l-form" :bordered="false" :value="{
39
+ mode: modelValue.mode
40
+ }" @submit="onSubmit">
41
+ <FormKit label="Mode" type="l-select" :options="[
42
+ { label: 'Production', value: 'prod' },
43
+ { label: 'Development', value: 'dev' },
44
+
45
+ ]" name="mode" validation="required">
46
+ </FormKit>
47
+ </FormKit>
48
+ </template>
@@ -0,0 +1,61 @@
1
+ <script setup lang="ts">
2
+
3
+ import { q, m } from '#imports'
4
+ import { useQuasar } from 'quasar'
5
+ const $q = useQuasar()
6
+
7
+ const modelValue = defineModel<{
8
+ forget_password_enabled: string,
9
+ forget_password_subject: string,
10
+ forget_password_template: string,
11
+ }>()
12
+
13
+ if (!modelValue.value.forget_password_enabled == "0") {
14
+ modelValue.value.forget_password_enabled = "1"
15
+ }
16
+
17
+ if (!modelValue.value.forget_password_subject) {
18
+ modelValue.value.forget_password_subject = "Password Reset Code"
19
+ }
20
+
21
+ if (!modelValue.value.forget_password_template) {
22
+ modelValue.value.forget_password_template = "Your password reset code is: {code}"
23
+ }
24
+
25
+
26
+ const onSubmit = async (d) => {
27
+ let data = [];
28
+ Object.keys(d).forEach((key) => {
29
+ if (d[key] === undefined) {
30
+ d[key] = ""
31
+ }
32
+
33
+ data.push({
34
+ name: key,
35
+ value: d[key]
36
+ })
37
+ })
38
+ await m("updateAppConfigs", { data })
39
+ //update the props.data
40
+ Object.keys(d).forEach((key) => {
41
+ modelValue.value[key] = d[key]
42
+ })
43
+ $q.notify({ message: "Settings saved", color: "positive" })
44
+ }
45
+
46
+ </script>
47
+ <template>
48
+ <form-kit type="l-form" :value="{
49
+ forget_password_enabled: modelValue.forget_password_enabled,
50
+ forget_password_subject: modelValue.forget_password_subject,
51
+ forget_password_template: modelValue.forget_password_template,
52
+ }" @submit="onSubmit">
53
+ <form-kit type="l-checkbox" label="Forget Password Enabled" name="forget_password_enabled" true-value="1"
54
+ false-value="0" />
55
+
56
+ <form-kit type="l-input" label="Forget Password Subject" name="forget_password_subject" />
57
+ <form-kit type="l-input" label="Forget Password Template" name="forget_password_template"
58
+ input-type="textarea" />
59
+
60
+ </form-kit>
61
+ </template>
@@ -0,0 +1,49 @@
1
+ <script setup lang="ts">
2
+ import { q, m } from '#imports'
3
+ import { useQuasar } from 'quasar'
4
+
5
+ const $q = useQuasar()
6
+
7
+ const modelValue = defineModel<{
8
+ company: string
9
+ company_logo: string
10
+ copyright_name: string
11
+ copyright_year: string
12
+ }>()
13
+
14
+
15
+ const onSubmit = async (d, form) => {
16
+ let data = [];
17
+ Object.keys(d).forEach((key) => {
18
+ data.push({
19
+ name: key,
20
+ value: d[key]
21
+ })
22
+ })
23
+ await m("updateAppConfigs", { data })
24
+
25
+ Object.keys(d).forEach((key) => {
26
+ modelValue.value[key] = d[key]
27
+
28
+ })
29
+
30
+ $q.notify({ message: "Settings saved", color: "positive" })
31
+ }
32
+
33
+ </script>
34
+ <template>
35
+ <FormKit type="l-form" :bordered="false" :value="{
36
+ company: modelValue.company,
37
+ company_logo: modelValue.company_logo,
38
+ copyright_name: modelValue.copyright_name,
39
+ copyright_year: modelValue.copyright_year
40
+ }" @submit="onSubmit">
41
+
42
+ <FormKit type="l-input" label="Company" name="company" validation="required"></FormKit>
43
+ <FormKit type="l-input" label="Company logo" name="company_logo"></FormKit>
44
+
45
+ <FormKit label="Copyright name" type="l-input" name="copyright_name" validation="required" />
46
+ <FormKit label="Copyright year" type="l-input" name="copyright_year" validation="required" />
47
+
48
+ </FormKit>
49
+ </template>
@@ -0,0 +1,88 @@
1
+ <script setup lang="ts">
2
+ import { q, m } from '#imports'
3
+ import { useQuasar } from 'quasar'
4
+ const $q = useQuasar()
5
+
6
+ const modelValue = defineModel<{
7
+ mail_driver: string,
8
+ mail_host: string
9
+ mail_port: string
10
+ mail_username: string
11
+ mail_password: string
12
+ mail_encryption: string
13
+ mail_from: string
14
+ mail_from_name: string
15
+ mail_reply_to: string
16
+ mail_reply_to_name: string
17
+ }>()
18
+
19
+ if (!modelValue.value.mail_driver) {
20
+ modelValue.value.mail_driver = "mail"
21
+ }
22
+
23
+ const onSubmit = async (d) => {
24
+ let data = [];
25
+ Object.keys(d).forEach((key) => {
26
+ if (d[key] === undefined) {
27
+ d[key] = ""
28
+ }
29
+
30
+ data.push({
31
+ name: key,
32
+ value: d[key]
33
+ })
34
+ })
35
+ await m("updateAppConfigs", { data })
36
+ //update the props.data
37
+ Object.keys(d).forEach((key) => {
38
+ modelValue.value[key] = d[key]
39
+ })
40
+ $q.notify({ message: "Settings saved", color: "positive" })
41
+ }
42
+
43
+ </script>
44
+ <template>
45
+ <FormKit type="l-form" :bordered="false" :value="{
46
+ mail_driver: modelValue.mail_driver,
47
+ mail_host: modelValue.mail_host,
48
+ mail_port: modelValue.mail_port,
49
+ mail_username: modelValue.mail_username,
50
+ mail_password: modelValue.mail_password,
51
+ mail_encryption: modelValue.mail_encryption,
52
+ mail_from: modelValue.mail_from,
53
+ mail_from_name: modelValue.mail_from_name,
54
+ mail_reply_to: modelValue.mail_reply_to,
55
+ mail_reply_to_name: modelValue.mail_reply_to_name,
56
+ }" @submit="onSubmit" #default="{ value }">
57
+
58
+ <FormKit type="l-select" label="Mail Driver" name="mail_driver" :options="[{
59
+ label: 'Mail', value: 'mail'
60
+ }, {
61
+ label: 'SMTP', value: 'smtp'
62
+ }]" validation="required"></FormKit>
63
+
64
+ <FormKit type="l-input" label="From Address" name="mail_from" validation="email"></FormKit>
65
+ <FormKit type="l-input" label="From Name" name="mail_from_name"></FormKit>
66
+ <FormKit type="l-input" label="Reply To Address" name="mail_reply_to" validation="email">
67
+ </FormKit>
68
+ <FormKit type="l-input" label="Reply To Name" name="mail_reply_to_name"></FormKit>
69
+
70
+ <template v-if="value.mail_driver === 'smtp'">
71
+ <FormKit type="l-input" label="SMTP Host" name="mail_host"></FormKit>
72
+ <FormKit type="l-input" label="SMTP Port" name="mail_port"></FormKit>
73
+ <FormKit type="l-input" label="SMTP Username" name="mail_username"></FormKit>
74
+ <FormKit type="l-input" label="SMTP Password" name="mail_password"></FormKit>
75
+
76
+ <FormKit type="l-select" label="SMTP Encryption" name="mail_encryption" :options="[
77
+ { label: 'None', value: '' },
78
+ { label: 'SSL', value: 'ssl' },
79
+ { label: 'TLS', value: 'tls' },
80
+ ]"></FormKit>
81
+ </template>
82
+
83
+
84
+
85
+
86
+ </FormKit>
87
+
88
+ </template>
@@ -0,0 +1,52 @@
1
+ <script setup lang="ts">
2
+ import { q, m } from '#imports'
3
+ import { useQuasar } from 'quasar'
4
+
5
+ const $q = useQuasar()
6
+
7
+ const modelValue = defineModel<{
8
+ file_manager: string
9
+ revision: string
10
+ }>()
11
+
12
+ const onSubmit = async (d, form) => {
13
+ let data = [];
14
+ Object.keys(d).forEach((key) => {
15
+ data.push({
16
+ name: key,
17
+ value: d[key]
18
+ })
19
+ })
20
+ await m("updateAppConfigs", {
21
+ data
22
+ })
23
+ //update the modelValue
24
+ Object.keys(d).forEach((key) => {
25
+ modelValue.value[key] = d[key]
26
+ })
27
+
28
+ $q.notify({
29
+ message: "Settings saved",
30
+ color: "positive"
31
+ })
32
+ }
33
+
34
+
35
+ </script>
36
+
37
+ <template>
38
+ <FormKit type="l-form" :bordered="false" :value="{
39
+ file_manager: modelValue.file_manager,
40
+ revision: modelValue.revision
41
+ }" @submit="onSubmit">
42
+ <q-field label="File manager" stack-label>
43
+ <FormKit type="l-checkbox" label="Show" name="file_manager" true-value="1" false-value="0" />
44
+ </q-field>
45
+
46
+ <FormKit type="l-select" label="Revision" name="revision" use-input use-chips multiple hide-dropdown-icon
47
+ input-debounce="0" new-value-mode="add-unique" />
48
+
49
+
50
+ </FormKit>
51
+
52
+ </template>
@@ -0,0 +1,82 @@
1
+ <script setup lang="ts">
2
+ import { q, m } from '#imports'
3
+ import { useQuasar } from 'quasar'
4
+
5
+ const $q = useQuasar()
6
+
7
+ const modelValue = defineModel<{
8
+ password_contains_uppercase: string
9
+ password_contains_lowercase: string
10
+ password_contains_numeric: string
11
+ password_contains_symbol: string
12
+ password_min_length: string
13
+ two_factor_authentication: string
14
+ auth_lockout_duration: string
15
+ auth_lockout_attempts: string
16
+ access_token_expire: string
17
+ }>()
18
+
19
+ const onSubmit = async (d, form) => {
20
+ let data = [];
21
+ Object.keys(d).forEach((key) => {
22
+ data.push({
23
+ name: key,
24
+ value: d[key]
25
+ })
26
+ })
27
+ await m("updateAppConfigs", { data })
28
+ //update the modelValue
29
+ Object.keys(d).forEach((key) => {
30
+ modelValue.value[key] = d[key]
31
+ })
32
+
33
+ $q.notify({ message: "Settings saved", color: "positive" })
34
+ }
35
+
36
+ </script>
37
+
38
+ <template>
39
+
40
+ <FormKit type="l-form" :bordered="false" :value="{
41
+ password_contains_uppercase: modelValue.password_contains_uppercase,
42
+ password_contains_lowercase: modelValue.password_contains_lowercase,
43
+ password_contains_numeric: modelValue.password_contains_numeric,
44
+ password_contains_symbol: modelValue.password_contains_symbol,
45
+ password_min_length: modelValue.password_min_length,
46
+ two_factor_authentication: modelValue.two_factor_authentication,
47
+ auth_lockout_duration: modelValue.auth_lockout_duration,
48
+ auth_lockout_attempts: modelValue.auth_lockout_attempts,
49
+ access_token_expire: modelValue.access_token_expire
50
+
51
+ }" @submit="onSubmit">
52
+ <q-field label="Password policy" stack-label :color="$light.color">
53
+ <FormKit type="l-checkbox" label="Upper Case" name="password_contains_uppercase" true-value="1"
54
+ false-value="0" />
55
+ <FormKit type="l-checkbox" label="Lower Case" name="password_contains_lowercase" true-value="1"
56
+ false-value="0" />
57
+ <FormKit type="l-checkbox" label="Number" name="password_contains_numeric" true-value="1" false-value="0" />
58
+ <FormKit type="l-checkbox" label="Special Character" name="password_contains_symbol" true-value="1"
59
+ false-value="0" />
60
+ </q-field>
61
+
62
+ <FormKit type="l-input" label="Password min Length" name="password_min_length" validation="required|number">
63
+ </FormKit>
64
+
65
+
66
+ <q-field label="Two factor authentication" stack-label :color="$light.color">
67
+ <FormKit type="l-checkbox" label="Enable" name="two_factor_authentication" true-value="1" false-value="0" />
68
+ </q-field>
69
+
70
+ <FormKit label="Auth lockout duration" type="l-input" name="auth_lockout_duration"
71
+ hint="The number of minutes the user is locked out after the maximum number of failed login attempts. Default is 15 minutes."
72
+ validation="required" />
73
+
74
+ <FormKit label="Auth lockout attempts" type="l-input" name="auth_lockout_attempts"
75
+ hint="The number of failed login attempts before the user is locked out. Default is 5 attempts."
76
+ validation="required" />
77
+
78
+ <FormKit label="Access token expiration" type="l-input" name="access_token_expire"
79
+ hint="The access token expiration time in seconds. Default is 28800 seconds (8 hours)."
80
+ validation="required" />
81
+ </FormKit>
82
+ </template>
@@ -18,7 +18,16 @@ watch(route, (to, from) => {
18
18
 
19
19
  let app = null
20
20
  try {
21
- app = (await q({ app: ['company', 'companyLogo', 'logged', 'twoFactorAuthentication', 'googleClientId'] })).app;
21
+ app = (await q({
22
+ app: {
23
+ company: true,
24
+ companyLogo: true,
25
+ logged: true,
26
+ twoFactorAuthentication: true,
27
+ googleClientId: true,
28
+ forgetPasswordEnabled: true
29
+ }
30
+ })).app;
22
31
  light.setCompany(app.company);
23
32
  light.setCompanyLogo(app.companyLogo);
24
33
  } catch (e) {
@@ -41,8 +50,7 @@ provide('color', color)
41
50
  <q-layout v-if="!app.logged">
42
51
  <q-page-container class="bg-grey-2" style="color:#1f1f1f">
43
52
  <q-page padding>
44
- <l-login :two-factor-authentication="app.twoFactorAuthentication"
45
- :google-client-id="app.googleClientId"></l-login>
53
+ <l-login v-bind="app"></l-login>
46
54
  </q-page>
47
55
  </q-page-container>
48
56
  </q-layout>
@@ -53,7 +61,7 @@ provide('color', color)
53
61
  <slot name="header"></slot>
54
62
  </template>
55
63
 
56
-
64
+
57
65
 
58
66
 
59
67
 
@@ -5,15 +5,16 @@ import type { QCheckboxProps } from "quasar"
5
5
  export interface LCheckboxProps extends QCheckboxProps {
6
6
  }
7
7
 
8
+ const modelValue = defineModel();
9
+
8
10
  const emit = defineEmits(["update:modelValue"]);
9
11
 
10
12
  const props = defineProps<LCheckboxProps>()
11
13
 
12
14
  const localValue = computed({
13
- get: () => props.modelValue,
15
+ get: () => modelValue.value,
14
16
  set: (value) => {
15
- // popup.value.hide();
16
- emit('update:modelValue', value)
17
+ modelValue.value = value;
17
18
  }
18
19
  });
19
20
  </script>
@@ -3,24 +3,46 @@ import { useLight } from "#imports";
3
3
  import { ref, reactive, onMounted } from 'vue'
4
4
  import { useQuasar } from 'quasar';
5
5
  import { useI18n } from 'vue-i18n';
6
- import { m, notify, api } from '#imports';
6
+ import { m, api } from '#imports';
7
7
 
8
8
 
9
9
  const light = useLight();
10
10
 
11
11
  const props = defineProps({
12
12
  twoFactorAuthentication: Boolean,
13
- googleClientId: String
13
+ googleClientId: String,
14
+ forgetPasswordEnabled: Boolean
14
15
  })
15
16
 
16
- const i18n = useI18n();
17
+ const { t } = useI18n();
18
+
17
19
  const form1 = ref(null);
18
20
  const data = reactive({
19
21
  username: "", password: "", code: ""
20
22
  });
21
23
 
24
+
22
25
  const $q = useQuasar()
23
26
 
27
+ const loginWithCode = (username, password) => {
28
+ $q.dialog({
29
+ title: "Enter your code",
30
+ message: "Please enter your two factor authentication code (If you lost your authenticator, please contact your administrator)",
31
+ prompt: {
32
+ type: "text",
33
+ required: true
34
+ },
35
+ cancel: true,
36
+ persistent: true
37
+ }).onOk(async code => {
38
+ data.username = username;
39
+ data.password = password;
40
+ data.code = code;
41
+ submit();
42
+ });
43
+
44
+ }
45
+
24
46
  const submit = async () => {
25
47
  if (await form1.value.validate()) {
26
48
 
@@ -28,41 +50,63 @@ const submit = async () => {
28
50
  await api.auth.login(data.username, data.password, data.code)
29
51
  window.self.location.reload();
30
52
  } catch (e) {
31
- notify(e.message, "negative");
53
+ data.code = "";
54
+
55
+ if (e.message == "two factor authentication code is required") {
56
+
57
+ loginWithCode(data.username, data.password);
58
+ return;
59
+ }
60
+
61
+ $q.notify({
62
+ message: e.message,
63
+ color: "negative",
64
+ icon: "sym_o_error",
65
+ });
66
+
32
67
 
33
68
  }
34
69
  }
35
70
  }
36
71
 
37
- const forgetPassword = async () => {
72
+ const resetPassword = (username, code) => {
38
73
  $q.dialog({
39
- title: i18n.t("Forget password"),
40
- message: "Please enter your email address, we will send you a code to reset your password",
74
+ title: "Reset password",
75
+ message: "Please enter your new password",
41
76
  prompt: {
42
- type: "email",
43
- required: true,
77
+ model: "",
78
+ type: "password",
44
79
  isValid: (val) => {
45
- //test val is email
46
- const re = /\S+@\S+\.\S+/;
47
- return re.test(val);
80
+ return val.length > 0;
48
81
  }
49
82
  },
50
- cancel: true
51
- }).onOk(async email => {
52
- $q.loading.show();
83
+ cancel: true,
84
+ persistent: true
85
+ }).onOk(async password => {
53
86
  try {
54
- await api.auth.forgetPassword(email);
87
+ await api.auth.resetPassword(username, password, code);
88
+ $q.notify({
89
+ message: "Your password has been reset successfully",
90
+ color: "positive",
91
+ icon: "sym_o_check",
92
+ });
55
93
  } catch (e) {
56
94
  $q.notify({
57
95
  message: e.message,
58
96
  color: "negative",
59
97
  icon: "sym_o_error",
60
98
  });
61
- return;
62
- } finally {
63
- $q.loading.hide();
99
+ resetPassword(username, code);
64
100
  }
101
+ });
102
+ }
65
103
 
104
+
105
+ const forgetPassword = async () => {
106
+
107
+ $q.dialog({
108
+ component: resolveComponent("l-forget-password-dialog"),
109
+ }).onOk(async (data) => {
66
110
  $q.dialog({
67
111
  title: "Enter your code",
68
112
  message: "Please enter the code sent to your email, your code will expire in 10 minutes",
@@ -73,23 +117,8 @@ const forgetPassword = async () => {
73
117
  cancel: true,
74
118
  persistent: true
75
119
  }).onOk(async code => {
76
- if (await api.auth.verifyCode(email, code)) {
77
- $q.dialog({
78
- title: "Reset password",
79
- message: "Please enter your new password",
80
- required: true,
81
- prompt: {
82
- type: "password"
83
- },
84
- }).onOk(async password => {
85
- if (await api.auth.resetPassword(email, password, code)) {
86
- $q.notify({
87
- message: "Your password has been reset successfully",
88
- color: "positive",
89
- icon: "sym_o_check",
90
- });
91
- }
92
- });
120
+ if (await api.auth.verifyCode(data.username, code)) {
121
+ resetPassword(data.username, code);
93
122
  } else {
94
123
  $q.notify({
95
124
  message: "Your code is invalid",
@@ -14,7 +14,9 @@ const { error, errorMessage } = getErrorMessage(props.context.node);
14
14
 
15
15
  </script>
16
16
  <template>
17
- <l-checkbox v-model="value" :label="context.label" v-bind="context.attrs" :error="error" :error-message="errorMessage">
17
+
18
+ <l-checkbox v-model="value" :label="context.label" v-bind="context.attrs" :error="error"
19
+ :error-message="errorMessage">
18
20
  <template v-for="(s, name) in $slots" v-slot:[name]="props" :key="name">
19
21
  <slot :name="name" v-bind="props ?? {}"></slot>
20
22
  </template>
@@ -8,16 +8,6 @@ const route = useRoute();
8
8
  const router = useRouter();
9
9
  const quasar = useQuasar();
10
10
 
11
- declare module '@formkit/inputs' {
12
- interface FormKitInputProps<Props extends FormKitInputs<Props>> {
13
- lForm: {
14
- type: 'l-form';
15
- gutter?: 'none' | 'xs' | 'sm' | 'md' | 'lg' | 'xl';
16
- bordered?: boolean;
17
- };
18
- }
19
- }
20
-
21
11
 
22
12
  let props = defineProps({
23
13
  context: {
@@ -47,7 +47,6 @@ const onBlur = () => {
47
47
  error.value = false
48
48
  }
49
49
  }
50
-
51
50
  </script>
52
51
  <template>
53
52
  <l-input v-model="value" :label="context?.label" v-bind="context?.attrs" :error="error" :type="context?.inputType"
@@ -73,6 +73,13 @@ declare module '@formkit/inputs' {
73
73
  dense?: boolean;
74
74
  };
75
75
  }
76
+ interface FormKitInputProps<Props extends FormKitInputs<Props>> {
77
+ lForm: {
78
+ type: 'l-form';
79
+ gutter?: 'none' | 'xs' | 'sm' | 'md' | 'lg' | 'xl';
80
+ bordered?: boolean;
81
+ };
82
+ }
76
83
  }
77
84
  export declare const useLight: (options?: {
78
85
  color?: string;
@@ -118,4 +125,4 @@ export declare const useLight: (options?: {
118
125
  isGranted: (right?: string) => boolean;
119
126
  setPermissions: (permissions: Array<string>) => void;
120
127
  };
121
- export * from "./lib.js";
128
+ export * from "./lib/index.js";
@@ -1,9 +1,6 @@
1
1
  <script setup>
2
2
  import { ref } from 'vue'
3
- import { useQuasar } from 'quasar'
4
- import { reset } from "@formkit/core"
5
3
  import { q, m } from '#imports'
6
- const quasar = useQuasar()
7
4
 
8
5
  const { app } = await q({ app: { config: ["name", "value"] } })
9
6
  const obj = app.config.reduce((acc, cur) => {
@@ -11,163 +8,32 @@ const obj = app.config.reduce((acc, cur) => {
11
8
  return acc
12
9
  }, {});
13
10
 
14
- const fields = ["company", "company_logo",
15
- "password_contains_uppercase",
16
- "password_contains_lowercase",
17
- "password_contains_numeric",
18
- "password_contains_symbol",
19
- "password_min_length",
20
- "file_manager",
21
- "two_factor_authentication",
22
- "mode",
23
- "auth_lockout_duration",
24
- "auth_lockout_attempts",
25
- "access_token_expire",
26
- "copyright_year",
27
- "copyright_name",
28
- "revision"
29
- ];
30
-
31
11
  obj.revision = obj.revision ? obj.revision.split(',') : []
32
12
 
33
- //filter out fields that are not in the app.config table
34
- Object.keys(obj).forEach((key) => {
35
- if (!fields.includes(key)) {
36
- delete obj[key]
37
- }
38
- })
39
-
40
- const onSubmit = async (d, form) => {
41
- let data = [];
42
- Object.keys(d).forEach((key) => {
43
- data.push({
44
- name: key,
45
- value: d[key]
46
- })
47
- })
48
-
49
- await m("updateAppConfigs", { data })
50
- //show success
51
- quasar.notify({
52
- message: "Settings updated",
53
- color: "positive",
54
- icon: "check"
55
- })
56
-
57
- reset(form, d)
58
-
59
-
60
- }
61
-
62
-
63
- if (obj.mode != 'prod') {
64
- obj.mode = 'dev'
65
- } else {
66
- obj.mode = 'prod'
67
- }
68
-
69
13
  const tab = ref('general')
70
-
71
14
  </script>
72
15
  <template>
73
16
  <l-page>
74
17
 
75
18
  <l-card>
76
- <q-splitter unit="px" :model-value="120">
19
+ <q-splitter unit="px" :model-value="145">
77
20
  <template #before>
78
21
  <q-tabs v-model="tab" vertical :active-color="$light.color">
79
22
  <q-tab name="general" icon="sym_o_info" :label="$t('General')" />
80
23
  <q-tab name="security" icon="sym_o_security" :label="$t('Security')" />
81
24
  <q-tab name="Modules" icon="sym_o_apps" :label="$t('Modules')" />
82
- <q-tab name="Developer" icon="sym_o_code" :label="$t('Developer')" />
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 Password')" />
27
+ <q-tab name="developer" icon="sym_o_code" :label="$t('Developer')" />
83
28
  </q-tabs>
84
29
  </template>
85
30
  <template #after>
86
- <FormKit type="l-form" :bordered="false" :value="{
87
- company: obj.company,
88
- company_logo: obj.company_logo,
89
- copyright_name: obj.copyright_name,
90
- copyright_year: obj.copyright_year
91
- }" v-if="tab == 'general'" @submit="onSubmit">
92
- <FormKit type="l-input" label="Company" name="company" validation="required"></FormKit>
93
- <FormKit type="l-input" label="Company logo" name="company_logo"></FormKit>
94
-
95
- <FormKit label="Copyright name" type="l-input" name="copyright_name" validation="required" />
96
- <FormKit label="Copyright year" type="l-input" name="copyright_year" validation="required" />
97
-
98
- </FormKit>
99
-
100
- <FormKit type="l-form" :bordered="false" :value="{
101
- password_contains_uppercase: obj.password_contains_uppercase,
102
- password_contains_lowercase: obj.password_contains_lowercase,
103
- password_contains_numeric: obj.password_contains_numeric,
104
- password_contains_symbol: obj.password_contains_symbol,
105
- password_min_length: obj.password_min_length,
106
- two_factor_authentication: obj.two_factor_authentication,
107
- auth_lockout_duration: obj.auth_lockout_duration,
108
- auth_lockout_attempts: obj.auth_lockout_attempts,
109
- access_token_expire: obj.access_token_expire,
110
- }" v-if="tab == 'security'" @submit="onSubmit">
111
- <q-field label="Password policy" stack-label>
112
- <FormKit type="l-checkbox" label="Upper Case" name="password_contains_uppercase"
113
- true-value="1" false-value="0" />
114
- <FormKit type="l-checkbox" label="Lower Case" name="password_contains_lowercase"
115
- true-value="1" false-value="0" />
116
- <FormKit type="l-checkbox" label="Number" name="password_contains_numeric" true-value="1"
117
- false-value="0" />
118
- <FormKit type="l-checkbox" label="Special Character" name="password_contains_symbol"
119
- true-value="1" false-value="0" />
120
- </q-field>
121
-
122
- <FormKit type="l-input" label="Password min Length" name="password_min_length"
123
- validation="required|number">
124
- </FormKit>
125
-
126
-
127
- <q-field label="Two factor authentication" stack-label>
128
- <FormKit type="l-checkbox" label="Enable" name="two_factor_authentication" true-value="1"
129
- false-value="0" />
130
- </q-field>
131
-
132
- <FormKit label="Auth lockout duration" type="l-input" name="auth_lockout_duration"
133
- hint="The number of minutes the user is locked out after the maximum number of failed login attempts. Default is 15 minutes."
134
- validation="required" />
135
-
136
- <FormKit label="Auth lockout attempts" type="l-input" name="auth_lockout_attempts"
137
- hint="The number of failed login attempts before the user is locked out. Default is 5 attempts."
138
- validation="required" />
139
-
140
- <FormKit label="Access token expiration" type="l-input" name="access_token_expire"
141
- hint="The access token expiration time in seconds. Default is 28800 seconds (8 hours)."
142
- validation="required" />
143
- </FormKit>
144
-
145
- <FormKit type="l-form" :bordered="false" :value="{
146
- file_manager: obj.file_manager,
147
- revision: obj.revision
148
- }" v-if="tab == 'Modules'" @submit="onSubmit">
149
- <q-field label="File manager" stack-label>
150
- <FormKit type="l-checkbox" label="Show" name="file_manager" true-value="1"
151
- false-value="0" />
152
- </q-field>
153
-
154
- <FormKit type="l-select" label="Revision" name="revision" use-input use-chips multiple
155
- hide-dropdown-icon input-debounce="0" new-value-mode="add-unique" />
156
-
157
-
158
- </FormKit>
159
-
160
- <FormKit type="l-form" :bordered="false" :value="{
161
- mode: obj.mode,
162
-
163
- }" v-if="tab == 'Developer'" @submit="onSubmit">
164
- <FormKit label="Mode" type="l-select" :options="[
165
- { label: 'Production', value: 'prod' },
166
- { label: 'Development', value: 'dev' },
167
-
168
- ]" name="mode" validation="required">
169
- </FormKit>
170
- </FormKit>
31
+ <l-system-setting-general v-if="tab == 'general'" v-model="obj" />
32
+ <l-system-setting-mail v-if="tab == 'mail'" v-model="obj" />
33
+ <l-system-setting-security v-if="tab == 'security'" v-model="obj" />
34
+ <l-system-setting-modules v-if="tab == 'Modules'" v-model="obj" />
35
+ <l-system-setting-developer v-if="tab == 'developer'" v-model="obj" />
36
+ <l-system-setting-forget-password v-if="tab == 'forget-password'" v-model="obj" />
171
37
  </template>
172
38
  </q-splitter>
173
39
  </l-card>
@@ -1,7 +1,10 @@
1
1
  <script setup>
2
- import { useLight, getObject } from "#imports";
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
+
7
+ const $q = useQuasar();
5
8
  const route = useRoute();
6
9
 
7
10
  const obj = await getObject(["canUpdate"]);
@@ -11,6 +14,36 @@ const light = useLight();
11
14
  const tab = ref('overview');
12
15
  const id = route.params.user_id;
13
16
 
17
+ const reset2fa = async () => {
18
+
19
+ //confirm
20
+ await $q.dialog({
21
+ title: "Reset 2FA",
22
+ message: "Are you sure you want to reset 2FA?",
23
+ color: "negative",
24
+ ok: true,
25
+ cancel: true
26
+ }).onOk(async () => {
27
+ try {
28
+ await m('reset2FA', { id: parseInt(id) });
29
+ $q.notify({
30
+ type: 'positive',
31
+ message: '2FA reset successfully'
32
+ });
33
+ } catch (e) {
34
+ $q.notify({
35
+ type: 'negative',
36
+ message: e.message
37
+ });
38
+ }
39
+
40
+ })
41
+
42
+
43
+
44
+
45
+
46
+ }
14
47
 
15
48
  </script>
16
49
 
@@ -21,6 +54,7 @@ const id = route.params.user_id;
21
54
  <l-btn to="change-password" icon="sym_o_key" permission="user.changePassword"
22
55
  label="Change password"></l-btn>
23
56
  <l-btn to="update-role" icon="sym_o_people" permission="user.role.add" label="Update role"></l-btn>
57
+ <l-btn label="Reset 2FA" icon="sym_o_key" permission="user.reset2fa" @click="reset2fa"></l-btn>
24
58
  </template>
25
59
 
26
60
  <q-card flat bordered>
@@ -1,6 +1,8 @@
1
1
  <script setup>
2
2
  import { ref, reactive } from "vue"
3
+ import { useQuasar } from "quasar"
3
4
  import { q, m, notify } from '#imports'
5
+ const $q = useQuasar()
4
6
 
5
7
  const my = await q("my", ["twoFactorEnabled"])
6
8
  const my2FA = await m("my2FA", [])
@@ -25,6 +27,21 @@ if (my.twoFactorEnabled) {
25
27
  show.value = false;
26
28
  }
27
29
 
30
+ const onCopy = () => {
31
+ navigator.clipboard.writeText(my2FA.secret).then(() => {
32
+ $q.notify({
33
+ message: "Secret copied",
34
+ color: "green",
35
+ })
36
+ }, () => {
37
+ $q.notify({
38
+ message: "Failed to copy",
39
+ color: "red",
40
+ })
41
+
42
+ })
43
+ }
44
+
28
45
 
29
46
  </script>
30
47
  <template>
@@ -37,18 +54,33 @@ if (my.twoFactorEnabled) {
37
54
  <p>
38
55
  For Android user, install
39
56
  <a type="primary" target="_blank"
40
- href="https://play.google.com/store/apps/details?id=com.azure.authenticator">Authenticator</a>
57
+ href="https://play.google.com/store/apps/details?id=com.azure.authenticator">Microsoft
58
+ Authenticator</a>
59
+
60
+ or
61
+ <a type="primary" target="_blank"
62
+ href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2">Google
63
+ Authenticator</a>
64
+
41
65
  </p>
42
66
 
43
67
  <p>
44
68
  For iOS user, install
45
69
  <a type="primary" target="_blank"
46
- href="https://apps.apple.com/us/app/microsoft-authenticator/id983156458">Authenticator</a>
70
+ href="https://apps.apple.com/us/app/microsoft-authenticator/id983156458">Microsoft
71
+ Authenticator</a>
72
+ or
73
+ <a type="primary" target="_blank"
74
+ href="https://apps.apple.com/us/app/google-authenticator/id388497605">Google
75
+ Authenticator</a>
76
+
47
77
  </p>
48
78
  </div>
49
79
  <q-img :src="my2FA.image" width="250px" />
50
80
  <p>
51
- Secret : {{ my2FA.secret }}
81
+ Secret : <strong>{{ my2FA.secret }}</strong>
82
+
83
+ <q-btn flat round dense icon="sym_o_content_copy" @click="onCopy" />
52
84
  </p>
53
85
 
54
86
  <l-input v-model="obj.code" label="Code"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hostlink/nuxt-light",
3
- "version": "1.15.4",
3
+ "version": "1.17.0",
4
4
  "description": "HostLink Nuxt Light Framework",
5
5
  "repository": {
6
6
  "type": "git",
@@ -33,7 +33,7 @@
33
33
  },
34
34
  "dependencies": {
35
35
  "@formkit/drag-and-drop": "^0.1.6",
36
- "@hostlink/light": "^2.0.1",
36
+ "@hostlink/light": "^2.0.2",
37
37
  "@nuxt/kit": "^3.7.4",
38
38
  "@nuxt/module-builder": "^0.8.3",
39
39
  "@quasar/extras": "^1.16.11",