@adminforth/open-signup 1.0.9 → 1.1.1
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/.woodpecker/buildRelease.sh +13 -0
- package/.woodpecker/buildSlackNotify.sh +44 -0
- package/.woodpecker/release.yml +42 -0
- package/CHANGELOG.md +5 -0
- package/LICENSE +21 -0
- package/build.log +12 -0
- package/custom/SignupPage.vue +14 -9
- package/custom/tsconfig.json +19 -0
- package/dist/custom/SignupPage.vue +14 -9
- package/dist/custom/tsconfig.json +19 -0
- package/dist/index.js +45 -11
- package/index.ts +49 -14
- package/package.json +42 -6
- package/types.ts +9 -0
- package/Changelog.md +0 -15
- package/dist/custom/ResetPassword.vue +0 -295
- package/dist/custom/ResetPasswordUnderLogin.vue +0 -13
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
|
|
2
|
+
#!/bin/bash
|
|
3
|
+
|
|
4
|
+
# write npm run output both to console and to build.log
|
|
5
|
+
npm run build 2>&1 | tee build.log
|
|
6
|
+
build_status=${PIPESTATUS[0]}
|
|
7
|
+
|
|
8
|
+
# if exist status from the npm run build is not 0
|
|
9
|
+
# then exit with the status code from the npm run build
|
|
10
|
+
if [ $build_status -ne 0 ]; then
|
|
11
|
+
echo "Build failed. Exiting with status code $build_status"
|
|
12
|
+
exit $build_status
|
|
13
|
+
fi
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
|
|
3
|
+
set -x
|
|
4
|
+
|
|
5
|
+
COMMIT_SHORT_SHA=$(echo $CI_COMMIT_SHA | cut -c1-8)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
if [ "$CI_STEP_STATUS" = "success" ]; then
|
|
9
|
+
MESSAGE="Did a build without issues on \`$CI_REPO_NAME/$CI_COMMIT_BRANCH\`. Commit: _${CI_COMMIT_MESSAGE}_ (<$CI_COMMIT_URL|$COMMIT_SHORT_SHA>)"
|
|
10
|
+
|
|
11
|
+
curl -s -X POST -H "Content-Type: application/json" -d '{
|
|
12
|
+
"username": "'"$CI_COMMIT_AUTHOR"'",
|
|
13
|
+
"icon_url": "'"$CI_COMMIT_AUTHOR_AVATAR"'",
|
|
14
|
+
"attachments": [
|
|
15
|
+
{
|
|
16
|
+
"mrkdwn_in": ["text", "pretext"],
|
|
17
|
+
"color": "#36a64f",
|
|
18
|
+
"text": "'"$MESSAGE"'"
|
|
19
|
+
}
|
|
20
|
+
]
|
|
21
|
+
}' "$DEVELOPERS_SLACK_WEBHOOK"
|
|
22
|
+
exit 0
|
|
23
|
+
fi
|
|
24
|
+
export BUILD_LOG=$(cat ./build.log)
|
|
25
|
+
|
|
26
|
+
BUILD_LOG=$(echo $BUILD_LOG | sed 's/"/\\"/g')
|
|
27
|
+
|
|
28
|
+
MESSAGE="Broke \`$CI_REPO_NAME/$CI_COMMIT_BRANCH\` with commit _${CI_COMMIT_MESSAGE}_ (<$CI_COMMIT_URL|$COMMIT_SHORT_SHA>)"
|
|
29
|
+
CODE_BLOCK="\`\`\`$BUILD_LOG\n\`\`\`"
|
|
30
|
+
|
|
31
|
+
echo "Sending slack message to developers $MESSAGE"
|
|
32
|
+
# Send the message
|
|
33
|
+
curl -sS -X POST -H "Content-Type: application/json" -d '{
|
|
34
|
+
"username": "'"$CI_COMMIT_AUTHOR"'",
|
|
35
|
+
"icon_url": "'"$CI_COMMIT_AUTHOR_AVATAR"'",
|
|
36
|
+
"attachments": [
|
|
37
|
+
{
|
|
38
|
+
"mrkdwn_in": ["text", "pretext"],
|
|
39
|
+
"color": "#8A1C12",
|
|
40
|
+
"text": "'"$CODE_BLOCK"'",
|
|
41
|
+
"pretext": "'"$MESSAGE"'"
|
|
42
|
+
}
|
|
43
|
+
]
|
|
44
|
+
}' "$DEVELOPERS_SLACK_WEBHOOK" 2>&1
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
clone:
|
|
2
|
+
git:
|
|
3
|
+
image: woodpeckerci/plugin-git
|
|
4
|
+
settings:
|
|
5
|
+
partial: false
|
|
6
|
+
depth: 5
|
|
7
|
+
|
|
8
|
+
steps:
|
|
9
|
+
init-secrets:
|
|
10
|
+
when:
|
|
11
|
+
- event: push
|
|
12
|
+
image: infisical/cli
|
|
13
|
+
environment:
|
|
14
|
+
INFISICAL_TOKEN:
|
|
15
|
+
from_secret: VAULT_TOKEN
|
|
16
|
+
commands:
|
|
17
|
+
- infisical export --domain https://vault.devforth.io/api --format=dotenv-export --env="prod" > /woodpecker/deploy.vault.env
|
|
18
|
+
secrets:
|
|
19
|
+
- VAULT_TOKEN
|
|
20
|
+
|
|
21
|
+
release:
|
|
22
|
+
image: node:20
|
|
23
|
+
when:
|
|
24
|
+
- event: push
|
|
25
|
+
commands:
|
|
26
|
+
- apt update && apt install -y rsync
|
|
27
|
+
- export $(cat /woodpecker/deploy.vault.env | xargs)
|
|
28
|
+
- npm clean-install
|
|
29
|
+
- /bin/bash ./.woodpecker/buildRelease.sh
|
|
30
|
+
- npm audit signatures
|
|
31
|
+
- npx semantic-release
|
|
32
|
+
|
|
33
|
+
slack-on-failure:
|
|
34
|
+
when:
|
|
35
|
+
- event: push
|
|
36
|
+
status: [failure, success]
|
|
37
|
+
- event: push
|
|
38
|
+
image: curlimages/curl
|
|
39
|
+
commands:
|
|
40
|
+
- export $(cat /woodpecker/deploy.vault.env | xargs)
|
|
41
|
+
- /bin/sh ./.woodpecker/buildSlackNotify.sh
|
|
42
|
+
|
package/CHANGELOG.md
ADDED
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Devforth.io
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/build.log
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
|
|
2
|
+
> @adminforth/open-signup@1.0.9 build
|
|
3
|
+
> tsc && rsync -av --exclude 'node_modules' custom dist/
|
|
4
|
+
|
|
5
|
+
sending incremental file list
|
|
6
|
+
custom/
|
|
7
|
+
custom/SignupPage.vue
|
|
8
|
+
custom/SignupUnderLogin.vue
|
|
9
|
+
custom/tsconfig.json
|
|
10
|
+
|
|
11
|
+
sent 14,444 bytes received 77 bytes 29,042.00 bytes/sec
|
|
12
|
+
total size is 14,155 speedup is 0.97
|
package/custom/SignupPage.vue
CHANGED
|
@@ -133,15 +133,16 @@
|
|
|
133
133
|
|
|
134
134
|
|
|
135
135
|
<script setup lang="ts">
|
|
136
|
-
import { onMounted, ref, computed, Ref, onBeforeMount } from 'vue';
|
|
136
|
+
import { onMounted, ref, computed, Ref, onBeforeMount, nextTick } from 'vue';
|
|
137
137
|
import { useCoreStore } from '@/stores/core';
|
|
138
138
|
import { useUserStore } from '@/stores/user';
|
|
139
139
|
import { callAdminForthApi, loadFile, applyRegexValidation } from '@/utils';
|
|
140
|
-
import { useRoute, useRouter } from 'vue-router';
|
|
140
|
+
import { onBeforeRouteUpdate, useRoute, useRouter } from 'vue-router';
|
|
141
141
|
import { IconEyeSolid, IconEyeSlashSolid } from '@iconify-prerendered/vue-flowbite';
|
|
142
142
|
import Button from '@/afcl/Button.vue';
|
|
143
143
|
import Link from '@/afcl/Link.vue';
|
|
144
144
|
import { useI18n } from 'vue-i18n';
|
|
145
|
+
import adminforth from '@/adminforth';
|
|
145
146
|
|
|
146
147
|
const { t } = useI18n();
|
|
147
148
|
|
|
@@ -209,10 +210,6 @@ const validationError = computed(() => {
|
|
|
209
210
|
|
|
210
211
|
const error: Ref<string | null> = ref(null);
|
|
211
212
|
|
|
212
|
-
onMounted(async () => {
|
|
213
|
-
emailInput.value?.focus();
|
|
214
|
-
});
|
|
215
|
-
|
|
216
213
|
|
|
217
214
|
const backgroundPosition = computed(() => {
|
|
218
215
|
return coreStore.config?.loginBackgroundPosition || '1/2';
|
|
@@ -245,6 +242,7 @@ const passwordConstraints: Ref<{
|
|
|
245
242
|
// }
|
|
246
243
|
|
|
247
244
|
onBeforeMount(() => {
|
|
245
|
+
// console.log('⛔ SignUp Page. Before Mount: ', route)
|
|
248
246
|
if (localStorage.getItem('isAuthorized') === 'true') {
|
|
249
247
|
// if route has next param, redirect
|
|
250
248
|
coreStore.fetchMenuAndResource();
|
|
@@ -257,15 +255,22 @@ onBeforeMount(() => {
|
|
|
257
255
|
})
|
|
258
256
|
|
|
259
257
|
onMounted(async () => {
|
|
258
|
+
emailInput.value?.focus();
|
|
259
|
+
await coreStore.getPublicConfig();
|
|
260
260
|
|
|
261
|
+
await nextTick();
|
|
262
|
+
await router.isReady();
|
|
261
263
|
|
|
262
|
-
await coreStore.getPublicConfig();
|
|
263
264
|
// getPasswordConstraints
|
|
265
|
+
console.log('⛔ SignUp Page. Before Mount: ', route.meta)
|
|
264
266
|
passwordConstraints.value = await callAdminForthApi({
|
|
265
267
|
path: `/plugin/${route.meta.pluginInstanceId}/password-constraints`,
|
|
266
268
|
method: 'GET',
|
|
267
269
|
});
|
|
268
|
-
})
|
|
270
|
+
})
|
|
271
|
+
|
|
272
|
+
// onMounted(async () => {
|
|
273
|
+
// });
|
|
269
274
|
|
|
270
275
|
async function doSignup() {
|
|
271
276
|
error.value = null;
|
|
@@ -319,7 +324,7 @@ const signupAfterEmailConfirmation = async () => {
|
|
|
319
324
|
}
|
|
320
325
|
});
|
|
321
326
|
if (resp.error) {
|
|
322
|
-
|
|
327
|
+
adminforth.alert({
|
|
323
328
|
message: t(`Error fetching data: {error}`, { error: resp.error }),
|
|
324
329
|
variant: 'danger',
|
|
325
330
|
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"baseUrl": ".", // This should point to your project root
|
|
4
|
+
"paths": {
|
|
5
|
+
"@/*": [
|
|
6
|
+
// "node_modules/adminforth/dist/spa/src/*"
|
|
7
|
+
"../../../spa/src/*"
|
|
8
|
+
],
|
|
9
|
+
"*": [
|
|
10
|
+
// "node_modules/adminforth/dist/spa/node_modules/*"
|
|
11
|
+
"../../../spa/node_modules/*"
|
|
12
|
+
],
|
|
13
|
+
"@@/*": [
|
|
14
|
+
// "node_modules/adminforth/dist/spa/src/*"
|
|
15
|
+
"."
|
|
16
|
+
]
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -133,15 +133,16 @@
|
|
|
133
133
|
|
|
134
134
|
|
|
135
135
|
<script setup lang="ts">
|
|
136
|
-
import { onMounted, ref, computed, Ref, onBeforeMount } from 'vue';
|
|
136
|
+
import { onMounted, ref, computed, Ref, onBeforeMount, nextTick } from 'vue';
|
|
137
137
|
import { useCoreStore } from '@/stores/core';
|
|
138
138
|
import { useUserStore } from '@/stores/user';
|
|
139
139
|
import { callAdminForthApi, loadFile, applyRegexValidation } from '@/utils';
|
|
140
|
-
import { useRoute, useRouter } from 'vue-router';
|
|
140
|
+
import { onBeforeRouteUpdate, useRoute, useRouter } from 'vue-router';
|
|
141
141
|
import { IconEyeSolid, IconEyeSlashSolid } from '@iconify-prerendered/vue-flowbite';
|
|
142
142
|
import Button from '@/afcl/Button.vue';
|
|
143
143
|
import Link from '@/afcl/Link.vue';
|
|
144
144
|
import { useI18n } from 'vue-i18n';
|
|
145
|
+
import adminforth from '@/adminforth';
|
|
145
146
|
|
|
146
147
|
const { t } = useI18n();
|
|
147
148
|
|
|
@@ -209,10 +210,6 @@ const validationError = computed(() => {
|
|
|
209
210
|
|
|
210
211
|
const error: Ref<string | null> = ref(null);
|
|
211
212
|
|
|
212
|
-
onMounted(async () => {
|
|
213
|
-
emailInput.value?.focus();
|
|
214
|
-
});
|
|
215
|
-
|
|
216
213
|
|
|
217
214
|
const backgroundPosition = computed(() => {
|
|
218
215
|
return coreStore.config?.loginBackgroundPosition || '1/2';
|
|
@@ -245,6 +242,7 @@ const passwordConstraints: Ref<{
|
|
|
245
242
|
// }
|
|
246
243
|
|
|
247
244
|
onBeforeMount(() => {
|
|
245
|
+
// console.log('⛔ SignUp Page. Before Mount: ', route)
|
|
248
246
|
if (localStorage.getItem('isAuthorized') === 'true') {
|
|
249
247
|
// if route has next param, redirect
|
|
250
248
|
coreStore.fetchMenuAndResource();
|
|
@@ -257,15 +255,22 @@ onBeforeMount(() => {
|
|
|
257
255
|
})
|
|
258
256
|
|
|
259
257
|
onMounted(async () => {
|
|
258
|
+
emailInput.value?.focus();
|
|
259
|
+
await coreStore.getPublicConfig();
|
|
260
260
|
|
|
261
|
+
await nextTick();
|
|
262
|
+
await router.isReady();
|
|
261
263
|
|
|
262
|
-
await coreStore.getPublicConfig();
|
|
263
264
|
// getPasswordConstraints
|
|
265
|
+
console.log('⛔ SignUp Page. Before Mount: ', route.meta)
|
|
264
266
|
passwordConstraints.value = await callAdminForthApi({
|
|
265
267
|
path: `/plugin/${route.meta.pluginInstanceId}/password-constraints`,
|
|
266
268
|
method: 'GET',
|
|
267
269
|
});
|
|
268
|
-
})
|
|
270
|
+
})
|
|
271
|
+
|
|
272
|
+
// onMounted(async () => {
|
|
273
|
+
// });
|
|
269
274
|
|
|
270
275
|
async function doSignup() {
|
|
271
276
|
error.value = null;
|
|
@@ -319,7 +324,7 @@ const signupAfterEmailConfirmation = async () => {
|
|
|
319
324
|
}
|
|
320
325
|
});
|
|
321
326
|
if (resp.error) {
|
|
322
|
-
|
|
327
|
+
adminforth.alert({
|
|
323
328
|
message: t(`Error fetching data: {error}`, { error: resp.error }),
|
|
324
329
|
variant: 'danger',
|
|
325
330
|
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"baseUrl": ".", // This should point to your project root
|
|
4
|
+
"paths": {
|
|
5
|
+
"@/*": [
|
|
6
|
+
// "node_modules/adminforth/dist/spa/src/*"
|
|
7
|
+
"../../../spa/src/*"
|
|
8
|
+
],
|
|
9
|
+
"*": [
|
|
10
|
+
// "node_modules/adminforth/dist/spa/node_modules/*"
|
|
11
|
+
"../../../spa/node_modules/*"
|
|
12
|
+
],
|
|
13
|
+
"@@/*": [
|
|
14
|
+
// "node_modules/adminforth/dist/spa/src/*"
|
|
15
|
+
"."
|
|
16
|
+
]
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -131,7 +131,7 @@ export default class OpenSignupPlugin extends AdminForthPlugin {
|
|
|
131
131
|
return {
|
|
132
132
|
minLength: this.passwordField.minLength,
|
|
133
133
|
maxLength: this.passwordField.maxLength,
|
|
134
|
-
validation: yield Promise.all(this.passwordField.validation.map((
|
|
134
|
+
validation: yield Promise.all(this.passwordField.validation.map((_a) => __awaiter(this, [_a], void 0, function* ({ regExp, message }) { return ({ regExp, message: yield tr(message, 'opensignup') }); }))),
|
|
135
135
|
};
|
|
136
136
|
})
|
|
137
137
|
});
|
|
@@ -139,7 +139,7 @@ export default class OpenSignupPlugin extends AdminForthPlugin {
|
|
|
139
139
|
method: 'POST',
|
|
140
140
|
path: `/plugin/${this.pluginInstanceId}/complete-verified-signup`,
|
|
141
141
|
noAuth: true,
|
|
142
|
-
handler: (
|
|
142
|
+
handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, response, headers, query, cookies, tr, requestUrl }) {
|
|
143
143
|
const { token, password } = body;
|
|
144
144
|
const { email } = yield this.adminforth.auth.verify(token, 'tempVerifyEmailToken', false);
|
|
145
145
|
if (!email) {
|
|
@@ -159,20 +159,22 @@ export default class OpenSignupPlugin extends AdminForthPlugin {
|
|
|
159
159
|
[this.options.confirmEmails.emailConfirmedField]: true,
|
|
160
160
|
[this.options.passwordHashField]: yield AdminForth.Utils.generatePasswordHash(password),
|
|
161
161
|
});
|
|
162
|
-
return yield this.doLogin(email, response, { body, headers, query, cookies });
|
|
162
|
+
return yield this.doLogin(email, response, { body, headers, query, cookies, requestUrl });
|
|
163
163
|
})
|
|
164
164
|
});
|
|
165
165
|
server.endpoint({
|
|
166
166
|
method: 'POST',
|
|
167
167
|
path: `/plugin/${this.pluginInstanceId}/signup`,
|
|
168
168
|
noAuth: true,
|
|
169
|
-
handler: (
|
|
169
|
+
handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, response, headers, query, cookies, tr, requestUrl }) {
|
|
170
|
+
var _b, _c;
|
|
170
171
|
const { email, url, password } = body;
|
|
172
|
+
const extra = { body, headers, query, cookies, requestUrl: url };
|
|
171
173
|
// validate email
|
|
172
174
|
if (this.emailField.validation) {
|
|
173
175
|
for (const { regExp, message } of this.emailField.validation) {
|
|
174
176
|
if (!new RegExp(regExp).test(email)) {
|
|
175
|
-
return { error: message, ok: false };
|
|
177
|
+
return { error: yield tr(message, 'opensignup'), ok: false };
|
|
176
178
|
}
|
|
177
179
|
}
|
|
178
180
|
}
|
|
@@ -180,20 +182,20 @@ export default class OpenSignupPlugin extends AdminForthPlugin {
|
|
|
180
182
|
if (!this.options.confirmEmails) {
|
|
181
183
|
if (password.length < this.passwordField.minLength) {
|
|
182
184
|
return {
|
|
183
|
-
error: tr(`Password must be at least ${this.passwordField.minLength} characters long`, 'opensignup'),
|
|
185
|
+
error: yield tr(`Password must be at least ${this.passwordField.minLength} characters long`, 'opensignup'),
|
|
184
186
|
ok: false
|
|
185
187
|
};
|
|
186
188
|
}
|
|
187
189
|
if (password.length > this.passwordField.maxLength) {
|
|
188
190
|
return {
|
|
189
|
-
error: tr(`Password must be at most ${this.passwordField.maxLength} characters long`, 'opensignup'),
|
|
191
|
+
error: yield tr(`Password must be at most ${this.passwordField.maxLength} characters long`, 'opensignup'),
|
|
190
192
|
ok: false
|
|
191
193
|
};
|
|
192
194
|
}
|
|
193
195
|
if (this.passwordField.validation) {
|
|
194
196
|
for (const { regExp, message } of this.passwordField.validation) {
|
|
195
197
|
if (!new RegExp(regExp).test(password)) {
|
|
196
|
-
return { error: tr(message, 'opensignup'), ok: false };
|
|
198
|
+
return { error: yield tr(message, 'opensignup'), ok: false };
|
|
197
199
|
}
|
|
198
200
|
}
|
|
199
201
|
}
|
|
@@ -204,14 +206,46 @@ export default class OpenSignupPlugin extends AdminForthPlugin {
|
|
|
204
206
|
// first check again if email already exists
|
|
205
207
|
const existingUser = yield this.adminforth.resource(this.authResource.resourceId).get(Filters.EQ(this.emailField.name, normalizedEmail));
|
|
206
208
|
if ((!this.options.confirmEmails && existingUser) || (this.options.confirmEmails && (existingUser === null || existingUser === void 0 ? void 0 : existingUser[this.emailConfirmedField.name]))) {
|
|
207
|
-
return { error: tr(
|
|
209
|
+
return { error: yield tr(`Email already exists`, 'opensignup'), ok: false };
|
|
208
210
|
}
|
|
209
211
|
// create user
|
|
210
212
|
if (!existingUser) {
|
|
211
|
-
|
|
213
|
+
let recordToCreate = Object.assign(Object.assign(Object.assign({}, (this.options.defaultFieldValues || {})), (this.options.confirmEmails ? { [this.options.confirmEmails.emailConfirmedField]: false } : {})), { [this.emailField.name]: normalizedEmail, [this.options.passwordHashField]: password ? yield AdminForth.Utils.generatePasswordHash(password) : '' });
|
|
214
|
+
if ((_b = this.options.hooks) === null || _b === void 0 ? void 0 : _b.beforeUserSave) {
|
|
215
|
+
const hook = this.options.hooks.beforeUserSave;
|
|
216
|
+
const resp = yield hook({
|
|
217
|
+
resource: this.authResource,
|
|
218
|
+
record: recordToCreate,
|
|
219
|
+
adminforth: this.adminforth,
|
|
220
|
+
extra,
|
|
221
|
+
});
|
|
222
|
+
if (!resp || (!resp.ok && !resp.error)) {
|
|
223
|
+
throw new Error(`Hook beforeUserSave must return object with {ok: true} or { error: 'Error' } `);
|
|
224
|
+
}
|
|
225
|
+
if (resp.error) {
|
|
226
|
+
return { error: resp.error };
|
|
227
|
+
}
|
|
228
|
+
recordToCreate = resp.record || recordToCreate;
|
|
229
|
+
}
|
|
230
|
+
const created = yield this.adminforth.resource(this.authResource.resourceId).create(recordToCreate);
|
|
231
|
+
if ((_c = this.options.hooks) === null || _c === void 0 ? void 0 : _c.afterUserSave) {
|
|
232
|
+
const hook = this.options.hooks.afterUserSave;
|
|
233
|
+
const resp = yield hook({
|
|
234
|
+
resource: this.authResource,
|
|
235
|
+
record: created,
|
|
236
|
+
adminforth: this.adminforth,
|
|
237
|
+
extra,
|
|
238
|
+
});
|
|
239
|
+
if (!resp || (!resp.ok && !resp.error)) {
|
|
240
|
+
throw new Error(`Hook afterUserSave must return object with {ok: true} or { error: 'Error' } `);
|
|
241
|
+
}
|
|
242
|
+
if (resp.error) {
|
|
243
|
+
return { error: resp.error };
|
|
244
|
+
}
|
|
245
|
+
}
|
|
212
246
|
}
|
|
213
247
|
if (!this.options.confirmEmails) {
|
|
214
|
-
const resp = yield this.doLogin(email, response, { body, headers, query, cookies });
|
|
248
|
+
const resp = yield this.doLogin(email, response, { body, headers, query, cookies, requestUrl });
|
|
215
249
|
return resp;
|
|
216
250
|
}
|
|
217
251
|
// send confirmation email
|
package/index.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import AdminForth, { AdminForthPlugin, Filters, suggestIfTypo, AdminForthDataTypes } from "adminforth";
|
|
2
2
|
import type { IAdminForth, IHttpServer, AdminForthComponentDeclaration, AdminForthResourceColumn, AdminForthResource, BeforeLoginConfirmationFunction, HttpExtra } from "adminforth";
|
|
3
3
|
import type { PluginOptions } from './types.js';
|
|
4
|
-
import { error } from "console";
|
|
5
4
|
|
|
6
5
|
|
|
7
6
|
export default class OpenSignupPlugin extends AdminForthPlugin {
|
|
@@ -162,7 +161,7 @@ export default class OpenSignupPlugin extends AdminForthPlugin {
|
|
|
162
161
|
method: 'POST',
|
|
163
162
|
path: `/plugin/${this.pluginInstanceId}/complete-verified-signup`,
|
|
164
163
|
noAuth: true,
|
|
165
|
-
handler: async ({ body, response, headers, query, cookies, tr }) => {
|
|
164
|
+
handler: async ({ body, response, headers, query, cookies, tr, requestUrl }) => {
|
|
166
165
|
const { token, password } = body;
|
|
167
166
|
const { email } = await this.adminforth.auth.verify(token, 'tempVerifyEmailToken', false);
|
|
168
167
|
if (!email) {
|
|
@@ -185,7 +184,7 @@ export default class OpenSignupPlugin extends AdminForthPlugin {
|
|
|
185
184
|
[this.options.confirmEmails.emailConfirmedField]: true,
|
|
186
185
|
[this.options.passwordHashField]: await AdminForth.Utils.generatePasswordHash(password),
|
|
187
186
|
});
|
|
188
|
-
return await this.doLogin(email, response, { body, headers, query, cookies });
|
|
187
|
+
return await this.doLogin(email, response, { body, headers, query, cookies, requestUrl });
|
|
189
188
|
}
|
|
190
189
|
});
|
|
191
190
|
|
|
@@ -193,36 +192,35 @@ export default class OpenSignupPlugin extends AdminForthPlugin {
|
|
|
193
192
|
method: 'POST',
|
|
194
193
|
path: `/plugin/${this.pluginInstanceId}/signup`,
|
|
195
194
|
noAuth: true,
|
|
196
|
-
handler: async ({ body, response, headers, query, cookies, tr }) => {
|
|
195
|
+
handler: async ({ body, response, headers, query, cookies, tr, requestUrl }) => {
|
|
197
196
|
const { email, url, password } = body;
|
|
198
|
-
|
|
197
|
+
const extra = { body, headers, query, cookies, requestUrl: url };
|
|
199
198
|
// validate email
|
|
200
199
|
if (this.emailField.validation) {
|
|
201
200
|
for (const { regExp, message } of this.emailField.validation) {
|
|
202
201
|
if (!new RegExp(regExp).test(email)) {
|
|
203
|
-
return { error: message, ok: false };
|
|
202
|
+
return { error: await tr(message, 'opensignup'), ok: false };
|
|
204
203
|
}
|
|
205
204
|
}
|
|
206
205
|
}
|
|
207
|
-
|
|
208
206
|
// validate password
|
|
209
207
|
if (!this.options.confirmEmails) {
|
|
210
208
|
if (password.length < this.passwordField.minLength) {
|
|
211
209
|
return {
|
|
212
|
-
error: tr(`Password must be at least ${this.passwordField.minLength} characters long`, 'opensignup'),
|
|
210
|
+
error: await tr(`Password must be at least ${this.passwordField.minLength} characters long`, 'opensignup'),
|
|
213
211
|
ok: false
|
|
214
212
|
};
|
|
215
213
|
}
|
|
216
214
|
if (password.length > this.passwordField.maxLength) {
|
|
217
215
|
return {
|
|
218
|
-
error: tr(`Password must be at most ${this.passwordField.maxLength} characters long`, 'opensignup'),
|
|
216
|
+
error: await tr(`Password must be at most ${this.passwordField.maxLength} characters long`, 'opensignup'),
|
|
219
217
|
ok: false
|
|
220
218
|
};
|
|
221
219
|
}
|
|
222
220
|
if (this.passwordField.validation) {
|
|
223
221
|
for (const { regExp, message } of this.passwordField.validation) {
|
|
224
222
|
if (!new RegExp(regExp).test(password)) {
|
|
225
|
-
return { error: tr(message, 'opensignup'), ok: false };
|
|
223
|
+
return { error: await tr(message, 'opensignup'), ok: false };
|
|
226
224
|
}
|
|
227
225
|
}
|
|
228
226
|
}
|
|
@@ -235,21 +233,58 @@ export default class OpenSignupPlugin extends AdminForthPlugin {
|
|
|
235
233
|
// first check again if email already exists
|
|
236
234
|
const existingUser = await this.adminforth.resource(this.authResource.resourceId).get(Filters.EQ(this.emailField.name, normalizedEmail));
|
|
237
235
|
if ((!this.options.confirmEmails && existingUser) || (this.options.confirmEmails && existingUser?.[this.emailConfirmedField.name])) {
|
|
238
|
-
return { error: tr(
|
|
236
|
+
return { error: await tr(`Email already exists`, 'opensignup'), ok: false };
|
|
239
237
|
}
|
|
240
238
|
|
|
241
239
|
// create user
|
|
242
240
|
if (!existingUser) {
|
|
243
|
-
|
|
241
|
+
let recordToCreate = {
|
|
244
242
|
...(this.options.defaultFieldValues || {}),
|
|
245
243
|
...(this.options.confirmEmails ? { [this.options.confirmEmails.emailConfirmedField]: false } : {}),
|
|
246
244
|
[this.emailField.name]: normalizedEmail,
|
|
247
245
|
[this.options.passwordHashField]: password ? await AdminForth.Utils.generatePasswordHash(password) : '',
|
|
248
|
-
}
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
if (this.options.hooks?.beforeUserSave) {
|
|
249
|
+
const hook = this.options.hooks.beforeUserSave;
|
|
250
|
+
const resp = await hook({
|
|
251
|
+
resource: this.authResource,
|
|
252
|
+
record: recordToCreate,
|
|
253
|
+
adminforth: this.adminforth,
|
|
254
|
+
extra,
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
if (!resp || (!resp.ok && !resp.error)) {
|
|
258
|
+
throw new Error(`Hook beforeUserSave must return object with {ok: true} or { error: 'Error' } `);
|
|
259
|
+
}
|
|
260
|
+
if (resp.error) {
|
|
261
|
+
return { error: resp.error };
|
|
262
|
+
}
|
|
263
|
+
recordToCreate = resp.record || recordToCreate;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const created = await this.adminforth.resource(this.authResource.resourceId).create(recordToCreate);
|
|
267
|
+
|
|
268
|
+
if (this.options.hooks?.afterUserSave) {
|
|
269
|
+
const hook = this.options.hooks.afterUserSave;
|
|
270
|
+
const resp = await hook({
|
|
271
|
+
resource: this.authResource,
|
|
272
|
+
record: created,
|
|
273
|
+
adminforth: this.adminforth,
|
|
274
|
+
extra,
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
if (!resp || (!resp.ok && !resp.error)) {
|
|
278
|
+
throw new Error(`Hook afterUserSave must return object with {ok: true} or { error: 'Error' } `);
|
|
279
|
+
}
|
|
280
|
+
if (resp.error) {
|
|
281
|
+
return { error: resp.error };
|
|
282
|
+
}
|
|
283
|
+
}
|
|
249
284
|
}
|
|
250
285
|
|
|
251
286
|
if (!this.options.confirmEmails) {
|
|
252
|
-
const resp = await this.doLogin(email, response, { body, headers, query, cookies });
|
|
287
|
+
const resp = await this.doLogin(email, response, { body, headers, query, cookies, requestUrl });
|
|
253
288
|
return resp;
|
|
254
289
|
}
|
|
255
290
|
|
package/package.json
CHANGED
|
@@ -1,19 +1,55 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adminforth/open-signup",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"scripts": {
|
|
8
|
-
"build": "tsc && rsync -av --exclude 'node_modules' custom dist/
|
|
9
|
-
"rollout": "npm run build && npm publish --access public",
|
|
8
|
+
"build": "tsc && rsync -av --exclude 'node_modules' custom dist/",
|
|
10
9
|
"prepare": "npm link adminforth"
|
|
11
10
|
},
|
|
12
|
-
"
|
|
13
|
-
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "https://github.com/devforth/adminforth-open-signup.git"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"adminforth",
|
|
17
|
+
"signup"
|
|
18
|
+
],
|
|
19
|
+
"author": "devforth",
|
|
14
20
|
"license": "ISC",
|
|
15
|
-
"description": "",
|
|
21
|
+
"description": "Open signup for adminforth",
|
|
16
22
|
"dependencies": {
|
|
17
23
|
"@aws-sdk/client-ses": "^3.654.0"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@types/node": "^22.10.7",
|
|
27
|
+
"semantic-release": "^24.2.1",
|
|
28
|
+
"semantic-release-slack-bot": "^4.0.2",
|
|
29
|
+
"typescript": "^5.7.3"
|
|
30
|
+
},
|
|
31
|
+
"release": {
|
|
32
|
+
"plugins": [
|
|
33
|
+
"@semantic-release/commit-analyzer",
|
|
34
|
+
"@semantic-release/release-notes-generator",
|
|
35
|
+
"@semantic-release/npm",
|
|
36
|
+
"@semantic-release/github",
|
|
37
|
+
[
|
|
38
|
+
"semantic-release-slack-bot",
|
|
39
|
+
{
|
|
40
|
+
"notifyOnSuccess": true,
|
|
41
|
+
"notifyOnFail": true,
|
|
42
|
+
"slackIcon": ":package:",
|
|
43
|
+
"markdownReleaseNotes": true
|
|
44
|
+
}
|
|
45
|
+
]
|
|
46
|
+
],
|
|
47
|
+
"branches": [
|
|
48
|
+
"main",
|
|
49
|
+
{
|
|
50
|
+
"name": "next",
|
|
51
|
+
"prerelease": true
|
|
52
|
+
}
|
|
53
|
+
]
|
|
18
54
|
}
|
|
19
55
|
}
|
package/types.ts
CHANGED
package/Changelog.md
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
## [v1.0.9]
|
|
2
|
-
|
|
3
|
-
# Fixed
|
|
4
|
-
- when user created normalize email to lowercase. This is not needed when right email validator is set on email field because
|
|
5
|
-
it will not allow to create such email, but if user forgot to set it it might save situation
|
|
6
|
-
|
|
7
|
-
## [v1.0.8]
|
|
8
|
-
|
|
9
|
-
# Fixed
|
|
10
|
-
- ensure next params keeps be encoded as URI between moving to/from login page
|
|
11
|
-
|
|
12
|
-
## [v1.0.7]
|
|
13
|
-
|
|
14
|
-
# Added
|
|
15
|
-
- next param support
|
|
@@ -1,295 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div class="relative flex items-center justify-center min-h-screen bg-gray-100 dark:bg-gray-800 relative w-screen h-screen"
|
|
3
|
-
:style="coreStore.config?.loginBackgroundImage && backgroundPosition === 'over' ? {
|
|
4
|
-
'background-image': 'url(' + loadFile(coreStore.config?.loginBackgroundImage) + ')',
|
|
5
|
-
'background-size': 'cover',
|
|
6
|
-
'background-position': 'center',
|
|
7
|
-
'background-blend-mode': 'darken'
|
|
8
|
-
}: {}"
|
|
9
|
-
>
|
|
10
|
-
|
|
11
|
-
<img v-if="coreStore.config?.loginBackgroundImage && backgroundPosition !== 'over'"
|
|
12
|
-
:src="loadFile(coreStore.config?.loginBackgroundImage)"
|
|
13
|
-
class="position-absolute top-0 left-0 h-screen object-cover w-0"
|
|
14
|
-
:class="{
|
|
15
|
-
'1/2': 'md:w-1/2',
|
|
16
|
-
'1/3': 'md:w-1/3',
|
|
17
|
-
'2/3': 'md:w-2/3',
|
|
18
|
-
'3/4': 'md:w-3/4',
|
|
19
|
-
'2/5': 'md:w-2/5',
|
|
20
|
-
'3/5': 'md:w-3/5',
|
|
21
|
-
}[backgroundPosition]"
|
|
22
|
-
/>
|
|
23
|
-
|
|
24
|
-
<!-- Main modal -->
|
|
25
|
-
<div id="authentication-modal" tabindex="-1"
|
|
26
|
-
class="overflow-y-auto flex flex-grow
|
|
27
|
-
overflow-x-hidden z-50 min-w-[350px] justify-center items-center md:inset-0 h-[calc(100%-1rem)] max-h-full">
|
|
28
|
-
<div class="relative p-4 w-full max-h-full max-w-[400px]">
|
|
29
|
-
<!-- Modal content -->
|
|
30
|
-
<div class="relative bg-white rounded-lg shadow dark:bg-gray-700 dark:shadow-black" >
|
|
31
|
-
<!-- Modal header -->
|
|
32
|
-
<div class="flex items-center justify-between p-4 md:p-5 border-b rounded-t dark:border-gray-600">
|
|
33
|
-
<h3 class="text-xl font-semibold text-gray-900 dark:text-white">
|
|
34
|
-
Reset your password on {{ coreStore.config?.brandName }}
|
|
35
|
-
</h3>
|
|
36
|
-
</div>
|
|
37
|
-
<!-- Modal body -->
|
|
38
|
-
<div class="p-4 md:p-5">
|
|
39
|
-
<form v-if="enteringNew" class="space-y-4" role="alert" @submit.prevent>
|
|
40
|
-
<div class="relative">
|
|
41
|
-
<label for="password" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">New password</label>
|
|
42
|
-
<input
|
|
43
|
-
:type="unmasked ? 'text' : 'password'"
|
|
44
|
-
name="password" id="password"
|
|
45
|
-
v-model="password"
|
|
46
|
-
@keydown.enter="passwordConfirmationInput.focus()"
|
|
47
|
-
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white"
|
|
48
|
-
placeholder="New password" required
|
|
49
|
-
/>
|
|
50
|
-
|
|
51
|
-
<button
|
|
52
|
-
type="button"
|
|
53
|
-
@click="unmasked = !unmasked"
|
|
54
|
-
class="h-6 absolute inset-y-2 top-9 right-1 flex items-center pr-2 z-index-100 focus:outline-none"
|
|
55
|
-
>
|
|
56
|
-
<IconEyeSolid class="w-6 h-6 text-gray-400" v-if="!unmasked" />
|
|
57
|
-
<IconEyeSlashSolid class="w-6 h-6 text-gray-400" v-else />
|
|
58
|
-
</button>
|
|
59
|
-
</div>
|
|
60
|
-
|
|
61
|
-
<div class="relative">
|
|
62
|
-
<label for="password_confirmation" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Confirm new password</label>
|
|
63
|
-
<input
|
|
64
|
-
:type="unmasked ? 'text' : 'password'"
|
|
65
|
-
name="password_confirmation" id="password_confirmation"
|
|
66
|
-
v-model="passwordConfirmation"
|
|
67
|
-
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white"
|
|
68
|
-
placeholder="Confirm new password" required
|
|
69
|
-
/>
|
|
70
|
-
|
|
71
|
-
<button
|
|
72
|
-
type="button"
|
|
73
|
-
@click="unmasked = !unmasked"
|
|
74
|
-
class="h-6 absolute inset-y-2 top-9 right-1 flex items-center pr-2 z-index-100 focus:outline-none"
|
|
75
|
-
>
|
|
76
|
-
<IconEyeSolid class="w-6 h-6 text-gray-400" v-if="!unmasked" />
|
|
77
|
-
<IconEyeSlashSolid class="w-6 h-6 text-gray-400" v-else />
|
|
78
|
-
</button>
|
|
79
|
-
</div>
|
|
80
|
-
|
|
81
|
-
<div v-if="validationError || error" class="flex items-center p-4 mb-4 text-sm text-red-800 rounded-lg bg-red-50 dark:bg-gray-800 dark:text-red-400" role="alert">
|
|
82
|
-
<svg class="flex-shrink-0 inline w-4 h-4 me-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20">
|
|
83
|
-
<path d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5ZM9.5 4a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3ZM12 15H8a1 1 0 0 1 0-2h1v-3H8a1 1 0 0 1 0-2h2a1 1 0 0 1 1 1v4h1a1 1 0 0 1 0 2Z"/>
|
|
84
|
-
</svg>
|
|
85
|
-
<span class="sr-only">Info</span>
|
|
86
|
-
<div>
|
|
87
|
-
{{ validationError || error }}
|
|
88
|
-
</div>
|
|
89
|
-
</div>
|
|
90
|
-
|
|
91
|
-
<button
|
|
92
|
-
:disabled="inProgress || (validationRunning && validationError)"
|
|
93
|
-
@click="setNewPassword"
|
|
94
|
-
type="submit" class="flex items-center justify-center gap-1 w-full text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800 disabled:opacity-50">
|
|
95
|
-
<svg v-if="inProgress"
|
|
96
|
-
aria-hidden="true" class="w-4 h-4 text-gray-200 animate-spin dark:text-gray-600 fill-blue-600" viewBox="0 0 100 101" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z" fill="currentColor"/><path d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874
|
|
97
|
-
// 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z" fill="currentFill"/></svg>
|
|
98
|
-
Set new password
|
|
99
|
-
</button>
|
|
100
|
-
</form>
|
|
101
|
-
<!-- END of set new paasord -->
|
|
102
|
-
<div v-if="!enteringNew && requestSent" class="flex items center justify-center p-4 mb-4 text-sm text-green-800 rounded-lg bg-green-50 dark:bg-green-800 dark:text-green-400" role="alert">
|
|
103
|
-
If user with specified email exists in our db, then request was sent. Please check your email at {{ sentToEmail }} to reset your password.
|
|
104
|
-
</div>
|
|
105
|
-
|
|
106
|
-
<form v-if="!enteringNew && !requestSent" class="space-y-4" @submit.prevent>
|
|
107
|
-
<div>
|
|
108
|
-
<label for="email" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Your email</label>
|
|
109
|
-
<input type="email" name="email" id="email"
|
|
110
|
-
@keydown.enter="reset()"
|
|
111
|
-
ref="emailInput"
|
|
112
|
-
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white"
|
|
113
|
-
placeholder="name@company.com"
|
|
114
|
-
required
|
|
115
|
-
/>
|
|
116
|
-
</div>
|
|
117
|
-
|
|
118
|
-
<div v-if="error" class="flex items-center p-4 mb-4 text-sm text-red-800 rounded-lg bg-red-50 dark:bg-gray-800 dark:text-red-400" role="alert">
|
|
119
|
-
<svg class="flex-shrink-0 inline w-4 h-4 me-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20">
|
|
120
|
-
<path d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5ZM9.5 4a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3ZM12 15H8a1 1 0 0 1 0-2h1v-3H8a1 1 0 0 1 0-2h2a1 1 0 0 1 1 1v4h1a1 1 0 0 1 0 2Z"/>
|
|
121
|
-
</svg>
|
|
122
|
-
<span class="sr-only">Info</span>
|
|
123
|
-
<div>
|
|
124
|
-
{{ error }}
|
|
125
|
-
</div>
|
|
126
|
-
</div>
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
<button
|
|
130
|
-
@click="reset"
|
|
131
|
-
type="submit" class="flex items-center justify-center gap-1 w-full text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800">
|
|
132
|
-
<svg v-if="inProgress"
|
|
133
|
-
aria-hidden="true" class="w-4 h-4 text-gray-200 animate-spin dark:text-gray-600 fill-blue-600" viewBox="0 0 100 101" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z" fill="currentColor"/><path d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z" fill="currentFill"/></svg>
|
|
134
|
-
|
|
135
|
-
Reset Password
|
|
136
|
-
|
|
137
|
-
</button>
|
|
138
|
-
|
|
139
|
-
<p class="text-gray-500 dark:text-gray-400 font-sm text-right mt-3">
|
|
140
|
-
or <router-link class=" text-blue-600 underline dark:text-blue-500 hover:no-underline
|
|
141
|
-
cursor-pointer" to="/login">
|
|
142
|
-
Back to login
|
|
143
|
-
</router-link>
|
|
144
|
-
</p>
|
|
145
|
-
|
|
146
|
-
</form>
|
|
147
|
-
|
|
148
|
-
</div>
|
|
149
|
-
</div>
|
|
150
|
-
</div>
|
|
151
|
-
</div>
|
|
152
|
-
|
|
153
|
-
</div>
|
|
154
|
-
</template>
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
<script setup lang="ts">
|
|
158
|
-
|
|
159
|
-
import { onMounted, ref, computed, Ref } from 'vue';
|
|
160
|
-
import { useCoreStore } from '@/stores/core';
|
|
161
|
-
import { callAdminForthApi, loadFile, applyRegexValidation } from '@/utils';
|
|
162
|
-
import { useRoute, useRouter } from 'vue-router';
|
|
163
|
-
import { IconEyeSolid, IconEyeSlashSolid } from '@iconify-prerendered/vue-flowbite';
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
const inProgress = ref(false);
|
|
167
|
-
|
|
168
|
-
const coreStore = useCoreStore();
|
|
169
|
-
const requestSent = ref(false);
|
|
170
|
-
|
|
171
|
-
const emailInput = ref(null);
|
|
172
|
-
const password = ref("");
|
|
173
|
-
const passwordConfirmation = ref("");
|
|
174
|
-
const unmasked = ref(false);
|
|
175
|
-
const sentToEmail: Ref<string> = ref('');
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
const route = useRoute();
|
|
179
|
-
const router = useRouter();
|
|
180
|
-
|
|
181
|
-
function checkPassowrd() {
|
|
182
|
-
|
|
183
|
-
if (!password.value || !passwordConfirmation.value) {
|
|
184
|
-
return 'Please enter both password and password confirmation';
|
|
185
|
-
}
|
|
186
|
-
if (password.value !== passwordConfirmation.value) {
|
|
187
|
-
return 'Passwords do not match';
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
if (password.value.length < passwordField.value.minLength) {
|
|
191
|
-
return `Password must be at least ${passwordField.value.minLength} characters long`;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
if (password.value.length > passwordField.value.maxLength) {
|
|
195
|
-
return `Password must be at most ${passwordField.value.maxLength} characters long`;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
if (passwordField.value.validation) {
|
|
199
|
-
const valError = applyRegexValidation(password.value, passwordField.value.validation);
|
|
200
|
-
if (valError) {
|
|
201
|
-
return valError;
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
return null;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
const validationRunning = ref(false);
|
|
209
|
-
|
|
210
|
-
const validationError = computed(() => {
|
|
211
|
-
console.log('validationRunning.value', validationRunning.value, 'aa', checkPassowrd());
|
|
212
|
-
if (validationRunning.value) {
|
|
213
|
-
return checkPassowrd();
|
|
214
|
-
}
|
|
215
|
-
return null;
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
const error = ref(null);
|
|
219
|
-
|
|
220
|
-
// if token query param is present, we are entering new password
|
|
221
|
-
const enteringNew = computed(() => route.query.token);
|
|
222
|
-
|
|
223
|
-
const backgroundPosition = computed(() => {
|
|
224
|
-
return coreStore.config?.loginBackgroundPosition || '1/2';
|
|
225
|
-
});
|
|
226
|
-
|
|
227
|
-
const passwordField = computed(
|
|
228
|
-
() => route.meta.passwordField
|
|
229
|
-
)
|
|
230
|
-
|
|
231
|
-
onMounted(async () => {
|
|
232
|
-
await coreStore.getPublicConfig();
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
async function reset() {
|
|
236
|
-
error.value = null;
|
|
237
|
-
const email = emailInput.value.value;
|
|
238
|
-
if (!email) {
|
|
239
|
-
error.value = 'Please enter your email';
|
|
240
|
-
return;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
inProgress.value = true;
|
|
244
|
-
const resp = await callAdminForthApi({
|
|
245
|
-
path: `/plugin/${route.meta.pluginInstanceId}/reset-password`,
|
|
246
|
-
method: 'POST',
|
|
247
|
-
body: {
|
|
248
|
-
email,
|
|
249
|
-
url: window.location.origin + window.location.pathname,
|
|
250
|
-
}
|
|
251
|
-
});
|
|
252
|
-
inProgress.value = false;
|
|
253
|
-
if (resp.error) {
|
|
254
|
-
error.value = resp.error;
|
|
255
|
-
} else {
|
|
256
|
-
error.value = null;
|
|
257
|
-
requestSent.value = true;
|
|
258
|
-
sentToEmail.value = email;
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
async function setNewPassword() {
|
|
263
|
-
error.value = null;
|
|
264
|
-
|
|
265
|
-
if (checkPassowrd()) {
|
|
266
|
-
validationRunning.value = true;
|
|
267
|
-
return;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
inProgress.value = true;
|
|
271
|
-
const resp = await callAdminForthApi({
|
|
272
|
-
path: `/plugin/${route.meta.pluginInstanceId}/reset-password-confirm`,
|
|
273
|
-
method: 'POST',
|
|
274
|
-
body: {
|
|
275
|
-
token: route.query.token,
|
|
276
|
-
password: password.value,
|
|
277
|
-
}
|
|
278
|
-
});
|
|
279
|
-
inProgress.value = false;
|
|
280
|
-
if (resp.error) {
|
|
281
|
-
error.value = resp.error;
|
|
282
|
-
} else {
|
|
283
|
-
error.value = null;
|
|
284
|
-
router.push('/login');
|
|
285
|
-
window.adminforth.alert({
|
|
286
|
-
message: 'Password reset successfully. Please login with your new password',
|
|
287
|
-
variant: 'success',
|
|
288
|
-
timeout: 15,
|
|
289
|
-
});
|
|
290
|
-
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
</script>
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<p class="text-gray-500 dark:text-gray-400 font-sm text-left mt-3">
|
|
3
|
-
Forgot password? <router-link class=" text-blue-600 underline dark:text-blue-500 hover:no-underline
|
|
4
|
-
cursor-pointer" to="/reset-password">
|
|
5
|
-
reset it
|
|
6
|
-
</router-link>
|
|
7
|
-
</p>
|
|
8
|
-
</template>
|
|
9
|
-
|
|
10
|
-
<script setup>
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
</script>
|