@goweekdays/layer-core 1.0.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/.changeset/README.md +8 -0
- package/.changeset/config.json +11 -0
- package/.github/workflows/main.yml +19 -0
- package/.github/workflows/publish.yml +39 -0
- package/CHANGELOG.md +7 -0
- package/README.md +75 -0
- package/app/app.vue +8 -0
- package/app/assets/fonts/ProductSans-Black.ttf +0 -0
- package/app/assets/fonts/ProductSans-BlackItalic.ttf +0 -0
- package/app/assets/fonts/ProductSans-Bold.ttf +0 -0
- package/app/assets/fonts/ProductSans-BoldItalic.ttf +0 -0
- package/app/assets/fonts/ProductSans-Italic.ttf +0 -0
- package/app/assets/fonts/ProductSans-Light.ttf +0 -0
- package/app/assets/fonts/ProductSans-LightItalic.ttf +0 -0
- package/app/assets/fonts/ProductSans-Medium.ttf +0 -0
- package/app/assets/fonts/ProductSans-MediumItalic.ttf +0 -0
- package/app/assets/fonts/ProductSans-Regular.ttf +0 -0
- package/app/assets/fonts/ProductSans-Thin.ttf +0 -0
- package/app/assets/fonts/ProductSans-ThinItalic.ttf +0 -0
- package/app/assets/main.css +10 -0
- package/app/assets/settings.scss +89 -0
- package/app/components/Input/ListGroupSelection.vue +107 -0
- package/app/components/Input/Password.vue +35 -0
- package/app/composables/useLocalAuth.ts +131 -0
- package/app/composables/useLocalSetup.ts +34 -0
- package/app/composables/useMember.ts +100 -0
- package/app/composables/useRole.ts +87 -0
- package/app/composables/useUtils.ts +308 -0
- package/app/layouts/plain.vue +7 -0
- package/app/middleware/01.auth.ts +14 -0
- package/app/middleware/org.ts +13 -0
- package/app/pages/forgot-password.vue +76 -0
- package/app/pages/index.vue +3 -0
- package/app/pages/login.vue +134 -0
- package/app/pages/logout.vue +18 -0
- package/app/pages/privacy-policy.vue +23 -0
- package/app/pages/refund-policy.vue +23 -0
- package/app/pages/sign-up.vue +141 -0
- package/app/pages/terms-and-conditions.vue +23 -0
- package/app/plugins/member.client.ts +66 -0
- package/app/plugins/secure.client.ts +34 -0
- package/app/plugins/vuetify.ts +54 -0
- package/app/public/background.png +0 -0
- package/app/public/favicon.svg +1 -0
- package/app/public/logo.png +0 -0
- package/app/public/pwa-192x192.png +0 -0
- package/app/public/pwa-512x512.png +0 -0
- package/app/public/robots.txt +1 -0
- package/app/types/local.d.ts +113 -0
- package/app/types/member.d.ts +13 -0
- package/app/types/role.d.ts +12 -0
- package/content/privacy-policy.md +151 -0
- package/content/refund-policy.md +65 -0
- package/content/terms-and-conditions.md +137 -0
- package/content.config.ts +10 -0
- package/nuxt.config.ts +45 -0
- package/package.json +32 -0
- package/public/favicon.ico +0 -0
- package/public/robots.txt +2 -0
- package/tsconfig.json +18 -0
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# Changesets
|
|
2
|
+
|
|
3
|
+
Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
|
|
4
|
+
with multi-package repos, or single-package repos to help you version and publish your code. You can
|
|
5
|
+
find the full documentation for it [in our repository](https://github.com/changesets/changesets)
|
|
6
|
+
|
|
7
|
+
We have a quick list of common questions to get you started engaging with this project in
|
|
8
|
+
[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://unpkg.com/@changesets/config@3.0.3/schema.json",
|
|
3
|
+
"changelog": "@changesets/cli/changelog",
|
|
4
|
+
"commit": false,
|
|
5
|
+
"fixed": [],
|
|
6
|
+
"linked": [],
|
|
7
|
+
"access": "restricted",
|
|
8
|
+
"baseBranch": "main",
|
|
9
|
+
"updateInternalDependencies": "patch",
|
|
10
|
+
"ignore": []
|
|
11
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
on:
|
|
3
|
+
push:
|
|
4
|
+
branches:
|
|
5
|
+
- main
|
|
6
|
+
paths:
|
|
7
|
+
- ".changeset/**"
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
build:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
- uses: actions/setup-node@v4
|
|
15
|
+
with:
|
|
16
|
+
node-version: 20.x
|
|
17
|
+
cache: "yarn"
|
|
18
|
+
- run: yarn install --frozen-lockfile && yarn postinstall
|
|
19
|
+
- run: yarn generate
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
name: Publish
|
|
2
|
+
on:
|
|
3
|
+
workflow_run:
|
|
4
|
+
workflows: [CI]
|
|
5
|
+
branches: [main]
|
|
6
|
+
types: [completed]
|
|
7
|
+
|
|
8
|
+
concurrency: ${{ github.workflow }}-${{ github.ref }}
|
|
9
|
+
|
|
10
|
+
permissions:
|
|
11
|
+
contents: write
|
|
12
|
+
pull-requests: write
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
publish:
|
|
16
|
+
if: ${{ github.event.workflow_run.conclusion == 'success' }}
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v4
|
|
20
|
+
|
|
21
|
+
- uses: actions/setup-node@v4
|
|
22
|
+
with:
|
|
23
|
+
node-version: 20.x
|
|
24
|
+
cache: "yarn" # Use yarn for caching
|
|
25
|
+
cache-dependency-path: yarn.lock # Cache Yarn lockfile
|
|
26
|
+
|
|
27
|
+
- run: yarn install --immutable # Install dependencies with immutable lockfile
|
|
28
|
+
|
|
29
|
+
- name: Create .npmrc for publishing
|
|
30
|
+
run: |
|
|
31
|
+
echo '//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}' > ~/.npmrc
|
|
32
|
+
|
|
33
|
+
- name: Create Release Pull Request or Publish
|
|
34
|
+
id: changesets
|
|
35
|
+
uses: changesets/action@v1
|
|
36
|
+
with:
|
|
37
|
+
publish: yarn release # Use yarn for publishing
|
|
38
|
+
env:
|
|
39
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
package/CHANGELOG.md
ADDED
package/README.md
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# Nuxt Minimal Starter
|
|
2
|
+
|
|
3
|
+
Look at the [Nuxt documentation](https://nuxt.com/docs/getting-started/introduction) to learn more.
|
|
4
|
+
|
|
5
|
+
## Setup
|
|
6
|
+
|
|
7
|
+
Make sure to install dependencies:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# npm
|
|
11
|
+
npm install
|
|
12
|
+
|
|
13
|
+
# pnpm
|
|
14
|
+
pnpm install
|
|
15
|
+
|
|
16
|
+
# yarn
|
|
17
|
+
yarn install
|
|
18
|
+
|
|
19
|
+
# bun
|
|
20
|
+
bun install
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Development Server
|
|
24
|
+
|
|
25
|
+
Start the development server on `http://localhost:3000`:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
# npm
|
|
29
|
+
npm run dev
|
|
30
|
+
|
|
31
|
+
# pnpm
|
|
32
|
+
pnpm dev
|
|
33
|
+
|
|
34
|
+
# yarn
|
|
35
|
+
yarn dev
|
|
36
|
+
|
|
37
|
+
# bun
|
|
38
|
+
bun run dev
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Production
|
|
42
|
+
|
|
43
|
+
Build the application for production:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
# npm
|
|
47
|
+
npm run build
|
|
48
|
+
|
|
49
|
+
# pnpm
|
|
50
|
+
pnpm build
|
|
51
|
+
|
|
52
|
+
# yarn
|
|
53
|
+
yarn build
|
|
54
|
+
|
|
55
|
+
# bun
|
|
56
|
+
bun run build
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Locally preview production build:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
# npm
|
|
63
|
+
npm run preview
|
|
64
|
+
|
|
65
|
+
# pnpm
|
|
66
|
+
pnpm preview
|
|
67
|
+
|
|
68
|
+
# yarn
|
|
69
|
+
yarn preview
|
|
70
|
+
|
|
71
|
+
# bun
|
|
72
|
+
bun run preview
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Check out the [deployment documentation](https://nuxt.com/docs/getting-started/deployment) for more information.
|
package/app/app.vue
ADDED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
input[type="number"]::-webkit-inner-spin-button,
|
|
2
|
+
input[type="number"]::-webkit-outer-spin-button {
|
|
3
|
+
-webkit-appearance: none;
|
|
4
|
+
margin: 0;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.v-list-item--density-default.v-list-item--three-line .v-list-item__prepend,
|
|
8
|
+
.v-list-item--density-default.v-list-item--three-line .v-list-item__append {
|
|
9
|
+
padding-top: 0 !important;
|
|
10
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
@use 'vuetify/settings' with (
|
|
2
|
+
// variables go here
|
|
3
|
+
$body-font-family: ('ProductSans', sans-serif),
|
|
4
|
+
$heading-font-family: ('ProductSans', sans-serif)
|
|
5
|
+
);
|
|
6
|
+
|
|
7
|
+
@font-face {
|
|
8
|
+
font-family: 'ProductSans';
|
|
9
|
+
src: url('/assets/fonts/ProductSans-Black.ttf') format('truetype');
|
|
10
|
+
font-weight: 900;
|
|
11
|
+
font-style: normal;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
@font-face {
|
|
15
|
+
font-family: 'ProductSans';
|
|
16
|
+
src: url('/assets/fonts/ProductSans-BlackItalic.ttf') format('truetype');
|
|
17
|
+
font-weight: 900;
|
|
18
|
+
font-style: italic;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
@font-face {
|
|
22
|
+
font-family: 'ProductSans';
|
|
23
|
+
src: url('/assets/fonts/ProductSans-Bold.ttf') format('truetype');
|
|
24
|
+
font-weight: bold;
|
|
25
|
+
font-style: normal;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
@font-face {
|
|
29
|
+
font-family: 'ProductSans';
|
|
30
|
+
src: url('/assets/fonts/ProductSans-BoldItalic.ttf') format('truetype');
|
|
31
|
+
font-weight: bold;
|
|
32
|
+
font-style: italic;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
@font-face {
|
|
36
|
+
font-family: 'ProductSans';
|
|
37
|
+
src: url('/assets/fonts/ProductSans-Italic.ttf') format('truetype');
|
|
38
|
+
font-weight: normal;
|
|
39
|
+
font-style: italic;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
@font-face {
|
|
43
|
+
font-family: 'ProductSans';
|
|
44
|
+
src: url('/assets/fonts/ProductSans-Light.ttf') format('truetype');
|
|
45
|
+
font-weight: 300;
|
|
46
|
+
font-style: normal;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
@font-face {
|
|
50
|
+
font-family: 'ProductSans';
|
|
51
|
+
src: url('/assets/fonts/ProductSans-LightItalic.ttf') format('truetype');
|
|
52
|
+
font-weight: 300;
|
|
53
|
+
font-style: italic;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
@font-face {
|
|
57
|
+
font-family: 'ProductSans';
|
|
58
|
+
src: url('/assets/fonts/ProductSans-Medium.ttf') format('truetype');
|
|
59
|
+
font-weight: 500;
|
|
60
|
+
font-style: normal;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
@font-face {
|
|
64
|
+
font-family: 'ProductSans';
|
|
65
|
+
src: url('/assets/fonts/ProductSans-MediumItalic.ttf') format('truetype');
|
|
66
|
+
font-weight: 500;
|
|
67
|
+
font-style: italic;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
@font-face {
|
|
71
|
+
font-family: 'ProductSans';
|
|
72
|
+
src: url('/assets/fonts/ProductSans-Regular.ttf') format('truetype');
|
|
73
|
+
font-weight: normal;
|
|
74
|
+
font-style: normal;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
@font-face {
|
|
78
|
+
font-family: 'ProductSans';
|
|
79
|
+
src: url('/assets/fonts/ProductSans-Thin.ttf') format('truetype');
|
|
80
|
+
font-weight: 100;
|
|
81
|
+
font-style: normal;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
@font-face {
|
|
85
|
+
font-family: 'ProductSans';
|
|
86
|
+
src: url('/assets/fonts/ProductSans-ThinItalic.ttf') format('truetype');
|
|
87
|
+
font-weight: 100;
|
|
88
|
+
font-style: italic;
|
|
89
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-input v-bind="attrs">
|
|
3
|
+
<v-card width="100%" v-bind="attrs">
|
|
4
|
+
<v-list
|
|
5
|
+
v-model:selected="selected"
|
|
6
|
+
lines="two"
|
|
7
|
+
:select-strategy="attrs.readonly ? 'classic' : 'leaf'"
|
|
8
|
+
class="pa-0"
|
|
9
|
+
density="compact"
|
|
10
|
+
read-only
|
|
11
|
+
open-strategy="single"
|
|
12
|
+
>
|
|
13
|
+
<template name="prepend"></template>
|
|
14
|
+
|
|
15
|
+
<template
|
|
16
|
+
v-for="(permission, permissionKey, permissionIndex) in props.items"
|
|
17
|
+
:key="permissionKey"
|
|
18
|
+
>
|
|
19
|
+
<v-divider v-if="permissionIndex > 0"></v-divider>
|
|
20
|
+
<v-list-group :value="permissionKey" fluid>
|
|
21
|
+
<template v-slot:activator="{ props }">
|
|
22
|
+
<v-list-item v-bind="props" density="compact">
|
|
23
|
+
<span class="text-capitalize">
|
|
24
|
+
{{ String(permissionKey).replace(/-/g, " ") }}
|
|
25
|
+
</span>
|
|
26
|
+
|
|
27
|
+
<template #prepend>
|
|
28
|
+
<v-chip class="mr-2" small>
|
|
29
|
+
{{ selectedActionCount(String(permissionKey)) }}
|
|
30
|
+
</v-chip>
|
|
31
|
+
</template>
|
|
32
|
+
</v-list-item>
|
|
33
|
+
</template>
|
|
34
|
+
|
|
35
|
+
<template v-for="(item, itemKey) in permission" :key="itemKey">
|
|
36
|
+
<v-divider></v-divider>
|
|
37
|
+
<v-list-item v-if="attrs.readonly" density="compact">
|
|
38
|
+
<template #title class="pl-2">
|
|
39
|
+
<span class="text-subtitle-2 text-capitalize">
|
|
40
|
+
{{ String(itemKey).replace(/-/g, " ") }}
|
|
41
|
+
</span>
|
|
42
|
+
</template>
|
|
43
|
+
|
|
44
|
+
<template #subtitle class="pl-2">
|
|
45
|
+
<span class="text-subtitle-2">{{ item.description }}</span>
|
|
46
|
+
</template>
|
|
47
|
+
</v-list-item>
|
|
48
|
+
|
|
49
|
+
<v-list-item
|
|
50
|
+
v-else
|
|
51
|
+
:value="`${permissionKey}:${itemKey}`"
|
|
52
|
+
density="compact"
|
|
53
|
+
>
|
|
54
|
+
<template #title class="pl-2">
|
|
55
|
+
<span class="text-subtitle-2 text-capitalize">
|
|
56
|
+
{{ String(itemKey).replace(/-/g, " ") }}
|
|
57
|
+
</span>
|
|
58
|
+
</template>
|
|
59
|
+
|
|
60
|
+
<template #subtitle class="pl-2">
|
|
61
|
+
<span class="text-subtitle-2 text-capitalize">
|
|
62
|
+
{{ String(item.description).replace(/-/g, " ") }}
|
|
63
|
+
</span>
|
|
64
|
+
</template>
|
|
65
|
+
|
|
66
|
+
<template #prepend="{ isSelected }">
|
|
67
|
+
<v-list-item-action start class="pl-1">
|
|
68
|
+
<v-checkbox-btn :model-value="isSelected"></v-checkbox-btn>
|
|
69
|
+
</v-list-item-action>
|
|
70
|
+
</template>
|
|
71
|
+
</v-list-item>
|
|
72
|
+
</template>
|
|
73
|
+
</v-list-group>
|
|
74
|
+
</template>
|
|
75
|
+
|
|
76
|
+
<slot v-if="noData" name="no-data">
|
|
77
|
+
<v-list-item>
|
|
78
|
+
<v-list-item-title>No data</v-list-item-title>
|
|
79
|
+
</v-list-item>
|
|
80
|
+
</slot>
|
|
81
|
+
|
|
82
|
+
<template name="append"></template>
|
|
83
|
+
</v-list>
|
|
84
|
+
</v-card>
|
|
85
|
+
</v-input>
|
|
86
|
+
</template>
|
|
87
|
+
|
|
88
|
+
<script setup lang="ts">
|
|
89
|
+
const selected = defineModel<Array<string>>({ default: [] });
|
|
90
|
+
const attrs = useAttrs();
|
|
91
|
+
const props = defineProps({
|
|
92
|
+
items: {
|
|
93
|
+
type: Object,
|
|
94
|
+
required: true,
|
|
95
|
+
default: () => ({}),
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
const selectedActionCount = (resource: string) => {
|
|
100
|
+
return selected.value.filter((permission) => permission.startsWith(resource))
|
|
101
|
+
.length;
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const noData = computed(() => {
|
|
105
|
+
return Object.keys(props.items).length === 0;
|
|
106
|
+
});
|
|
107
|
+
</script>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-text-field
|
|
3
|
+
id="_password"
|
|
4
|
+
:model-value="modelValue"
|
|
5
|
+
:type="showPassword ? 'text' : 'password'"
|
|
6
|
+
:append-inner-icon="showPassword ? 'mdi-eye-off' : 'mdi-eye'"
|
|
7
|
+
@click:append-inner="togglePasswordVisibility"
|
|
8
|
+
@update:model-value="updatePassword"
|
|
9
|
+
v-bind="$attrs"
|
|
10
|
+
autocomplete="off"
|
|
11
|
+
/>
|
|
12
|
+
</template>
|
|
13
|
+
|
|
14
|
+
<script setup lang="ts">
|
|
15
|
+
// Define the props for the v-model
|
|
16
|
+
defineProps({
|
|
17
|
+
modelValue: {
|
|
18
|
+
type: String,
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
// Define emit for updating v-model
|
|
23
|
+
const emit = defineEmits(["update:modelValue"]);
|
|
24
|
+
|
|
25
|
+
// Control password visibility
|
|
26
|
+
const showPassword = ref(false);
|
|
27
|
+
const togglePasswordVisibility = () => {
|
|
28
|
+
showPassword.value = !showPassword.value;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
// Handle input update and emit to parent
|
|
32
|
+
const updatePassword = (value: string) => {
|
|
33
|
+
emit("update:modelValue", value);
|
|
34
|
+
};
|
|
35
|
+
</script>
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
export default function useLocalAuth() {
|
|
2
|
+
const { cookieConfig } = useLocalSetup();
|
|
3
|
+
|
|
4
|
+
const currentUser = useState<TUser | null>("currentUser", () => null);
|
|
5
|
+
const membership = useState<TMember | null>("membership", () => null);
|
|
6
|
+
const permissions = useState<string[]>("permissions", () => []);
|
|
7
|
+
|
|
8
|
+
const hasPermission = (permissionKey: string): ComputedRef<boolean> => {
|
|
9
|
+
return computed(() => {
|
|
10
|
+
if (!permissions.value.length) return false;
|
|
11
|
+
return permissions.value.some((perm) => perm === permissionKey);
|
|
12
|
+
});
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
function authenticate() {
|
|
16
|
+
if (currentUser.value) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const user = useCookie("user", cookieConfig).value;
|
|
21
|
+
|
|
22
|
+
const { data: getCurrentUserReq, error: getCurrentUserErr } =
|
|
23
|
+
useLazyAsyncData("get-current-user", () =>
|
|
24
|
+
$fetch<TUser>(`/api/users/id/${user}`)
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
watchEffect(() => {
|
|
28
|
+
if (getCurrentUserReq.value) {
|
|
29
|
+
currentUser.value = getCurrentUserReq.value;
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
watchEffect(() => {
|
|
34
|
+
if (getCurrentUserErr.value) {
|
|
35
|
+
// Redirect to login page if user authentication fails
|
|
36
|
+
navigateTo({ name: "index" });
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async function login({ email = "", password = "", role = "" }) {
|
|
42
|
+
return $fetch<Record<string, any>>("/api/auth/login", {
|
|
43
|
+
method: "POST",
|
|
44
|
+
body: JSON.stringify({ email, password, role }),
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function logout() {
|
|
49
|
+
useCookie("sid", cookieConfig).value = null;
|
|
50
|
+
useCookie("user", cookieConfig).value = null;
|
|
51
|
+
useCookie("role", cookieConfig).value = null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async function getCurrentUser() {
|
|
55
|
+
const user = useCookie("user", cookieConfig).value;
|
|
56
|
+
if (!user) return null;
|
|
57
|
+
try {
|
|
58
|
+
const _user = await $fetch<TUser>(`/api/users/id/${user}`, {
|
|
59
|
+
method: "GET",
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
currentUser.value = _user;
|
|
63
|
+
return _user;
|
|
64
|
+
} catch (error) {
|
|
65
|
+
console.log("Error fetching current user:", error);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async function forgotPassword(email: string) {
|
|
70
|
+
if (!email) {
|
|
71
|
+
throw new Error("Email is required for password reset request.");
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
const response = await $fetch("/api/auth/forget-password", {
|
|
76
|
+
method: "POST",
|
|
77
|
+
body: JSON.stringify({ email }),
|
|
78
|
+
headers: { "Content-Type": "application/json" },
|
|
79
|
+
});
|
|
80
|
+
return response;
|
|
81
|
+
} catch (error) {
|
|
82
|
+
console.error("Error in password reset request:", error);
|
|
83
|
+
throw error;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async function resetPassword(
|
|
88
|
+
otp: string,
|
|
89
|
+
newPassword: string,
|
|
90
|
+
passwordConfirmation: string
|
|
91
|
+
) {
|
|
92
|
+
try {
|
|
93
|
+
return await $fetch("/api/auth/reset-password", {
|
|
94
|
+
method: "POST",
|
|
95
|
+
body: JSON.stringify({ otp, newPassword, passwordConfirmation }),
|
|
96
|
+
headers: { "Content-Type": "application/json" },
|
|
97
|
+
});
|
|
98
|
+
} catch (error) {
|
|
99
|
+
console.error("Error resetting password:", error);
|
|
100
|
+
throw error;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function verify(id: string) {
|
|
105
|
+
return $fetch<TKeyValuePair>(`/api/auth/verify/${id}`, {
|
|
106
|
+
method: "GET",
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function signUp(email: string, referral: string) {
|
|
111
|
+
return $fetch<TKeyValuePair>("/api/auth/sign-up", {
|
|
112
|
+
method: "POST",
|
|
113
|
+
body: { email, referral },
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return {
|
|
118
|
+
currentUser,
|
|
119
|
+
membership,
|
|
120
|
+
permissions,
|
|
121
|
+
hasPermission,
|
|
122
|
+
authenticate,
|
|
123
|
+
login,
|
|
124
|
+
logout,
|
|
125
|
+
getCurrentUser,
|
|
126
|
+
forgotPassword,
|
|
127
|
+
resetPassword,
|
|
128
|
+
verify,
|
|
129
|
+
signUp,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export default function useLocal() {
|
|
2
|
+
const { DOMAIN } = useRuntimeConfig().public;
|
|
3
|
+
|
|
4
|
+
const cookieConfig = {
|
|
5
|
+
domain: DOMAIN ?? "localhost",
|
|
6
|
+
secure: true,
|
|
7
|
+
maxAge: 30 * 24 * 60 * 60,
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const drawer = useState("drawer", () => true);
|
|
11
|
+
|
|
12
|
+
const apps = computed(() => {
|
|
13
|
+
return [];
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
function redirect(link: string, page?: string) {
|
|
17
|
+
const href = page ? `${link}/${page}` : link;
|
|
18
|
+
|
|
19
|
+
window.location.href = href;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const headerSearch = useState("headerSearch", () => "");
|
|
23
|
+
|
|
24
|
+
const landingPage = useCookie("landingPage", cookieConfig);
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
cookieConfig,
|
|
28
|
+
drawer,
|
|
29
|
+
apps,
|
|
30
|
+
redirect,
|
|
31
|
+
headerSearch,
|
|
32
|
+
landingPage,
|
|
33
|
+
};
|
|
34
|
+
}
|