Compare commits
3 Commits
60af230a2d
...
50c33eb172
Author | SHA1 | Date | |
---|---|---|---|
|
50c33eb172 | ||
|
21b800f431 | ||
|
68273802cc |
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -2,7 +2,6 @@
|
||||||
node_modules
|
node_modules
|
||||||
/dist
|
/dist
|
||||||
|
|
||||||
|
|
||||||
# local env files
|
# local env files
|
||||||
.env.local
|
.env.local
|
||||||
.env.*.local
|
.env.*.local
|
||||||
|
@ -21,3 +20,5 @@ pnpm-debug.log*
|
||||||
*.njsproj
|
*.njsproj
|
||||||
*.sln
|
*.sln
|
||||||
*.sw?
|
*.sw?
|
||||||
|
|
||||||
|
.env
|
||||||
|
|
14
.prettierrc
Executable file
14
.prettierrc
Executable file
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"trailingComma": "none",
|
||||||
|
"tabWidth": 2,
|
||||||
|
"semi": false,
|
||||||
|
"singleQuote": true,
|
||||||
|
"bracketSpacing": true,
|
||||||
|
"arrowParens": "avoid",
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"files": "*.js, *.vue, *.css, *.scss",
|
||||||
|
"excludeFiles": "**/dist/**, **/node_modules/**"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
33
README.md
33
README.md
|
@ -1,44 +1,17 @@
|
||||||
# default
|
# IFMS Password updater - Web
|
||||||
|
|
||||||
## Project setup
|
## Project setup
|
||||||
|
|
||||||
```
|
|
||||||
# yarn
|
|
||||||
yarn
|
|
||||||
|
|
||||||
# npm
|
|
||||||
npm install
|
npm install
|
||||||
|
|
||||||
# pnpm
|
|
||||||
pnpm install
|
|
||||||
```
|
|
||||||
|
|
||||||
### Compiles and hot-reloads for development
|
### Compiles and hot-reloads for development
|
||||||
|
|
||||||
```
|
|
||||||
# yarn
|
|
||||||
yarn dev
|
|
||||||
|
|
||||||
# npm
|
# npm
|
||||||
npm run dev
|
|
||||||
|
|
||||||
# pnpm
|
npm run dev
|
||||||
pnpm dev
|
|
||||||
```
|
|
||||||
|
|
||||||
### Compiles and minifies for production
|
### Compiles and minifies for production
|
||||||
|
|
||||||
```
|
|
||||||
# yarn
|
|
||||||
yarn build
|
|
||||||
|
|
||||||
# npm
|
# npm
|
||||||
|
|
||||||
npm run build
|
npm run build
|
||||||
|
|
||||||
# pnpm
|
|
||||||
pnpm build
|
|
||||||
```
|
|
||||||
|
|
||||||
### Customize configuration
|
|
||||||
|
|
||||||
See [Configuration Reference](https://vitejs.dev/config/).
|
|
||||||
|
|
75
package-lock.json
generated
75
package-lock.json
generated
|
@ -13,7 +13,9 @@
|
||||||
"@trpc/server": "^10.44.1",
|
"@trpc/server": "^10.44.1",
|
||||||
"body-parser": "^1.20.2",
|
"body-parser": "^1.20.2",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
|
"dotenv": "^16.3.1",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
|
"ldapts": "^7.0.7",
|
||||||
"roboto-fontface": "*",
|
"roboto-fontface": "*",
|
||||||
"vue": "^3.2.0",
|
"vue": "^3.2.0",
|
||||||
"vue-router": "^4.0.0",
|
"vue-router": "^4.0.0",
|
||||||
|
@ -589,6 +591,14 @@
|
||||||
"node": ">=18.0.0"
|
"node": ">=18.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/asn1": {
|
||||||
|
"version": "0.2.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/asn1/-/asn1-0.2.4.tgz",
|
||||||
|
"integrity": "sha512-V91DSJ2l0h0gRhVP4oBfBzRBN9lAbPUkGDMCnwedqPKX2d84aAMc9CulOvxdw1f7DfEYx99afab+Rsm3e52jhA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/body-parser": {
|
"node_modules/@types/body-parser": {
|
||||||
"version": "1.19.5",
|
"version": "1.19.5",
|
||||||
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz",
|
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz",
|
||||||
|
@ -663,7 +673,6 @@
|
||||||
"version": "18.19.3",
|
"version": "18.19.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.3.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.3.tgz",
|
||||||
"integrity": "sha512-k5fggr14DwAytoA/t8rPrIz++lXK7/DqckthCmoZOKNsEbJkId4Z//BqgApXBUGrGddrigYa1oqheo/7YmW4rg==",
|
"integrity": "sha512-k5fggr14DwAytoA/t8rPrIz++lXK7/DqckthCmoZOKNsEbJkId4Z//BqgApXBUGrGddrigYa1oqheo/7YmW4rg==",
|
||||||
"devOptional": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"undici-types": "~5.26.4"
|
"undici-types": "~5.26.4"
|
||||||
}
|
}
|
||||||
|
@ -707,6 +716,11 @@
|
||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/uuid": {
|
||||||
|
"version": "9.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.7.tgz",
|
||||||
|
"integrity": "sha512-WUtIVRUZ9i5dYXefDEAI7sh9/O7jGvHg7Df/5O/gtH3Yabe5odI3UWopVR1qbPXQtvOxWu3mM4XxlYeZtMWF4g=="
|
||||||
|
},
|
||||||
"node_modules/@types/webfontloader": {
|
"node_modules/@types/webfontloader": {
|
||||||
"version": "1.6.38",
|
"version": "1.6.38",
|
||||||
"resolved": "https://registry.npmjs.org/@types/webfontloader/-/webfontloader-1.6.38.tgz",
|
"resolved": "https://registry.npmjs.org/@types/webfontloader/-/webfontloader-1.6.38.tgz",
|
||||||
|
@ -1235,6 +1249,14 @@
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/asn1": {
|
||||||
|
"version": "0.2.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
|
||||||
|
"integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"safer-buffer": "~2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/balanced-match": {
|
"node_modules/balanced-match": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||||
|
@ -1544,7 +1566,6 @@
|
||||||
"version": "4.3.4",
|
"version": "4.3.4",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||||
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
|
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
|
||||||
"devOptional": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ms": "2.1.2"
|
"ms": "2.1.2"
|
||||||
},
|
},
|
||||||
|
@ -1617,6 +1638,17 @@
|
||||||
"node": ">=6.0.0"
|
"node": ">=6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/dotenv": {
|
||||||
|
"version": "16.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz",
|
||||||
|
"integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/motdotla/dotenv?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ee-first": {
|
"node_modules/ee-first": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||||
|
@ -2568,6 +2600,22 @@
|
||||||
"json-buffer": "3.0.1"
|
"json-buffer": "3.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/ldapts": {
|
||||||
|
"version": "7.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/ldapts/-/ldapts-7.0.7.tgz",
|
||||||
|
"integrity": "sha512-c/W9jZJ2WiGiAIBnkzUmYkIw1d5jebfVfp+vpR9Z6xh4QXm+kwwaQbmHFJHKsW4OwFARdOaQ03H3fBky4+XcVg==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/asn1": ">=0.2.4",
|
||||||
|
"@types/uuid": ">=9",
|
||||||
|
"asn1": "~0.2.6",
|
||||||
|
"debug": "~4.3.4",
|
||||||
|
"strict-event-emitter-types": "~2.0.0",
|
||||||
|
"uuid": "~9.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/levn": {
|
"node_modules/levn": {
|
||||||
"version": "0.4.1",
|
"version": "0.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
|
||||||
|
@ -2743,8 +2791,7 @@
|
||||||
"node_modules/ms": {
|
"node_modules/ms": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
|
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||||
"devOptional": true
|
|
||||||
},
|
},
|
||||||
"node_modules/muggle-string": {
|
"node_modules/muggle-string": {
|
||||||
"version": "0.3.1",
|
"version": "0.3.1",
|
||||||
|
@ -3471,6 +3518,11 @@
|
||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/strict-event-emitter-types": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/strict-event-emitter-types/-/strict-event-emitter-types-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-Nk/brWYpD85WlOgzw5h173aci0Teyv8YdIAEtV+N88nDB0dLlazZyJMIsN6eo1/AR61l+p6CJTG1JIyFaoNEEA=="
|
||||||
|
},
|
||||||
"node_modules/string-width": {
|
"node_modules/string-width": {
|
||||||
"version": "4.2.3",
|
"version": "4.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||||
|
@ -3657,8 +3709,7 @@
|
||||||
"node_modules/undici-types": {
|
"node_modules/undici-types": {
|
||||||
"version": "5.26.5",
|
"version": "5.26.5",
|
||||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
|
||||||
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
|
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="
|
||||||
"devOptional": true
|
|
||||||
},
|
},
|
||||||
"node_modules/unpipe": {
|
"node_modules/unpipe": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
|
@ -3701,6 +3752,18 @@
|
||||||
"node": ">= 0.4.0"
|
"node": ">= 0.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/uuid": {
|
||||||
|
"version": "9.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
|
||||||
|
"integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
|
||||||
|
"funding": [
|
||||||
|
"https://github.com/sponsors/broofa",
|
||||||
|
"https://github.com/sponsors/ctavan"
|
||||||
|
],
|
||||||
|
"bin": {
|
||||||
|
"uuid": "dist/bin/uuid"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/vary": {
|
"node_modules/vary": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||||
|
|
|
@ -19,7 +19,9 @@
|
||||||
"@trpc/server": "^10.44.1",
|
"@trpc/server": "^10.44.1",
|
||||||
"body-parser": "^1.20.2",
|
"body-parser": "^1.20.2",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
|
"dotenv": "^16.3.1",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
|
"ldapts": "^7.0.7",
|
||||||
"roboto-fontface": "*",
|
"roboto-fontface": "*",
|
||||||
"vue": "^3.2.0",
|
"vue": "^3.2.0",
|
||||||
"vue-router": "^4.0.0",
|
"vue-router": "^4.0.0",
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import "dotenv/config";
|
||||||
|
|
||||||
import { server } from "./server";
|
import { server } from "./server";
|
||||||
|
|
||||||
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
|
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
|
||||||
|
|
11
src/server/lib/encodePassword.ts
Normal file
11
src/server/lib/encodePassword.ts
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
export function encodePassword(password: string): string {
|
||||||
|
let newPassword = ''
|
||||||
|
password = '"' + password + '"'
|
||||||
|
for (let i = 0; i < password.length; i++) {
|
||||||
|
newPassword += String.fromCharCode(
|
||||||
|
password.charCodeAt(i) & 0xff,
|
||||||
|
(password.charCodeAt(i) >>> 8) & 0xff
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return newPassword
|
||||||
|
}
|
89
src/server/lib/updatePassword.ts
Normal file
89
src/server/lib/updatePassword.ts
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
import {
|
||||||
|
Client,
|
||||||
|
Change,
|
||||||
|
Attribute,
|
||||||
|
InvalidCredentialsError,
|
||||||
|
UnwillingToPerformError
|
||||||
|
} from 'ldapts'
|
||||||
|
import { encodePassword } from './encodePassword'
|
||||||
|
|
||||||
|
const ldapClient = new Client({
|
||||||
|
url: process.env.LDAP_URL || 'ldap://10.1.0.16'
|
||||||
|
})
|
||||||
|
|
||||||
|
const bindUser = process.env.AD_BIND_USER || ''
|
||||||
|
const bindPassword = process.env.AD_BIND_PASSWORD || ''
|
||||||
|
const baseDN = process.env.AD_BASE_DN || ''
|
||||||
|
|
||||||
|
async function getUserDN(username: string): Promise<string> {
|
||||||
|
try {
|
||||||
|
await ldapClient.bind(bindUser, bindPassword)
|
||||||
|
|
||||||
|
const { searchEntries } = await ldapClient.search(baseDN, {
|
||||||
|
scope: 'sub',
|
||||||
|
attributes: ['dn'],
|
||||||
|
filter: `(sAMAccountName=${username})`
|
||||||
|
})
|
||||||
|
|
||||||
|
return searchEntries[0]?.dn
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err)
|
||||||
|
} finally {
|
||||||
|
await ldapClient.unbind()
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error('User not found')
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updatePassword({
|
||||||
|
username,
|
||||||
|
password,
|
||||||
|
newPassword
|
||||||
|
}: {
|
||||||
|
username: string
|
||||||
|
password: string
|
||||||
|
newPassword: string
|
||||||
|
}): Promise<'SUCCESS' | 'FAIL'> {
|
||||||
|
try {
|
||||||
|
const userDN = await getUserDN(username)
|
||||||
|
|
||||||
|
await ldapClient.bind(userDN, password)
|
||||||
|
|
||||||
|
console.log('binded')
|
||||||
|
|
||||||
|
await ldapClient.modify(userDN, [
|
||||||
|
new Change({
|
||||||
|
operation: 'delete',
|
||||||
|
modification: new Attribute({
|
||||||
|
type: 'unicodePwd',
|
||||||
|
values: [encodePassword(password)]
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
new Change({
|
||||||
|
operation: 'add',
|
||||||
|
modification: new Attribute({
|
||||||
|
type: 'unicodePwd',
|
||||||
|
values: [encodePassword(newPassword)]
|
||||||
|
})
|
||||||
|
})
|
||||||
|
])
|
||||||
|
|
||||||
|
return 'SUCCESS'
|
||||||
|
} catch (err: any) {
|
||||||
|
console.log(err)
|
||||||
|
|
||||||
|
if (err instanceof InvalidCredentialsError) {
|
||||||
|
throw new Error('Usuário ou senha atual incorreta.')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err instanceof UnwillingToPerformError) {
|
||||||
|
throw new Error(
|
||||||
|
'A senha atual está correta, mas o servidor recusou a alteração. Verifique se a nova senha atende aos requisitos de complexidade.'
|
||||||
|
)
|
||||||
|
} else throw err
|
||||||
|
} finally {
|
||||||
|
await ldapClient.unbind()
|
||||||
|
console.log('unbinded')
|
||||||
|
}
|
||||||
|
return 'FAIL'
|
||||||
|
}
|
|
@ -1,21 +1,46 @@
|
||||||
import { initTRPC, TRPCError } from "@trpc/server";
|
import { initTRPC, TRPCError } from '@trpc/server'
|
||||||
import * as trpcExpress from "@trpc/server/adapters/express";
|
import * as trpcExpress from '@trpc/server/adapters/express'
|
||||||
|
|
||||||
// import { z } from "zod";
|
import { z } from 'zod'
|
||||||
|
import { updatePassword } from './lib/updatePassword'
|
||||||
|
|
||||||
export const t = initTRPC.create();
|
export const { procedure, router } = initTRPC.create()
|
||||||
|
|
||||||
const { query, mutation, input } = t.procedure;
|
const { query, input } = procedure
|
||||||
|
|
||||||
export const appRouter = t.router({
|
export const appRouter = router({
|
||||||
hello: query(async () => {
|
hello: query(async () => {
|
||||||
return "Hello World!";
|
return 'Hello World!'
|
||||||
}),
|
}),
|
||||||
});
|
|
||||||
|
updatePassword: input(
|
||||||
|
z.object({
|
||||||
|
username: z.string(),
|
||||||
|
password: z.string(),
|
||||||
|
newPassword: z.string().min(8)
|
||||||
|
})
|
||||||
|
).mutation(async ({ input }) => {
|
||||||
|
console.log('input', input)
|
||||||
|
|
||||||
|
const { username, password, newPassword } = input
|
||||||
|
try {
|
||||||
|
await updatePassword({
|
||||||
|
username,
|
||||||
|
password,
|
||||||
|
newPassword
|
||||||
|
})
|
||||||
|
} catch (err: any) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: 'BAD_REQUEST',
|
||||||
|
message: err.message
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
// export type definition of API
|
// export type definition of API
|
||||||
export type AppRouter = typeof appRouter;
|
export type AppRouter = typeof appRouter
|
||||||
|
|
||||||
export const trpcMiddleware = trpcExpress.createExpressMiddleware({
|
export const trpcMiddleware = trpcExpress.createExpressMiddleware({
|
||||||
router: appRouter,
|
router: appRouter
|
||||||
});
|
})
|
||||||
|
|
Loading…
Reference in New Issue
Block a user