mirror of
https://github.com/LemmyNet/lemmy-ui.git
synced 2024-11-25 13:51:13 +00:00
Make modal work when enabling and disabling 2FA
This commit is contained in:
parent
bf7b75e070
commit
5f611b8561
5 changed files with 215 additions and 75 deletions
|
@ -42,6 +42,7 @@
|
||||||
"@babel/preset-typescript": "^7.21.5",
|
"@babel/preset-typescript": "^7.21.5",
|
||||||
"@babel/runtime": "^7.21.5",
|
"@babel/runtime": "^7.21.5",
|
||||||
"@emoji-mart/data": "^1.1.0",
|
"@emoji-mart/data": "^1.1.0",
|
||||||
|
"@shortcm/qr-image": "^9.0.2",
|
||||||
"autosize": "^6.0.1",
|
"autosize": "^6.0.1",
|
||||||
"babel-loader": "^9.1.2",
|
"babel-loader": "^9.1.2",
|
||||||
"babel-plugin-inferno": "^6.6.0",
|
"babel-plugin-inferno": "^6.6.0",
|
||||||
|
|
|
@ -448,3 +448,7 @@ br.big {
|
||||||
.skip-link:focus {
|
.skip-link:focus {
|
||||||
top: 0;
|
top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.totp-link {
|
||||||
|
width: fit-content;
|
||||||
|
}
|
||||||
|
|
|
@ -1,34 +1,70 @@
|
||||||
import { Component, linkEvent } from "inferno";
|
import { Component, linkEvent } from "inferno";
|
||||||
import { I18NextService } from "../../services";
|
import { I18NextService } from "../../services";
|
||||||
|
import { toast } from "../../toast";
|
||||||
|
|
||||||
interface TotpModalProps {
|
interface TotpModalProps {
|
||||||
onSubmit: (totp: string) => void;
|
/**Takes totp as param, returns whether submit was successful*/
|
||||||
|
onSubmit: (totp: string) => Promise<boolean>;
|
||||||
type: "login" | "remove" | "generate";
|
type: "login" | "remove" | "generate";
|
||||||
secretUrl?: string;
|
secretUrl?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TotpModalState {
|
interface TotpModalState {
|
||||||
totp: string;
|
totp: string;
|
||||||
|
qrCode?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const TOTP_LENGTH = 6;
|
const TOTP_LENGTH = 6;
|
||||||
|
|
||||||
function focusInput() {
|
async function handleSubmit(modal: TotpModal, totp: string) {
|
||||||
document.getElementById("totp-input-0")?.focus();
|
const succeeded = await modal.props.onSubmit(totp);
|
||||||
|
|
||||||
|
modal.setState({ totp: "" });
|
||||||
|
if (succeeded) {
|
||||||
|
document.getElementById("totp-close-button")?.click();
|
||||||
|
} else {
|
||||||
|
document.getElementById(`totp-input-0`)?.focus();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleInput(
|
function handleInput(
|
||||||
{ modal, i }: { modal: TotpModal; i: number },
|
{ modal, i }: { modal: TotpModal; i: number },
|
||||||
event: any,
|
event: any,
|
||||||
) {
|
) {
|
||||||
const { totp } = modal.state;
|
modal.setState(prev => ({ ...prev, totp: prev.totp + event.target.value }));
|
||||||
|
document.getElementById(`totp-input-${i + 1}`)?.focus();
|
||||||
|
|
||||||
|
const { totp } = modal.state;
|
||||||
if (totp.length >= TOTP_LENGTH) {
|
if (totp.length >= TOTP_LENGTH) {
|
||||||
modal.props.onSubmit(totp);
|
handleSubmit(modal, totp);
|
||||||
} else {
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleKeyUp(
|
||||||
|
{ modal, i }: { modal: TotpModal; i: number },
|
||||||
|
event: any,
|
||||||
|
) {
|
||||||
|
if (event.key === "Backspace" && i > 0) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
modal.setState(prev => ({
|
modal.setState(prev => ({
|
||||||
totp: prev.totp + event.target.value,
|
...prev,
|
||||||
|
totp: prev.totp.slice(0, prev.totp.length - 1),
|
||||||
}));
|
}));
|
||||||
document.getElementById(`totp-input-${i + 1}`)?.focus();
|
document.getElementById(`totp-input-${i - 1}`)?.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handlePaste(modal: TotpModal, event: any) {
|
||||||
|
event.preventDefault();
|
||||||
|
const text: string = event.clipboardData.getData("text");
|
||||||
|
|
||||||
|
if (text.length > TOTP_LENGTH || isNaN(Number(text))) {
|
||||||
|
toast("Invalid TOTP: Must be string of six digits", "danger");
|
||||||
|
modal.setState({ totp: "" });
|
||||||
|
} else {
|
||||||
|
modal.setState({ totp: text });
|
||||||
|
handleSubmit(modal, text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,12 +80,13 @@ export default class TotpModal extends Component<
|
||||||
super(props, context);
|
super(props, context);
|
||||||
|
|
||||||
this.clearTotp = this.clearTotp.bind(this);
|
this.clearTotp = this.clearTotp.bind(this);
|
||||||
|
this.handleShow = this.handleShow.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
async componentDidMount() {
|
||||||
document
|
document
|
||||||
.getElementById("totpModal")
|
.getElementById("totpModal")
|
||||||
?.addEventListener("shown.bs.modal", focusInput);
|
?.addEventListener("shown.bs.modal", this.handleShow);
|
||||||
|
|
||||||
document
|
document
|
||||||
.getElementById("totpModal")
|
.getElementById("totpModal")
|
||||||
|
@ -59,7 +96,7 @@ export default class TotpModal extends Component<
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
document
|
document
|
||||||
.getElementById("totpModal")
|
.getElementById("totpModal")
|
||||||
?.removeEventListener("shown.bs.modal", focusInput);
|
?.removeEventListener("shown.bs.modal", this.handleShow);
|
||||||
|
|
||||||
document
|
document
|
||||||
.getElementById("totpModal")
|
.getElementById("totpModal")
|
||||||
|
@ -67,7 +104,7 @@ export default class TotpModal extends Component<
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { type } = this.props;
|
const { type, secretUrl } = this.props;
|
||||||
const { totp } = this.state;
|
const { totp } = this.state;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -93,39 +130,61 @@ export default class TotpModal extends Component<
|
||||||
className="btn-close"
|
className="btn-close"
|
||||||
data-bs-dismiss="modal"
|
data-bs-dismiss="modal"
|
||||||
aria-label="Close"
|
aria-label="Close"
|
||||||
|
id="totp-close-button"
|
||||||
/>
|
/>
|
||||||
</header>
|
</header>
|
||||||
<form
|
<div className="modal-body">
|
||||||
id="totp-form"
|
{type === "generate" && (
|
||||||
className="modal-body d-flex flex-column justify-content-center"
|
<div className="mx-auto">
|
||||||
>
|
<a
|
||||||
<label
|
className="btn btn-secondary mx-auto d-block totp-link"
|
||||||
className="form-label"
|
href={secretUrl}
|
||||||
id="totp-input-label"
|
>
|
||||||
htmlFor="totp-input-0"
|
Click here for your TOTP link
|
||||||
>
|
</a>
|
||||||
Enter TOTP
|
<div className="mx-auto mt-3 w-50 h-50 text-center">
|
||||||
</label>
|
<span className="fw-semibold">
|
||||||
<div className="d-flex justify-content-between align-items-center p-4">
|
or scan this QR code in your authenticator app
|
||||||
{Array.from(Array(TOTP_LENGTH).keys()).map(i => (
|
</span>
|
||||||
<input
|
<img
|
||||||
key={
|
src={this.state.qrCode}
|
||||||
i /*While using indices as keys is usually bad practice, in this case we don't have to worry about the order of the list items changing.*/
|
className="d-block mt-1 mx-auto"
|
||||||
}
|
alt="TOTP QR code"
|
||||||
type="text"
|
/>
|
||||||
inputMode="numeric"
|
</div>
|
||||||
autoComplete="one-time-code"
|
</div>
|
||||||
maxLength={1}
|
)}
|
||||||
value={totp[i] ?? ""}
|
<form id="totp-form">
|
||||||
disabled={totp.length !== i}
|
<label
|
||||||
aria-labelledby="totp-input-label"
|
className="form-label ms-2 mt-4 fw-bold"
|
||||||
id={`totp-input-${i}`}
|
id="totp-input-label"
|
||||||
className="form-control form-control-lg mx-2"
|
htmlFor="totp-input-0"
|
||||||
onInput={linkEvent({ modal: this, i }, handleInput)}
|
>
|
||||||
/>
|
Enter TOTP
|
||||||
))}
|
</label>
|
||||||
</div>
|
<div className="d-flex justify-content-between align-items-center p-2">
|
||||||
</form>
|
{Array.from(Array(TOTP_LENGTH).keys()).map(i => (
|
||||||
|
<input
|
||||||
|
key={
|
||||||
|
i /*While using indices as keys is usually bad practice, in this case we don't have to worry about the order of the list items changing.*/
|
||||||
|
}
|
||||||
|
type="text"
|
||||||
|
inputMode="numeric"
|
||||||
|
autoComplete="one-time-code"
|
||||||
|
maxLength={1}
|
||||||
|
value={totp[i] ?? ""}
|
||||||
|
disabled={totp.length !== i}
|
||||||
|
aria-labelledby="totp-input-label"
|
||||||
|
id={`totp-input-${i}`}
|
||||||
|
className="form-control form-control-lg mx-2"
|
||||||
|
onInput={linkEvent({ modal: this, i }, handleInput)}
|
||||||
|
onKeyUp={linkEvent({ modal: this, i }, handleKeyUp)}
|
||||||
|
onPaste={linkEvent(this, handlePaste)}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
<footer className="modal-footer">
|
<footer className="modal-footer">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
@ -142,7 +201,22 @@ export default class TotpModal extends Component<
|
||||||
}
|
}
|
||||||
|
|
||||||
clearTotp() {
|
clearTotp() {
|
||||||
console.log("clearing");
|
|
||||||
this.setState({ totp: "" });
|
this.setState({ totp: "" });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async handleShow() {
|
||||||
|
document.getElementById("totp-input-0")?.focus();
|
||||||
|
|
||||||
|
if (this.props.type === "generate") {
|
||||||
|
const { getSVG } = await import("@shortcm/qr-image/lib/svg");
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
qrCode: URL.createObjectURL(
|
||||||
|
new Blob([(await getSVG(this.props.secretUrl!)).buffer], {
|
||||||
|
type: "image/svg+xml",
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1038,35 +1038,29 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="btn btn-secondary my-2"
|
||||||
|
onClick={
|
||||||
|
!totpEnabled ? linkEvent(this, handleGenerateTotp) : undefined
|
||||||
|
}
|
||||||
|
data-bs-toggle="modal"
|
||||||
|
data-bs-target="#totpModal"
|
||||||
|
>{`${
|
||||||
|
totpEnabled ? "Disable" : "Enable"
|
||||||
|
} 2 factor authentication`}</button>
|
||||||
{totpEnabled ? (
|
{totpEnabled ? (
|
||||||
<div>
|
<TotpModal type="remove" onSubmit={this.handleDisable2fa} />
|
||||||
<TotpModal type="remove" onSubmit={this.handleDisable2fa} />
|
|
||||||
<button type="button" className="btn btn-secondary">
|
|
||||||
Disable 2 factor authentication
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
) : (
|
) : (
|
||||||
<div>
|
<TotpModal
|
||||||
<TotpModal
|
type="generate"
|
||||||
type="generate"
|
onSubmit={this.handleEnable2fa}
|
||||||
secretUrl={
|
secretUrl={
|
||||||
generateTotpRes.state === "success"
|
generateTotpRes.state === "success"
|
||||||
? generateTotpRes.data.totp_secret_url
|
? generateTotpRes.data.totp_secret_url
|
||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
onSubmit={this.handleEnable2fa}
|
/>
|
||||||
/>
|
|
||||||
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="btn btn-secondary"
|
|
||||||
onClick={linkEvent(this, handleGenerateTotp)}
|
|
||||||
data-bs-toggle="modal"
|
|
||||||
data-bs-target="#totpModal"
|
|
||||||
>
|
|
||||||
Enable 2 factor authentication
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -1080,19 +1074,23 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
totp_token: totp,
|
totp_token: totp,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
console.log("updating 2fa");
|
||||||
|
|
||||||
if (updateTotpRes.state === "failed") {
|
if (updateTotpRes.state === "failed") {
|
||||||
toast(updateTotpRes.msg, "danger");
|
toast("Invalid TOTP", "danger");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({ updateTotpRes });
|
this.setState({ updateTotpRes });
|
||||||
|
|
||||||
|
return updateTotpRes.state === "success";
|
||||||
}
|
}
|
||||||
|
|
||||||
handleEnable2fa(totp: string) {
|
handleEnable2fa(totp: string) {
|
||||||
this.handleToggle2fa(totp, true);
|
return this.handleToggle2fa(totp, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDisable2fa(totp: string) {
|
handleDisable2fa(totp: string) {
|
||||||
this.handleToggle2fa(totp, false);
|
return this.handleToggle2fa(totp, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePersonSearch = debounce(async (text: string) => {
|
handlePersonSearch = debounce(async (text: string) => {
|
||||||
|
|
65
yarn.lock
65
yarn.lock
|
@ -1613,6 +1613,20 @@
|
||||||
"@nodelib/fs.scandir" "2.1.5"
|
"@nodelib/fs.scandir" "2.1.5"
|
||||||
fastq "^1.6.0"
|
fastq "^1.6.0"
|
||||||
|
|
||||||
|
"@pdf-lib/standard-fonts@^1.0.0":
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@pdf-lib/standard-fonts/-/standard-fonts-1.0.0.tgz#8ba691c4421f71662ed07c9a0294b44528af2d7f"
|
||||||
|
integrity sha512-hU30BK9IUN/su0Mn9VdlVKsWBS6GyhVfqjwl1FjZN4TxP6cCw0jP2w7V3Hf5uX7M0AZJ16vey9yE0ny7Sa59ZA==
|
||||||
|
dependencies:
|
||||||
|
pako "^1.0.6"
|
||||||
|
|
||||||
|
"@pdf-lib/upng@^1.0.1":
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@pdf-lib/upng/-/upng-1.0.1.tgz#7dc9c636271aca007a9df4deaf2dd7e7960280cb"
|
||||||
|
integrity sha512-dQK2FUMQtowVP00mtIksrlZhdFXQZPC+taih1q4CvPZ5vqdxR/LKBaFg0oAfzd1GlHZXXSPdQfzQnt+ViGvEIQ==
|
||||||
|
dependencies:
|
||||||
|
pako "^1.0.10"
|
||||||
|
|
||||||
"@pkgjs/parseargs@^0.11.0":
|
"@pkgjs/parseargs@^0.11.0":
|
||||||
version "0.11.0"
|
version "0.11.0"
|
||||||
resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33"
|
resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33"
|
||||||
|
@ -1685,6 +1699,16 @@
|
||||||
domhandler "^5.0.3"
|
domhandler "^5.0.3"
|
||||||
selderee "^0.11.0"
|
selderee "^0.11.0"
|
||||||
|
|
||||||
|
"@shortcm/qr-image@^9.0.2":
|
||||||
|
version "9.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@shortcm/qr-image/-/qr-image-9.0.2.tgz#a24ed06026466974badb7fc7fc863d704d496bbe"
|
||||||
|
integrity sha512-/hz2NqFlT0Xmd5FDiYSsb/lDucZbByWeFUiEz1ekFnz6MHtdpv03mSMSsLm+LF8n/LgumjBcKci3gG2TMirIJA==
|
||||||
|
dependencies:
|
||||||
|
color-string "^1.9.1"
|
||||||
|
js-base64 "^3.7.5"
|
||||||
|
pdf-lib "^1.17.1"
|
||||||
|
sharp "^0.32.5"
|
||||||
|
|
||||||
"@surma/rollup-plugin-off-main-thread@^2.2.3":
|
"@surma/rollup-plugin-off-main-thread@^2.2.3":
|
||||||
version "2.2.3"
|
version "2.2.3"
|
||||||
resolved "https://registry.yarnpkg.com/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz#ee34985952ca21558ab0d952f00298ad2190c053"
|
resolved "https://registry.yarnpkg.com/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz#ee34985952ca21558ab0d952f00298ad2190c053"
|
||||||
|
@ -3210,7 +3234,7 @@ color-name@^1.0.0, color-name@~1.1.4:
|
||||||
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
|
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
|
||||||
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
|
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
|
||||||
|
|
||||||
color-string@^1.9.0:
|
color-string@^1.9.0, color-string@^1.9.1:
|
||||||
version "1.9.1"
|
version "1.9.1"
|
||||||
resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.9.1.tgz#4467f9146f036f855b764dfb5bf8582bf342c7a4"
|
resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.9.1.tgz#4467f9146f036f855b764dfb5bf8582bf342c7a4"
|
||||||
integrity sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==
|
integrity sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==
|
||||||
|
@ -5874,6 +5898,11 @@ jest-worker@^27.4.5:
|
||||||
merge-stream "^2.0.0"
|
merge-stream "^2.0.0"
|
||||||
supports-color "^8.0.0"
|
supports-color "^8.0.0"
|
||||||
|
|
||||||
|
js-base64@^3.7.5:
|
||||||
|
version "3.7.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-3.7.5.tgz#21e24cf6b886f76d6f5f165bfcd69cc55b9e3fca"
|
||||||
|
integrity sha512-3MEt5DTINKqfScXKfJFrRbxkrnk2AxPWGBL/ycjz4dK8iqiSJ06UxD8jh8xuh6p10TX4t2+7FsBYVxxQbMg+qA==
|
||||||
|
|
||||||
"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
|
"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
|
||||||
version "4.0.0"
|
version "4.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
|
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
|
||||||
|
@ -7451,6 +7480,11 @@ pacote@^8.1.6:
|
||||||
unique-filename "^1.1.0"
|
unique-filename "^1.1.0"
|
||||||
which "^1.3.0"
|
which "^1.3.0"
|
||||||
|
|
||||||
|
pako@^1.0.10, pako@^1.0.11, pako@^1.0.6:
|
||||||
|
version "1.0.11"
|
||||||
|
resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf"
|
||||||
|
integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==
|
||||||
|
|
||||||
parallel-transform@^1.1.0:
|
parallel-transform@^1.1.0:
|
||||||
version "1.2.0"
|
version "1.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/parallel-transform/-/parallel-transform-1.2.0.tgz#9049ca37d6cb2182c3b1d2c720be94d14a5814fc"
|
resolved "https://registry.yarnpkg.com/parallel-transform/-/parallel-transform-1.2.0.tgz#9049ca37d6cb2182c3b1d2c720be94d14a5814fc"
|
||||||
|
@ -7570,6 +7604,16 @@ path-type@^4.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
|
resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
|
||||||
integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
|
integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
|
||||||
|
|
||||||
|
pdf-lib@^1.17.1:
|
||||||
|
version "1.17.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/pdf-lib/-/pdf-lib-1.17.1.tgz#9e7dd21261a0c1fb17992580885b39e7d08f451f"
|
||||||
|
integrity sha512-V/mpyJAoTsN4cnP31vc0wfNA1+p20evqqnap0KLoRUN0Yk/p3wN52DOEsL4oBFcLdb76hlpKPtzJIgo67j/XLw==
|
||||||
|
dependencies:
|
||||||
|
"@pdf-lib/standard-fonts" "^1.0.0"
|
||||||
|
"@pdf-lib/upng" "^1.0.1"
|
||||||
|
pako "^1.0.11"
|
||||||
|
tslib "^1.11.1"
|
||||||
|
|
||||||
peberminta@^0.9.0:
|
peberminta@^0.9.0:
|
||||||
version "0.9.0"
|
version "0.9.0"
|
||||||
resolved "https://registry.yarnpkg.com/peberminta/-/peberminta-0.9.0.tgz#8ec9bc0eb84b7d368126e71ce9033501dca2a352"
|
resolved "https://registry.yarnpkg.com/peberminta/-/peberminta-0.9.0.tgz#8ec9bc0eb84b7d368126e71ce9033501dca2a352"
|
||||||
|
@ -8570,6 +8614,20 @@ sharp@^0.32.4:
|
||||||
tar-fs "^3.0.4"
|
tar-fs "^3.0.4"
|
||||||
tunnel-agent "^0.6.0"
|
tunnel-agent "^0.6.0"
|
||||||
|
|
||||||
|
sharp@^0.32.5:
|
||||||
|
version "0.32.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.32.6.tgz#6ad30c0b7cd910df65d5f355f774aa4fce45732a"
|
||||||
|
integrity sha512-KyLTWwgcR9Oe4d9HwCwNM2l7+J0dUQwn/yf7S0EnTtb0eVS4RxO0eUSvxPtzT4F3SY+C4K6fqdv/DO27sJ/v/w==
|
||||||
|
dependencies:
|
||||||
|
color "^4.2.3"
|
||||||
|
detect-libc "^2.0.2"
|
||||||
|
node-addon-api "^6.1.0"
|
||||||
|
prebuild-install "^7.1.1"
|
||||||
|
semver "^7.5.4"
|
||||||
|
simple-get "^4.0.1"
|
||||||
|
tar-fs "^3.0.4"
|
||||||
|
tunnel-agent "^0.6.0"
|
||||||
|
|
||||||
shebang-command@^1.2.0:
|
shebang-command@^1.2.0:
|
||||||
version "1.2.0"
|
version "1.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"
|
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"
|
||||||
|
@ -9386,6 +9444,11 @@ ts-api-utils@^1.0.1:
|
||||||
resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.0.1.tgz#8144e811d44c749cd65b2da305a032510774452d"
|
resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.0.1.tgz#8144e811d44c749cd65b2da305a032510774452d"
|
||||||
integrity sha512-lC/RGlPmwdrIBFTX59wwNzqh7aR2otPNPR/5brHZm/XKFYKsfqxihXUe9pU3JI+3vGkl+vyCoNNnPhJn3aLK1A==
|
integrity sha512-lC/RGlPmwdrIBFTX59wwNzqh7aR2otPNPR/5brHZm/XKFYKsfqxihXUe9pU3JI+3vGkl+vyCoNNnPhJn3aLK1A==
|
||||||
|
|
||||||
|
tslib@^1.11.1:
|
||||||
|
version "1.14.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
|
||||||
|
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
|
||||||
|
|
||||||
tslib@^2.1.0, tslib@^2.5.0, tslib@^2.6.0:
|
tslib@^2.1.0, tslib@^2.5.0, tslib@^2.6.0:
|
||||||
version "2.6.0"
|
version "2.6.0"
|
||||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.0.tgz#b295854684dbda164e181d259a22cd779dcd7bc3"
|
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.0.tgz#b295854684dbda164e181d259a22cd779dcd7bc3"
|
||||||
|
|
Loading…
Reference in a new issue