mirror of
https://github.com/Paris-est-Ludique/intranet.git
synced 2025-06-09 17:14:21 +02:00
Adds API login
This commit is contained in:
parent
051784a6da
commit
f952e24884
@ -77,6 +77,7 @@
|
||||
"@types/lodash": "^4.14.177",
|
||||
"autoprefixer": "^10.2.6",
|
||||
"axios": "^0.21.1",
|
||||
"bcrypt": "^5.0.1",
|
||||
"chalk": "^4.1.1",
|
||||
"compression": "^1.7.4",
|
||||
"connected-react-router": "^6.9.1",
|
||||
@ -120,6 +121,7 @@
|
||||
"@pmmmwh/react-refresh-webpack-plugin": "^0.4.3",
|
||||
"@testing-library/jest-dom": "^5.14.1",
|
||||
"@testing-library/react": "^12.0.0",
|
||||
"@types/bcrypt": "^5.0.0",
|
||||
"@types/compression": "^1.7.1",
|
||||
"@types/compression-webpack-plugin": "^6.0.6",
|
||||
"@types/css-minimizer-webpack-plugin": "^3.0.2",
|
||||
|
@ -7,7 +7,6 @@ const certbotRouter: Router = Router()
|
||||
certbotRouter.use((request: Request, response: Response, _next: NextFunction) => {
|
||||
const filename = request.originalUrl.replace(/.*\//, "")
|
||||
const resolvedPath: string = path.resolve(`../certbot/.well-known/acme-challenge/${filename}`)
|
||||
console.log("response", resolvedPath)
|
||||
response.setHeader("Content-Type", "text/html")
|
||||
return response.sendFile(resolvedPath)
|
||||
})
|
||||
|
@ -1,5 +1,5 @@
|
||||
import getExpressAccessors from "./expressAccessors"
|
||||
import { Envie, EnvieWithoutId } from "../services/envies"
|
||||
import { Envie, EnvieWithoutId } from "../../services/envies"
|
||||
|
||||
const { listGetRequest, getRequest, setRequest, addRequest } = getExpressAccessors<
|
||||
EnvieWithoutId,
|
@ -1,5 +1,5 @@
|
||||
import getExpressAccessors from "./expressAccessors"
|
||||
import { JeuJav, JeuJavWithoutId } from "../services/jeuxJav"
|
||||
import { JeuJav, JeuJavWithoutId } from "../../services/jeuxJav"
|
||||
|
||||
const { listGetRequest, getRequest, setRequest, addRequest } = getExpressAccessors<
|
||||
JeuJavWithoutId,
|
@ -1,5 +1,5 @@
|
||||
import getExpressAccessors from "./expressAccessors"
|
||||
import { Membre, MembreWithoutId } from "../services/membres"
|
||||
import { Membre, MembreWithoutId } from "../../services/membres"
|
||||
|
||||
const { listGetRequest, getRequest, setRequest, addRequest } = getExpressAccessors<
|
||||
MembreWithoutId,
|
@ -15,9 +15,10 @@ import devServer from "./devServer"
|
||||
import ssr from "./ssr"
|
||||
|
||||
import certbotRouter from "../routes/certbot"
|
||||
import { jeuJavListGet } from "../gsheets/jeuJav"
|
||||
import { envieListGet, envieAdd } from "../gsheets/envies"
|
||||
import { membreGet, membreSet } from "../gsheets/membres"
|
||||
import { jeuJavListGet } from "./gsheets/jeuJav"
|
||||
import { envieListGet, envieAdd } from "./gsheets/envies"
|
||||
import { membreGet, membreSet } from "./gsheets/membres"
|
||||
import signInHandler from "./userManagement/signIn"
|
||||
import config from "../config"
|
||||
|
||||
const app = express()
|
||||
@ -42,7 +43,11 @@ app.use(express.static(path.resolve(process.cwd(), "public")))
|
||||
// Enable dev-server in development
|
||||
if (__DEV__) devServer(app)
|
||||
|
||||
// Google Sheets requests
|
||||
/**
|
||||
* APIs
|
||||
*/
|
||||
|
||||
// Google Sheets API
|
||||
app.use(express.json())
|
||||
app.get("/JeuJavListGet", jeuJavListGet)
|
||||
app.get("/EnvieListGet", envieListGet)
|
||||
@ -50,6 +55,9 @@ app.get("/MembreGet", membreGet)
|
||||
app.post("/MembreSet", membreSet)
|
||||
app.post("/EnvieAdd", envieAdd)
|
||||
|
||||
// Sign in & up API
|
||||
app.post("/api/user/login", signInHandler)
|
||||
|
||||
// Use React server-side rendering middleware
|
||||
app.get("*", ssr)
|
||||
|
||||
|
56
src/server/userManagement/__tests__/signIn.tsx
Executable file
56
src/server/userManagement/__tests__/signIn.tsx
Executable file
@ -0,0 +1,56 @@
|
||||
/**
|
||||
* @jest-environment jsdom
|
||||
*/
|
||||
|
||||
import { signIn } from "../signIn"
|
||||
|
||||
// Could do a full test with wget --header='Content-Type:application/json' --post-data='{"email":"pikiou.sub@gmail.com","password":"mot de passe"}' http://localhost:3000/api/user/login
|
||||
|
||||
const mockUser = {
|
||||
mail: "my.email@gmail.com",
|
||||
passe: "$2y$10$cuKFHEow2IVSZSPtoVsw6uZFNFOOP/v1V7fubbyvrxhZdsnxLHr.2",
|
||||
prenom: "monPrénom",
|
||||
}
|
||||
|
||||
jest.mock("../../gsheets/accessors", () => () => ({
|
||||
listGet: () => [mockUser],
|
||||
}))
|
||||
|
||||
describe("signIn with", () => {
|
||||
it("right password", async () => {
|
||||
const res = await signIn("my.email@gmail.com", "12345678")
|
||||
expect(res).toEqual({
|
||||
membre: {
|
||||
prenom: mockUser.prenom,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it("invalid password length", async () => {
|
||||
await expect(signIn("my.email@gmail.com", "11011")).rejects.toThrow("Mot de passe invalid")
|
||||
})
|
||||
|
||||
it("empty password", async () => {
|
||||
await expect(signIn("my.email@gmail.com", " ")).rejects.toThrow("Mot de passe invalid")
|
||||
})
|
||||
|
||||
it("wrong password", async () => {
|
||||
await expect(signIn("my.email@gmail.com", "1234567891011")).rejects.toThrow(
|
||||
"Mauvais mot de passe pour cet email"
|
||||
)
|
||||
})
|
||||
|
||||
it("invalid email format", async () => {
|
||||
await expect(signIn("my.email@gmail", "12345678")).rejects.toThrow("Email invalid")
|
||||
})
|
||||
|
||||
it("empty email", async () => {
|
||||
await expect(signIn(" ", "12345678")).rejects.toThrow("Email invalid")
|
||||
})
|
||||
|
||||
it("unknown email", async () => {
|
||||
await expect(signIn("mon.emailBidon@gmail.com", "12345678")).rejects.toThrow(
|
||||
"Cet email ne correspond à aucun utilisateur"
|
||||
)
|
||||
})
|
||||
})
|
55
src/server/userManagement/signIn.ts
Normal file
55
src/server/userManagement/signIn.ts
Normal file
@ -0,0 +1,55 @@
|
||||
import { Request, Response, NextFunction } from "express"
|
||||
import bcrypt from "bcrypt"
|
||||
import { Membre, MemberLogin, emailRegexp, passwordMinLength } from "../../services/membres"
|
||||
import getAccessors from "../gsheets/accessors"
|
||||
|
||||
const { listGet } = getAccessors("Membres", new Membre())
|
||||
|
||||
export default async function signInHandler(
|
||||
request: Request,
|
||||
response: Response,
|
||||
_next: NextFunction
|
||||
): Promise<void> {
|
||||
try {
|
||||
if (typeof request.body.email !== "string" || typeof request.body.password !== "string") {
|
||||
throw Error()
|
||||
}
|
||||
const res = await signIn(request.body.email, request.body.password)
|
||||
response.status(200).json(res)
|
||||
} catch (e: any) {
|
||||
if (e.message) {
|
||||
response.status(200).json({ error: e.message })
|
||||
} else {
|
||||
response.status(400).json(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function signIn(rawEmail: string, rawPassword: string): Promise<MemberLogin> {
|
||||
const email = rawEmail.replace(/^\s*/, "").replace(/\s*$/, "")
|
||||
if (!emailRegexp.test(email)) {
|
||||
throw Error("Email invalid")
|
||||
}
|
||||
|
||||
const password = rawPassword.replace(/^\s*/, "").replace(/\s*$/, "")
|
||||
if (password.length < passwordMinLength) {
|
||||
throw Error("Mot de passe invalid")
|
||||
}
|
||||
|
||||
const membres: Membre[] = await listGet()
|
||||
const membre = membres.find((m) => m.mail === email)
|
||||
if (!membre) {
|
||||
throw Error("Cet email ne correspond à aucun utilisateur")
|
||||
}
|
||||
|
||||
const passwordMatch = await bcrypt.compare(password, membre.passe.replace(/^\$2y/, "$2a"))
|
||||
if (!passwordMatch) {
|
||||
throw Error("Mauvais mot de passe pour cet email")
|
||||
}
|
||||
|
||||
return {
|
||||
membre: {
|
||||
prenom: membre.prenom,
|
||||
},
|
||||
}
|
||||
}
|
@ -28,6 +28,17 @@ export class Membre {
|
||||
passe = ""
|
||||
}
|
||||
|
||||
export const emailRegexp =
|
||||
/^(([^<>()[\].,;:\s@"]+(\.[^<>()[\].,;:\s@"]+)*)|(".+"))@(([^<>()[\].,;:\s@"]+\.)+[^<>()[\].,;:\s@"]{2,})$/i
|
||||
export const passwordMinLength = 4
|
||||
|
||||
export interface MemberLogin {
|
||||
membre?: {
|
||||
prenom: string
|
||||
}
|
||||
error?: string
|
||||
}
|
||||
|
||||
export type MembreWithoutId = Omit<Membre, "id">
|
||||
|
||||
export const membreGet = get<Membre>("Membre")
|
||||
|
95
yarn.lock
95
yarn.lock
@ -1271,6 +1271,21 @@
|
||||
dependencies:
|
||||
make-dir "^3.0.2"
|
||||
|
||||
"@mapbox/node-pre-gyp@^1.0.0":
|
||||
version "1.0.7"
|
||||
resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.7.tgz#a26919cac6595662703330d1820a0ca206f45521"
|
||||
integrity sha512-PplSvl4pJ5N3BkVjAdDzpPhVUPdC73JgttkR+LnBx2OORC1GCQsBjUeEuipf9uOaAM1SbxcdZFfR3KDTKm2S0A==
|
||||
dependencies:
|
||||
detect-libc "^1.0.3"
|
||||
https-proxy-agent "^5.0.0"
|
||||
make-dir "^3.1.0"
|
||||
node-fetch "^2.6.5"
|
||||
nopt "^5.0.0"
|
||||
npmlog "^6.0.0"
|
||||
rimraf "^3.0.2"
|
||||
semver "^7.3.5"
|
||||
tar "^6.1.11"
|
||||
|
||||
"@nodelib/fs.scandir@2.1.5":
|
||||
version "2.1.5"
|
||||
resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
|
||||
@ -1484,6 +1499,13 @@
|
||||
dependencies:
|
||||
"@babel/types" "^7.3.0"
|
||||
|
||||
"@types/bcrypt@^5.0.0":
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/bcrypt/-/bcrypt-5.0.0.tgz#a835afa2882d165aff5690893db314eaa98b9f20"
|
||||
integrity sha512-agtcFKaruL8TmcvqbndlqHPSJgsolhf/qPWchFlgnW1gECTN/nKbFcoFnvKAQRFfKbh+BO6A3SWdJu9t+xF3Lw==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/body-parser@*":
|
||||
version "1.19.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.2.tgz#aea2059e28b7658639081347ac4fab3de166e6f0"
|
||||
@ -2406,6 +2428,11 @@ aproba@^1.0.3:
|
||||
resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a"
|
||||
integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==
|
||||
|
||||
"aproba@^1.0.3 || ^2.0.0":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc"
|
||||
integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==
|
||||
|
||||
arch@^2.1.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/arch/-/arch-2.2.0.tgz#1bc47818f305764f23ab3306b0bfc086c5a29d11"
|
||||
@ -2418,6 +2445,14 @@ archive-type@^4.0.0:
|
||||
dependencies:
|
||||
file-type "^4.2.0"
|
||||
|
||||
are-we-there-yet@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz#372e0e7bd279d8e94c653aaa1f67200884bf3e1c"
|
||||
integrity sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==
|
||||
dependencies:
|
||||
delegates "^1.0.0"
|
||||
readable-stream "^3.6.0"
|
||||
|
||||
are-we-there-yet@~1.1.2:
|
||||
version "1.1.7"
|
||||
resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz#b15474a932adab4ff8a50d9adfa7e4e926f21146"
|
||||
@ -2735,6 +2770,14 @@ bcrypt-pbkdf@^1.0.0:
|
||||
dependencies:
|
||||
tweetnacl "^0.14.3"
|
||||
|
||||
bcrypt@^5.0.1:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/bcrypt/-/bcrypt-5.0.1.tgz#f1a2c20f208e2ccdceea4433df0c8b2c54ecdf71"
|
||||
integrity sha512-9BTgmrhZM2t1bNuDtrtIMVSmmxZBrJ71n8Wg+YgdjHuIWYF7SjjmCPZFB+/5i/o/PIeRpwVJR3P+NrpIItUjqw==
|
||||
dependencies:
|
||||
"@mapbox/node-pre-gyp" "^1.0.0"
|
||||
node-addon-api "^3.1.0"
|
||||
|
||||
big.js@^5.2.2:
|
||||
version "5.2.2"
|
||||
resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328"
|
||||
@ -3264,6 +3307,11 @@ color-name@~1.1.4:
|
||||
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
|
||||
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
|
||||
|
||||
color-support@^1.1.2:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2"
|
||||
integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==
|
||||
|
||||
colord@^2.9.1:
|
||||
version "2.9.1"
|
||||
resolved "https://registry.yarnpkg.com/colord/-/colord-2.9.1.tgz#c961ea0efeb57c9f0f4834458f26cb9cc4a3f90e"
|
||||
@ -3375,7 +3423,7 @@ connected-react-router@^6.9.1:
|
||||
immutable "^3.8.1 || ^4.0.0-rc.1"
|
||||
seamless-immutable "^7.1.3"
|
||||
|
||||
console-control-strings@^1.0.0, console-control-strings@~1.1.0:
|
||||
console-control-strings@^1.0.0, console-control-strings@^1.1.0, console-control-strings@~1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
|
||||
integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=
|
||||
@ -3856,6 +3904,11 @@ destroy@~1.0.4:
|
||||
resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80"
|
||||
integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=
|
||||
|
||||
detect-libc@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b"
|
||||
integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=
|
||||
|
||||
detect-newline@^3.0.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651"
|
||||
@ -4962,6 +5015,21 @@ functional-red-black-tree@^1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327"
|
||||
integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=
|
||||
|
||||
gauge@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/gauge/-/gauge-4.0.0.tgz#afba07aa0374a93c6219603b1fb83eaa2264d8f8"
|
||||
integrity sha512-F8sU45yQpjQjxKkm1UOAhf0U/O0aFt//Fl7hsrNVto+patMHjs7dPI9mFOGUKbhrgKm0S3EjW3scMFuQmWSROw==
|
||||
dependencies:
|
||||
ansi-regex "^5.0.1"
|
||||
aproba "^1.0.3 || ^2.0.0"
|
||||
color-support "^1.1.2"
|
||||
console-control-strings "^1.0.0"
|
||||
has-unicode "^2.0.1"
|
||||
signal-exit "^3.0.0"
|
||||
string-width "^4.2.3"
|
||||
strip-ansi "^6.0.1"
|
||||
wide-align "^1.1.2"
|
||||
|
||||
gauge@~2.7.3:
|
||||
version "2.7.4"
|
||||
resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7"
|
||||
@ -5438,7 +5506,7 @@ has-tostringtag@^1.0.0:
|
||||
dependencies:
|
||||
has-symbols "^1.0.2"
|
||||
|
||||
has-unicode@^2.0.0:
|
||||
has-unicode@^2.0.0, has-unicode@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9"
|
||||
integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=
|
||||
@ -7500,7 +7568,12 @@ no-case@^2.2.0:
|
||||
dependencies:
|
||||
lower-case "^1.1.1"
|
||||
|
||||
node-fetch@^2.6.1:
|
||||
node-addon-api@^3.1.0:
|
||||
version "3.2.1"
|
||||
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161"
|
||||
integrity sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==
|
||||
|
||||
node-fetch@^2.6.1, node-fetch@^2.6.5:
|
||||
version "2.6.6"
|
||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.6.tgz#1751a7c01834e8e1697758732e9efb6eeadfaf89"
|
||||
integrity sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==
|
||||
@ -7700,6 +7773,16 @@ npmlog@^4.0.0, npmlog@^4.1.2:
|
||||
gauge "~2.7.3"
|
||||
set-blocking "~2.0.0"
|
||||
|
||||
npmlog@^6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-6.0.0.tgz#ba9ef39413c3d936ea91553db7be49c34ad0520c"
|
||||
integrity sha512-03ppFRGlsyUaQFbGC2C8QWJN/C/K7PsfyD9aQdhVKAQIH4sQBc8WASqFBP7O+Ut4d2oo5LoeoboB3cGdBZSp6Q==
|
||||
dependencies:
|
||||
are-we-there-yet "^2.0.0"
|
||||
console-control-strings "^1.1.0"
|
||||
gauge "^4.0.0"
|
||||
set-blocking "^2.0.0"
|
||||
|
||||
nth-check@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.0.1.tgz#2efe162f5c3da06a28959fbd3db75dbeea9f0fc2"
|
||||
@ -8923,7 +9006,7 @@ readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.6, readable
|
||||
string_decoder "~1.1.1"
|
||||
util-deprecate "~1.0.1"
|
||||
|
||||
readable-stream@^3.1.1:
|
||||
readable-stream@^3.1.1, readable-stream@^3.6.0:
|
||||
version "3.6.0"
|
||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
|
||||
integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==
|
||||
@ -10121,7 +10204,7 @@ tar-stream@^1.5.2:
|
||||
to-buffer "^1.1.1"
|
||||
xtend "^4.0.0"
|
||||
|
||||
tar@^6.0.2:
|
||||
tar@^6.0.2, tar@^6.1.11:
|
||||
version "6.1.11"
|
||||
resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621"
|
||||
integrity sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==
|
||||
@ -10915,7 +10998,7 @@ which@^2.0.1, which@^2.0.2:
|
||||
dependencies:
|
||||
isexe "^2.0.0"
|
||||
|
||||
wide-align@^1.1.0:
|
||||
wide-align@^1.1.0, wide-align@^1.1.2:
|
||||
version "1.1.5"
|
||||
resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3"
|
||||
integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==
|
||||
|
Loading…
x
Reference in New Issue
Block a user