mirror of
https://github.com/NginxProxyManager/nginx-proxy-manager.git
synced 2025-09-24 07:20:34 +00:00
404 hosts add update complete, fix certbot renewals
and remove the need for email and agreement on cert requests
This commit is contained in:
@@ -13,6 +13,7 @@ import utils from "../lib/utils.js";
|
|||||||
import { ssl as logger } from "../logger.js";
|
import { ssl as logger } from "../logger.js";
|
||||||
import certificateModel from "../models/certificate.js";
|
import certificateModel from "../models/certificate.js";
|
||||||
import tokenModel from "../models/token.js";
|
import tokenModel from "../models/token.js";
|
||||||
|
import userModel from "../models/user.js";
|
||||||
import internalAuditLog from "./audit-log.js";
|
import internalAuditLog from "./audit-log.js";
|
||||||
import internalHost from "./host.js";
|
import internalHost from "./host.js";
|
||||||
import internalNginx from "./nginx.js";
|
import internalNginx from "./nginx.js";
|
||||||
@@ -81,7 +82,7 @@ const internalCertificate = {
|
|||||||
Promise.resolve({
|
Promise.resolve({
|
||||||
permission_visibility: "all",
|
permission_visibility: "all",
|
||||||
}),
|
}),
|
||||||
token: new tokenModel(),
|
token: tokenModel(),
|
||||||
},
|
},
|
||||||
{ id: certificate.id },
|
{ id: certificate.id },
|
||||||
)
|
)
|
||||||
@@ -118,10 +119,7 @@ const internalCertificate = {
|
|||||||
data.nice_name = data.domain_names.join(", ");
|
data.nice_name = data.domain_names.join(", ");
|
||||||
}
|
}
|
||||||
|
|
||||||
const certificate = await certificateModel
|
const certificate = await certificateModel.query().insertAndFetch(data).then(utils.omitRow(omissions()));
|
||||||
.query()
|
|
||||||
.insertAndFetch(data)
|
|
||||||
.then(utils.omitRow(omissions()));
|
|
||||||
|
|
||||||
if (certificate.provider === "letsencrypt") {
|
if (certificate.provider === "letsencrypt") {
|
||||||
// Request a new Cert from LE. Let the fun begin.
|
// Request a new Cert from LE. Let the fun begin.
|
||||||
@@ -139,12 +137,19 @@ const internalCertificate = {
|
|||||||
// 2. Disable them in nginx temporarily
|
// 2. Disable them in nginx temporarily
|
||||||
await internalCertificate.disableInUseHosts(inUseResult);
|
await internalCertificate.disableInUseHosts(inUseResult);
|
||||||
|
|
||||||
|
const user = await userModel.query().where("is_deleted", 0).andWhere("id", data.owner_user_id).first();
|
||||||
|
if (!user || !user.email) {
|
||||||
|
throw new error.ValidationError(
|
||||||
|
"A valid email address must be set on your user account to use Let's Encrypt",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// With DNS challenge no config is needed, so skip 3 and 5.
|
// With DNS challenge no config is needed, so skip 3 and 5.
|
||||||
if (certificate.meta?.dns_challenge) {
|
if (certificate.meta?.dns_challenge) {
|
||||||
try {
|
try {
|
||||||
await internalNginx.reload();
|
await internalNginx.reload();
|
||||||
// 4. Request cert
|
// 4. Request cert
|
||||||
await internalCertificate.requestLetsEncryptSslWithDnsChallenge(certificate);
|
await internalCertificate.requestLetsEncryptSslWithDnsChallenge(certificate, user.email);
|
||||||
await internalNginx.reload();
|
await internalNginx.reload();
|
||||||
// 6. Re-instate previously disabled hosts
|
// 6. Re-instate previously disabled hosts
|
||||||
await internalCertificate.enableInUseHosts(inUseResult);
|
await internalCertificate.enableInUseHosts(inUseResult);
|
||||||
@@ -159,9 +164,9 @@ const internalCertificate = {
|
|||||||
try {
|
try {
|
||||||
await internalNginx.generateLetsEncryptRequestConfig(certificate);
|
await internalNginx.generateLetsEncryptRequestConfig(certificate);
|
||||||
await internalNginx.reload();
|
await internalNginx.reload();
|
||||||
setTimeout(() => {}, 5000)
|
setTimeout(() => {}, 5000);
|
||||||
// 4. Request cert
|
// 4. Request cert
|
||||||
await internalCertificate.requestLetsEncryptSsl(certificate);
|
await internalCertificate.requestLetsEncryptSsl(certificate, user.email);
|
||||||
// 5. Remove LE config
|
// 5. Remove LE config
|
||||||
await internalNginx.deleteLetsEncryptRequestConfig(certificate);
|
await internalNginx.deleteLetsEncryptRequestConfig(certificate);
|
||||||
await internalNginx.reload();
|
await internalNginx.reload();
|
||||||
@@ -204,8 +209,7 @@ const internalCertificate = {
|
|||||||
data.meta = _.assign({}, data.meta || {}, certificate.meta);
|
data.meta = _.assign({}, data.meta || {}, certificate.meta);
|
||||||
|
|
||||||
// Add to audit log
|
// Add to audit log
|
||||||
await internalAuditLog
|
await internalAuditLog.add(access, {
|
||||||
.add(access, {
|
|
||||||
action: "created",
|
action: "created",
|
||||||
object_type: "certificate",
|
object_type: "certificate",
|
||||||
object_id: certificate.id,
|
object_id: certificate.id,
|
||||||
@@ -248,8 +252,7 @@ const internalCertificate = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add to audit log
|
// Add to audit log
|
||||||
await internalAuditLog
|
await internalAuditLog.add(access, {
|
||||||
.add(access, {
|
|
||||||
action: "updated",
|
action: "updated",
|
||||||
object_type: "certificate",
|
object_type: "certificate",
|
||||||
object_id: row.id,
|
object_id: row.id,
|
||||||
@@ -268,7 +271,7 @@ const internalCertificate = {
|
|||||||
* @return {Promise}
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
get: async (access, data) => {
|
get: async (access, data) => {
|
||||||
const accessData = await access.can("certificates:get", data.id)
|
const accessData = await access.can("certificates:get", data.id);
|
||||||
const query = certificateModel
|
const query = certificateModel
|
||||||
.query()
|
.query()
|
||||||
.where("is_deleted", 0)
|
.where("is_deleted", 0)
|
||||||
@@ -367,10 +370,7 @@ const internalCertificate = {
|
|||||||
throw new error.ItemNotFoundError(data.id);
|
throw new error.ItemNotFoundError(data.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
await certificateModel
|
await certificateModel.query().where("id", row.id).patch({
|
||||||
.query()
|
|
||||||
.where("id", row.id)
|
|
||||||
.patch({
|
|
||||||
is_deleted: 1,
|
is_deleted: 1,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -435,10 +435,7 @@ const internalCertificate = {
|
|||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
getCount: async (userId, visibility) => {
|
getCount: async (userId, visibility) => {
|
||||||
const query = certificateModel
|
const query = certificateModel.query().count("id as count").where("is_deleted", 0);
|
||||||
.query()
|
|
||||||
.count("id as count")
|
|
||||||
.where("is_deleted", 0);
|
|
||||||
|
|
||||||
if (visibility !== "all") {
|
if (visibility !== "all") {
|
||||||
query.andWhere("owner_user_id", userId);
|
query.andWhere("owner_user_id", userId);
|
||||||
@@ -501,12 +498,10 @@ const internalCertificate = {
|
|||||||
* @param {Access} access
|
* @param {Access} access
|
||||||
* @param {Object} data
|
* @param {Object} data
|
||||||
* @param {Array} data.domain_names
|
* @param {Array} data.domain_names
|
||||||
* @param {String} data.meta.letsencrypt_email
|
|
||||||
* @param {Boolean} data.meta.letsencrypt_agree
|
|
||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
createQuickCertificate: async (access, data) => {
|
createQuickCertificate: async (access, data) => {
|
||||||
return internalCertificate.create(access, {
|
return await internalCertificate.create(access, {
|
||||||
provider: "letsencrypt",
|
provider: "letsencrypt",
|
||||||
domain_names: data.domain_names,
|
domain_names: data.domain_names,
|
||||||
meta: data.meta,
|
meta: data.meta,
|
||||||
@@ -652,7 +647,7 @@ const internalCertificate = {
|
|||||||
const certData = {};
|
const certData = {};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await utils.execFile("openssl", ["x509", "-in", certificateFile, "-subject", "-noout"])
|
const result = await utils.execFile("openssl", ["x509", "-in", certificateFile, "-subject", "-noout"]);
|
||||||
// Examples:
|
// Examples:
|
||||||
// subject=CN = *.jc21.com
|
// subject=CN = *.jc21.com
|
||||||
// subject=CN = something.example.com
|
// subject=CN = something.example.com
|
||||||
@@ -739,9 +734,10 @@ const internalCertificate = {
|
|||||||
/**
|
/**
|
||||||
* Request a certificate using the http challenge
|
* Request a certificate using the http challenge
|
||||||
* @param {Object} certificate the certificate row
|
* @param {Object} certificate the certificate row
|
||||||
|
* @param {String} email the email address to use for registration
|
||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
requestLetsEncryptSsl: async (certificate) => {
|
requestLetsEncryptSsl: async (certificate, email) => {
|
||||||
logger.info(
|
logger.info(
|
||||||
`Requesting LetsEncrypt certificates for Cert #${certificate.id}: ${certificate.domain_names.join(", ")}`,
|
`Requesting LetsEncrypt certificates for Cert #${certificate.id}: ${certificate.domain_names.join(", ")}`,
|
||||||
);
|
);
|
||||||
@@ -760,7 +756,7 @@ const internalCertificate = {
|
|||||||
"--authenticator",
|
"--authenticator",
|
||||||
"webroot",
|
"webroot",
|
||||||
"--email",
|
"--email",
|
||||||
certificate.meta.letsencrypt_email,
|
email,
|
||||||
"--preferred-challenges",
|
"--preferred-challenges",
|
||||||
"dns,http",
|
"dns,http",
|
||||||
"--domains",
|
"--domains",
|
||||||
@@ -779,9 +775,10 @@ const internalCertificate = {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Object} certificate the certificate row
|
* @param {Object} certificate the certificate row
|
||||||
|
* @param {String} email the email address to use for registration
|
||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
requestLetsEncryptSslWithDnsChallenge: async (certificate) => {
|
requestLetsEncryptSslWithDnsChallenge: async (certificate, email) => {
|
||||||
await installPlugin(certificate.meta.dns_provider);
|
await installPlugin(certificate.meta.dns_provider);
|
||||||
const dnsPlugin = dnsPlugins[certificate.meta.dns_provider];
|
const dnsPlugin = dnsPlugins[certificate.meta.dns_provider];
|
||||||
logger.info(
|
logger.info(
|
||||||
@@ -807,7 +804,7 @@ const internalCertificate = {
|
|||||||
`npm-${certificate.id}`,
|
`npm-${certificate.id}`,
|
||||||
"--agree-tos",
|
"--agree-tos",
|
||||||
"--email",
|
"--email",
|
||||||
certificate.meta.letsencrypt_email,
|
email,
|
||||||
"--domains",
|
"--domains",
|
||||||
certificate.domain_names.join(","),
|
certificate.domain_names.join(","),
|
||||||
"--authenticator",
|
"--authenticator",
|
||||||
@@ -847,7 +844,7 @@ const internalCertificate = {
|
|||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
renew: async (access, data) => {
|
renew: async (access, data) => {
|
||||||
await access.can("certificates:update", data)
|
await access.can("certificates:update", data);
|
||||||
const certificate = await internalCertificate.get(access, data);
|
const certificate = await internalCertificate.get(access, data);
|
||||||
|
|
||||||
if (certificate.provider === "letsencrypt") {
|
if (certificate.provider === "letsencrypt") {
|
||||||
@@ -860,9 +857,7 @@ const internalCertificate = {
|
|||||||
`${internalCertificate.getLiveCertPath(certificate.id)}/fullchain.pem`,
|
`${internalCertificate.getLiveCertPath(certificate.id)}/fullchain.pem`,
|
||||||
);
|
);
|
||||||
|
|
||||||
const updatedCertificate = await certificateModel
|
const updatedCertificate = await certificateModel.query().patchAndFetchById(certificate.id, {
|
||||||
.query()
|
|
||||||
.patchAndFetchById(certificate.id, {
|
|
||||||
expires_on: moment(certInfo.dates.to, "X").format("YYYY-MM-DD HH:mm:ss"),
|
expires_on: moment(certInfo.dates.to, "X").format("YYYY-MM-DD HH:mm:ss"),
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1159,7 +1154,9 @@ const internalCertificate = {
|
|||||||
return "no-host";
|
return "no-host";
|
||||||
}
|
}
|
||||||
// Other errors
|
// Other errors
|
||||||
logger.info(`HTTP challenge test failed for domain ${domain} because code ${result.responsecode} was returned`);
|
logger.info(
|
||||||
|
`HTTP challenge test failed for domain ${domain} because code ${result.responsecode} was returned`,
|
||||||
|
);
|
||||||
return `other:${result.responsecode}`;
|
return `other:${result.responsecode}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1201,7 +1198,7 @@ const internalCertificate = {
|
|||||||
|
|
||||||
getLiveCertPath: (certificateId) => {
|
getLiveCertPath: (certificateId) => {
|
||||||
return `/etc/letsencrypt/live/npm-${certificateId}`;
|
return `/etc/letsencrypt/live/npm-${certificateId}`;
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default internalCertificate;
|
export default internalCertificate;
|
||||||
|
@@ -54,10 +54,21 @@ const internalDeadHost = {
|
|||||||
thisData.advanced_config = "";
|
thisData.advanced_config = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
const row = await deadHostModel.query().insertAndFetch(thisData).then(utils.omitRow(omissions()));
|
const row = await deadHostModel.query()
|
||||||
|
.insertAndFetch(thisData)
|
||||||
|
.then(utils.omitRow(omissions()));
|
||||||
|
|
||||||
|
// Add to audit log
|
||||||
|
await internalAuditLog.add(access, {
|
||||||
|
action: "created",
|
||||||
|
object_type: "dead-host",
|
||||||
|
object_id: row.id,
|
||||||
|
meta: _.assign({}, data.meta || {}, row.meta),
|
||||||
|
});
|
||||||
|
|
||||||
if (createCertificate) {
|
if (createCertificate) {
|
||||||
const cert = await internalCertificate.createQuickCertificate(access, data);
|
const cert = await internalCertificate.createQuickCertificate(access, data);
|
||||||
|
|
||||||
// update host with cert id
|
// update host with cert id
|
||||||
await internalDeadHost.update(access, {
|
await internalDeadHost.update(access, {
|
||||||
id: row.id,
|
id: row.id,
|
||||||
@@ -71,17 +82,13 @@ const internalDeadHost = {
|
|||||||
expand: ["certificate", "owner"],
|
expand: ["certificate", "owner"],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Sanity check
|
||||||
|
if (createCertificate && !freshRow.certificate_id) {
|
||||||
|
throw new errs.InternalValidationError("The host was created but the Certificate creation failed.");
|
||||||
|
}
|
||||||
|
|
||||||
// Configure nginx
|
// Configure nginx
|
||||||
await internalNginx.configure(deadHostModel, "dead_host", freshRow);
|
await internalNginx.configure(deadHostModel, "dead_host", freshRow);
|
||||||
data.meta = _.assign({}, data.meta || {}, freshRow.meta);
|
|
||||||
|
|
||||||
// Add to audit log
|
|
||||||
await internalAuditLog.add(access, {
|
|
||||||
action: "created",
|
|
||||||
object_type: "dead-host",
|
|
||||||
object_id: freshRow.id,
|
|
||||||
meta: data,
|
|
||||||
});
|
|
||||||
|
|
||||||
return freshRow;
|
return freshRow;
|
||||||
},
|
},
|
||||||
@@ -94,7 +101,6 @@ const internalDeadHost = {
|
|||||||
*/
|
*/
|
||||||
update: async (access, data) => {
|
update: async (access, data) => {
|
||||||
const createCertificate = data.certificate_id === "new";
|
const createCertificate = data.certificate_id === "new";
|
||||||
|
|
||||||
if (createCertificate) {
|
if (createCertificate) {
|
||||||
delete data.certificate_id;
|
delete data.certificate_id;
|
||||||
}
|
}
|
||||||
@@ -147,6 +153,13 @@ const internalDeadHost = {
|
|||||||
|
|
||||||
thisData = internalHost.cleanSslHstsData(thisData, row);
|
thisData = internalHost.cleanSslHstsData(thisData, row);
|
||||||
|
|
||||||
|
|
||||||
|
// do the row update
|
||||||
|
await deadHostModel
|
||||||
|
.query()
|
||||||
|
.where({id: data.id})
|
||||||
|
.patch(data);
|
||||||
|
|
||||||
// Add to audit log
|
// Add to audit log
|
||||||
await internalAuditLog.add(access, {
|
await internalAuditLog.add(access, {
|
||||||
action: "updated",
|
action: "updated",
|
||||||
|
@@ -6,46 +6,6 @@ import utils from "./utils.js";
|
|||||||
|
|
||||||
const CERTBOT_VERSION_REPLACEMENT = "$(certbot --version | grep -Eo '[0-9](\\.[0-9]+)+')";
|
const CERTBOT_VERSION_REPLACEMENT = "$(certbot --version | grep -Eo '[0-9](\\.[0-9]+)+')";
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {array} pluginKeys
|
|
||||||
*/
|
|
||||||
const installPlugins = async (pluginKeys) => {
|
|
||||||
let hasErrors = false;
|
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
if (pluginKeys.length === 0) {
|
|
||||||
resolve();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
batchflow(pluginKeys)
|
|
||||||
.sequential()
|
|
||||||
.each((_i, pluginKey, next) => {
|
|
||||||
certbot
|
|
||||||
.installPlugin(pluginKey)
|
|
||||||
.then(() => {
|
|
||||||
next();
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
hasErrors = true;
|
|
||||||
next(err);
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.error((err) => {
|
|
||||||
logger.error(err.message);
|
|
||||||
})
|
|
||||||
.end(() => {
|
|
||||||
if (hasErrors) {
|
|
||||||
reject(
|
|
||||||
new errs.CommandError("Some plugins failed to install. Please check the logs above", 1),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
resolve();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Installs a cerbot plugin given the key for the object from
|
* Installs a cerbot plugin given the key for the object from
|
||||||
* ../global/certbot-dns-plugins.json
|
* ../global/certbot-dns-plugins.json
|
||||||
@@ -84,4 +44,43 @@ const installPlugin = async (pluginKey) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {array} pluginKeys
|
||||||
|
*/
|
||||||
|
const installPlugins = async (pluginKeys) => {
|
||||||
|
let hasErrors = false;
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
if (pluginKeys.length === 0) {
|
||||||
|
resolve();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
batchflow(pluginKeys)
|
||||||
|
.sequential()
|
||||||
|
.each((_i, pluginKey, next) => {
|
||||||
|
installPlugin(pluginKey)
|
||||||
|
.then(() => {
|
||||||
|
next();
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
hasErrors = true;
|
||||||
|
next(err);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.error((err) => {
|
||||||
|
logger.error(err.message);
|
||||||
|
})
|
||||||
|
.end(() => {
|
||||||
|
if (hasErrors) {
|
||||||
|
reject(
|
||||||
|
new errs.CommandError("Some plugins failed to install. Please check the logs above", 1),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export { installPlugins, installPlugin };
|
export { installPlugins, installPlugin };
|
||||||
|
@@ -98,6 +98,8 @@ router
|
|||||||
name: dnsPlugins[key].name,
|
name: dnsPlugins[key].name,
|
||||||
credentials: dnsPlugins[key].credentials,
|
credentials: dnsPlugins[key].credentials,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
clean.sort((a, b) => a.name.localeCompare(b.name));
|
||||||
res.status(200).send(clean);
|
res.status(200).send(clean);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
|
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
|
||||||
|
@@ -62,15 +62,9 @@
|
|||||||
"dns_provider_credentials": {
|
"dns_provider_credentials": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"letsencrypt_agree": {
|
|
||||||
"type": "boolean"
|
|
||||||
},
|
|
||||||
"letsencrypt_certificate": {
|
"letsencrypt_certificate": {
|
||||||
"type": "object"
|
"type": "object"
|
||||||
},
|
},
|
||||||
"letsencrypt_email": {
|
|
||||||
"$ref": "../common.json#/properties/email"
|
|
||||||
},
|
|
||||||
"propagation_seconds": {
|
"propagation_seconds": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"minimum": 0
|
"minimum": 0
|
||||||
|
@@ -36,8 +36,6 @@
|
|||||||
"domain_names": ["test.example.com"],
|
"domain_names": ["test.example.com"],
|
||||||
"expires_on": "2025-01-07T04:34:18.000Z",
|
"expires_on": "2025-01-07T04:34:18.000Z",
|
||||||
"meta": {
|
"meta": {
|
||||||
"letsencrypt_email": "jc@jc21.com",
|
|
||||||
"letsencrypt_agree": true,
|
|
||||||
"dns_challenge": false
|
"dns_challenge": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -37,8 +37,6 @@
|
|||||||
"nice_name": "My Test Cert",
|
"nice_name": "My Test Cert",
|
||||||
"domain_names": ["test.jc21.supernerd.pro"],
|
"domain_names": ["test.jc21.supernerd.pro"],
|
||||||
"meta": {
|
"meta": {
|
||||||
"letsencrypt_email": "jc@jc21.com",
|
|
||||||
"letsencrypt_agree": true,
|
|
||||||
"dns_challenge": false
|
"dns_challenge": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -36,8 +36,6 @@
|
|||||||
"domain_names": ["test.example.com"],
|
"domain_names": ["test.example.com"],
|
||||||
"expires_on": "2025-01-07T04:34:18.000Z",
|
"expires_on": "2025-01-07T04:34:18.000Z",
|
||||||
"meta": {
|
"meta": {
|
||||||
"letsencrypt_email": "jc@jc21.com",
|
|
||||||
"letsencrypt_agree": true,
|
|
||||||
"dns_challenge": false
|
"dns_challenge": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -52,8 +52,6 @@
|
|||||||
"nice_name": "test.example.com",
|
"nice_name": "test.example.com",
|
||||||
"domain_names": ["test.example.com"],
|
"domain_names": ["test.example.com"],
|
||||||
"meta": {
|
"meta": {
|
||||||
"letsencrypt_email": "jc@jc21.com",
|
|
||||||
"letsencrypt_agree": true,
|
|
||||||
"dns_challenge": false,
|
"dns_challenge": false,
|
||||||
"letsencrypt_certificate": {
|
"letsencrypt_certificate": {
|
||||||
"cn": "test.example.com",
|
"cn": "test.example.com",
|
||||||
|
@@ -121,12 +121,14 @@ const setupCertbotPlugins = async () => {
|
|||||||
// Make sure credentials file exists
|
// Make sure credentials file exists
|
||||||
const credentials_loc = `/etc/letsencrypt/credentials/credentials-${certificate.id}`;
|
const credentials_loc = `/etc/letsencrypt/credentials/credentials-${certificate.id}`;
|
||||||
// Escape single quotes and backslashes
|
// Escape single quotes and backslashes
|
||||||
|
if (typeof certificate.meta.dns_provider_credentials === "string") {
|
||||||
const escapedCredentials = certificate.meta.dns_provider_credentials
|
const escapedCredentials = certificate.meta.dns_provider_credentials
|
||||||
.replaceAll("'", "\\'")
|
.replaceAll("'", "\\'")
|
||||||
.replaceAll("\\", "\\\\");
|
.replaceAll("\\", "\\\\");
|
||||||
const credentials_cmd = `[ -f '${credentials_loc}' ] || { mkdir -p /etc/letsencrypt/credentials 2> /dev/null; echo '${escapedCredentials}' > '${credentials_loc}' && chmod 600 '${credentials_loc}'; }`;
|
const credentials_cmd = `[ -f '${credentials_loc}' ] || { mkdir -p /etc/letsencrypt/credentials 2> /dev/null; echo '${escapedCredentials}' > '${credentials_loc}' && chmod 600 '${credentials_loc}'; }`;
|
||||||
promises.push(utils.exec(credentials_cmd));
|
promises.push(utils.exec(credentials_cmd));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -19,6 +19,7 @@
|
|||||||
"@tabler/icons-react": "^3.35.0",
|
"@tabler/icons-react": "^3.35.0",
|
||||||
"@tanstack/react-query": "^5.89.0",
|
"@tanstack/react-query": "^5.89.0",
|
||||||
"@tanstack/react-table": "^8.21.3",
|
"@tanstack/react-table": "^8.21.3",
|
||||||
|
"@uiw/react-textarea-code-editor": "^3.1.1",
|
||||||
"classnames": "^2.5.1",
|
"classnames": "^2.5.1",
|
||||||
"country-flag-icons": "^1.5.20",
|
"country-flag-icons": "^1.5.20",
|
||||||
"date-fns": "^4.1.0",
|
"date-fns": "^4.1.0",
|
||||||
|
@@ -65,3 +65,8 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.textareaMono {
|
||||||
|
font-family: 'Courier New', Courier, monospace !important;
|
||||||
|
resize: vertical;
|
||||||
|
}
|
||||||
|
@@ -1,16 +1,8 @@
|
|||||||
.dnsChallengeWarning {
|
.dnsChallengeWarning {
|
||||||
border: 1px solid #fecaca; /* Tailwind's red-300 */
|
border: 1px solid var(--tblr-orange-lt);
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
border-radius: 0.375rem; /* Tailwind's rounded-md */
|
border-radius: 0.375rem;
|
||||||
margin-top: 1rem;
|
margin-top: 1rem;
|
||||||
|
background-color: var(--tblr-cyan-lt);
|
||||||
}
|
}
|
||||||
|
|
||||||
.textareaMono {
|
|
||||||
font-family: 'Courier New', Courier, monospace !important;
|
|
||||||
/* background-color: #f9fafb;
|
|
||||||
border: 1px solid #d1d5db;
|
|
||||||
padding: 0.5rem;
|
|
||||||
border-radius: 0.375rem;
|
|
||||||
width: 100%; */
|
|
||||||
resize: vertical;
|
|
||||||
}
|
|
||||||
|
@@ -1,4 +1,3 @@
|
|||||||
import cn from "classnames";
|
|
||||||
import { Field, useFormikContext } from "formik";
|
import { Field, useFormikContext } from "formik";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import Select, { type ActionMeta } from "react-select";
|
import Select, { type ActionMeta } from "react-select";
|
||||||
@@ -20,8 +19,8 @@ export function DNSProviderFields() {
|
|||||||
const v: any = values || {};
|
const v: any = values || {};
|
||||||
|
|
||||||
const handleChange = (newValue: any, _actionMeta: ActionMeta<DNSProviderOption>) => {
|
const handleChange = (newValue: any, _actionMeta: ActionMeta<DNSProviderOption>) => {
|
||||||
setFieldValue("dnsProvider", newValue?.value);
|
setFieldValue("meta.dnsProvider", newValue?.value);
|
||||||
setFieldValue("dnsProviderCredentials", newValue?.credentials);
|
setFieldValue("meta.dnsProviderCredentials", newValue?.credentials);
|
||||||
setDnsProviderId(newValue?.value);
|
setDnsProviderId(newValue?.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -34,12 +33,12 @@ export function DNSProviderFields() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.dnsChallengeWarning}>
|
<div className={styles.dnsChallengeWarning}>
|
||||||
<p className="text-danger">
|
<p className="text-info">
|
||||||
This section requires some knowledge about Certbot and its DNS plugins. Please consult the respective
|
This section requires some knowledge about Certbot and DNS plugins. Please consult the respective
|
||||||
plugins documentation.
|
plugins documentation.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<Field name="dnsProvider">
|
<Field name="meta.dnsProvider">
|
||||||
{({ field }: any) => (
|
{({ field }: any) => (
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<label htmlFor="dnsProvider" className="form-label">
|
<label htmlFor="dnsProvider" className="form-label">
|
||||||
@@ -64,33 +63,37 @@ export function DNSProviderFields() {
|
|||||||
|
|
||||||
{dnsProviderId ? (
|
{dnsProviderId ? (
|
||||||
<>
|
<>
|
||||||
<Field name="dnsProviderCredentials">
|
<Field name="meta.dnsProviderCredentials">
|
||||||
{({ field }: any) => (
|
{({ field }: any) => (
|
||||||
<div className="row mt-3">
|
<div className="mt-3">
|
||||||
<label htmlFor="dnsProviderCredentials" className="form-label">
|
<label htmlFor="dnsProviderCredentials" className="form-label">
|
||||||
Credentials File Content
|
Credentials File Content
|
||||||
</label>
|
</label>
|
||||||
<textarea
|
<textarea
|
||||||
id="dnsProviderCredentials"
|
id="dnsProviderCredentials"
|
||||||
className={cn("form-control", styles.textareaMono)}
|
className="form-control textareaMono"
|
||||||
rows={3}
|
rows={3}
|
||||||
spellCheck={false}
|
spellCheck={false}
|
||||||
value={v.dnsProviderCredentials || ""}
|
value={v.meta.dnsProviderCredentials || ""}
|
||||||
{...field}
|
{...field}
|
||||||
/>
|
/>
|
||||||
|
<div>
|
||||||
<small className="text-muted">
|
<small className="text-muted">
|
||||||
This plugin requires a configuration file containing an API token or other
|
This plugin requires a configuration file containing an API token or other
|
||||||
credentials to your provider
|
credentials to your provider
|
||||||
</small>
|
</small>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
<small className="text-danger">
|
<small className="text-danger">
|
||||||
This data will be stored as plaintext in the database and in a file!
|
This data will be stored as plaintext in the database and in a file!
|
||||||
</small>
|
</small>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</Field>
|
</Field>
|
||||||
<Field name="propagationSeconds">
|
<Field name="meta.propagationSeconds">
|
||||||
{({ field }: any) => (
|
{({ field }: any) => (
|
||||||
<div className="row mt-3">
|
<div className="mt-3">
|
||||||
<label htmlFor="propagationSeconds" className="form-label">
|
<label htmlFor="propagationSeconds" className="form-label">
|
||||||
Propagation Seconds
|
Propagation Seconds
|
||||||
</label>
|
</label>
|
||||||
|
@@ -2,6 +2,7 @@ import { Field, useFormikContext } from "formik";
|
|||||||
import type { ActionMeta, MultiValue } from "react-select";
|
import type { ActionMeta, MultiValue } from "react-select";
|
||||||
import CreatableSelect from "react-select/creatable";
|
import CreatableSelect from "react-select/creatable";
|
||||||
import { intl } from "src/locale";
|
import { intl } from "src/locale";
|
||||||
|
import { validateDomain, validateDomains } from "src/modules/Validations";
|
||||||
|
|
||||||
export type SelectOption = {
|
export type SelectOption = {
|
||||||
label: string;
|
label: string;
|
||||||
@@ -22,17 +23,10 @@ export function DomainNamesField({
|
|||||||
label = "domain-names",
|
label = "domain-names",
|
||||||
id = "domainNames",
|
id = "domainNames",
|
||||||
maxDomains,
|
maxDomains,
|
||||||
isWildcardPermitted,
|
isWildcardPermitted = true,
|
||||||
dnsProviderWildcardSupported,
|
dnsProviderWildcardSupported = true,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const { values, setFieldValue } = useFormikContext();
|
const { setFieldValue } = useFormikContext();
|
||||||
|
|
||||||
const getDomainCount = (v: string[] | undefined): number => {
|
|
||||||
if (v?.length) {
|
|
||||||
return v.length;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleChange = (v: MultiValue<SelectOption>, _actionMeta: ActionMeta<SelectOption>) => {
|
const handleChange = (v: MultiValue<SelectOption>, _actionMeta: ActionMeta<SelectOption>) => {
|
||||||
const doms = v?.map((i: SelectOption) => {
|
const doms = v?.map((i: SelectOption) => {
|
||||||
@@ -41,50 +35,18 @@ export function DomainNamesField({
|
|||||||
setFieldValue(name, doms);
|
setFieldValue(name, doms);
|
||||||
};
|
};
|
||||||
|
|
||||||
const isDomainValid = (d: string): boolean => {
|
|
||||||
const dom = d.trim().toLowerCase();
|
|
||||||
const v: any = values;
|
|
||||||
|
|
||||||
// Deny if the list of domains is hit
|
|
||||||
if (maxDomains && getDomainCount(v?.[name]) >= maxDomains) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dom.length < 3) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prevent wildcards
|
|
||||||
if ((!isWildcardPermitted || !dnsProviderWildcardSupported) && dom.indexOf("*") !== -1) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prevent duplicate * in domain
|
|
||||||
if ((dom.match(/\*/g) || []).length > 1) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prevent some invalid characters
|
|
||||||
if ((dom.match(/(@|,|!|&|\$|#|%|\^|\(|\))/g) || []).length > 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This will match *.com type domains,
|
|
||||||
return dom.match(/\*\.[^.]+$/m) === null;
|
|
||||||
};
|
|
||||||
|
|
||||||
const helperTexts: string[] = [];
|
const helperTexts: string[] = [];
|
||||||
if (maxDomains) {
|
if (maxDomains) {
|
||||||
helperTexts.push(intl.formatMessage({ id: "domain_names.max" }, { count: maxDomains }));
|
helperTexts.push(intl.formatMessage({ id: "domain-names.max" }, { count: maxDomains }));
|
||||||
}
|
}
|
||||||
if (!isWildcardPermitted) {
|
if (!isWildcardPermitted) {
|
||||||
helperTexts.push(intl.formatMessage({ id: "wildcards-not-permitted" }));
|
helperTexts.push(intl.formatMessage({ id: "domain-names.wildcards-not-permitted" }));
|
||||||
} else if (!dnsProviderWildcardSupported) {
|
} else if (!dnsProviderWildcardSupported) {
|
||||||
helperTexts.push(intl.formatMessage({ id: "wildcards-not-supported" }));
|
helperTexts.push(intl.formatMessage({ id: "domain-names.wildcards-not-supported" }));
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Field name={name}>
|
<Field name={name} validate={validateDomains(isWildcardPermitted && dnsProviderWildcardSupported, maxDomains)}>
|
||||||
{({ field, form }: any) => (
|
{({ field, form }: any) => (
|
||||||
<div className="mb-3">
|
<div className="mb-3">
|
||||||
<label className="form-label" htmlFor={id}>
|
<label className="form-label" htmlFor={id}>
|
||||||
@@ -97,21 +59,19 @@ export function DomainNamesField({
|
|||||||
id={id}
|
id={id}
|
||||||
closeMenuOnSelect={true}
|
closeMenuOnSelect={true}
|
||||||
isClearable={false}
|
isClearable={false}
|
||||||
isValidNewOption={isDomainValid}
|
isValidNewOption={validateDomain(isWildcardPermitted && dnsProviderWildcardSupported)}
|
||||||
isMulti
|
isMulti
|
||||||
placeholder="Start typing to add domain..."
|
placeholder={intl.formatMessage({ id: "domain-names.placeholder" })}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
value={field.value?.map((d: string) => ({ label: d, value: d }))}
|
value={field.value?.map((d: string) => ({ label: d, value: d }))}
|
||||||
/>
|
/>
|
||||||
{form.errors[field.name] ? (
|
{form.errors[field.name] && form.touched[field.name] ? (
|
||||||
<div className="invalid-feedback">
|
<small className="text-danger">{form.errors[field.name]}</small>
|
||||||
{form.errors[field.name] && form.touched[field.name] ? form.errors[field.name] : null}
|
|
||||||
</div>
|
|
||||||
) : helperTexts.length ? (
|
) : helperTexts.length ? (
|
||||||
helperTexts.map((i) => (
|
helperTexts.map((i) => (
|
||||||
<div key={i} className="invalid-feedback text-info">
|
<small key={i} className="text-info">
|
||||||
{i}
|
{i}
|
||||||
</div>
|
</small>
|
||||||
))
|
))
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
|
40
frontend/src/components/Form/NginxConfigField.tsx
Normal file
40
frontend/src/components/Form/NginxConfigField.tsx
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import CodeEditor from "@uiw/react-textarea-code-editor";
|
||||||
|
import { Field } from "formik";
|
||||||
|
import { intl } from "src/locale";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
id?: string;
|
||||||
|
name?: string;
|
||||||
|
label?: string;
|
||||||
|
}
|
||||||
|
export function NginxConfigField({
|
||||||
|
name = "advancedConfig",
|
||||||
|
label = "nginx-config.label",
|
||||||
|
id = "advancedConfig",
|
||||||
|
}: Props) {
|
||||||
|
return (
|
||||||
|
<Field name={name}>
|
||||||
|
{({ field }: any) => (
|
||||||
|
<div className="mt-3">
|
||||||
|
<label htmlFor={id} className="form-label">
|
||||||
|
{intl.formatMessage({ id: label })}
|
||||||
|
</label>
|
||||||
|
<CodeEditor
|
||||||
|
language="nginx"
|
||||||
|
placeholder={intl.formatMessage({ id: "nginx-config.placeholder" })}
|
||||||
|
padding={15}
|
||||||
|
data-color-mode="dark"
|
||||||
|
minHeight={200}
|
||||||
|
indentWidth={2}
|
||||||
|
style={{
|
||||||
|
fontFamily: "ui-monospace,SFMono-Regular,SF Mono,Consolas,Liberation Mono,Menlo,monospace",
|
||||||
|
borderRadius: "0.3rem",
|
||||||
|
minHeight: "200px",
|
||||||
|
}}
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Field>
|
||||||
|
);
|
||||||
|
}
|
@@ -2,7 +2,7 @@ import { IconShield } from "@tabler/icons-react";
|
|||||||
import { Field, useFormikContext } from "formik";
|
import { Field, useFormikContext } from "formik";
|
||||||
import Select, { type ActionMeta, components, type OptionProps } from "react-select";
|
import Select, { type ActionMeta, components, type OptionProps } from "react-select";
|
||||||
import type { Certificate } from "src/api/backend";
|
import type { Certificate } from "src/api/backend";
|
||||||
import { useCertificates, useUser } from "src/hooks";
|
import { useCertificates } from "src/hooks";
|
||||||
import { DateTimeFormat, intl } from "src/locale";
|
import { DateTimeFormat, intl } from "src/locale";
|
||||||
|
|
||||||
interface CertOption {
|
interface CertOption {
|
||||||
@@ -39,26 +39,33 @@ export function SSLCertificateField({
|
|||||||
required,
|
required,
|
||||||
allowNew,
|
allowNew,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const { data: currentUser } = useUser("me");
|
|
||||||
const { isLoading, isError, error, data } = useCertificates();
|
const { isLoading, isError, error, data } = useCertificates();
|
||||||
const { values, setFieldValue } = useFormikContext();
|
const { values, setFieldValue } = useFormikContext();
|
||||||
const v: any = values || {};
|
const v: any = values || {};
|
||||||
|
|
||||||
const handleChange = (newValue: any, _actionMeta: ActionMeta<CertOption>) => {
|
const handleChange = (newValue: any, _actionMeta: ActionMeta<CertOption>) => {
|
||||||
setFieldValue(name, newValue?.value);
|
setFieldValue(name, newValue?.value);
|
||||||
const { sslForced, http2Support, hstsEnabled, hstsSubdomains, dnsChallenge, letsencryptEmail } = v;
|
const {
|
||||||
|
sslForced,
|
||||||
|
http2Support,
|
||||||
|
hstsEnabled,
|
||||||
|
hstsSubdomains,
|
||||||
|
dnsChallenge,
|
||||||
|
dnsProvider,
|
||||||
|
dnsProviderCredentials,
|
||||||
|
propagationSeconds,
|
||||||
|
} = v;
|
||||||
if (!newValue?.value) {
|
if (!newValue?.value) {
|
||||||
sslForced && setFieldValue("sslForced", false);
|
sslForced && setFieldValue("sslForced", false);
|
||||||
http2Support && setFieldValue("http2Support", false);
|
http2Support && setFieldValue("http2Support", false);
|
||||||
hstsEnabled && setFieldValue("hstsEnabled", false);
|
hstsEnabled && setFieldValue("hstsEnabled", false);
|
||||||
hstsSubdomains && setFieldValue("hstsSubdomains", false);
|
hstsSubdomains && setFieldValue("hstsSubdomains", false);
|
||||||
}
|
}
|
||||||
if (newValue?.value === "new") {
|
if (newValue?.value !== "new") {
|
||||||
if (!letsencryptEmail) {
|
dnsChallenge && setFieldValue("dnsChallenge", undefined);
|
||||||
setFieldValue("letsencryptEmail", currentUser?.email);
|
dnsProvider && setFieldValue("dnsProvider", undefined);
|
||||||
}
|
dnsProviderCredentials && setFieldValue("dnsProviderCredentials", undefined);
|
||||||
} else {
|
propagationSeconds && setFieldValue("propagationSeconds", undefined);
|
||||||
dnsChallenge && setFieldValue("dnsChallenge", false);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -105,7 +112,7 @@ export function SSLCertificateField({
|
|||||||
<Select
|
<Select
|
||||||
className="react-select-container"
|
className="react-select-container"
|
||||||
classNamePrefix="react-select"
|
classNamePrefix="react-select"
|
||||||
defaultValue={options[0]}
|
defaultValue={options.find((o) => o.value === field.value) || options[0]}
|
||||||
options={options}
|
options={options}
|
||||||
components={{ Option }}
|
components={{ Option }}
|
||||||
styles={{
|
styles={{
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
import cn from "classnames";
|
import cn from "classnames";
|
||||||
import { Field, useFormikContext } from "formik";
|
import { Field, useFormikContext } from "formik";
|
||||||
import { DNSProviderFields } from "src/components";
|
import { DNSProviderFields } from "src/components";
|
||||||
|
import { intl } from "src/locale";
|
||||||
|
|
||||||
export function SSLOptionsFields() {
|
export function SSLOptionsFields() {
|
||||||
const { values, setFieldValue } = useFormikContext();
|
const { values, setFieldValue } = useFormikContext();
|
||||||
@@ -8,10 +9,16 @@ export function SSLOptionsFields() {
|
|||||||
|
|
||||||
const newCertificate = v?.certificateId === "new";
|
const newCertificate = v?.certificateId === "new";
|
||||||
const hasCertificate = newCertificate || (v?.certificateId && v?.certificateId > 0);
|
const hasCertificate = newCertificate || (v?.certificateId && v?.certificateId > 0);
|
||||||
const { sslForced, http2Support, hstsEnabled, hstsSubdomains, dnsChallenge } = v;
|
const { sslForced, http2Support, hstsEnabled, hstsSubdomains, meta } = v;
|
||||||
|
const { dnsChallenge } = meta || {};
|
||||||
|
|
||||||
const handleToggleChange = (e: any, fieldName: string) => {
|
const handleToggleChange = (e: any, fieldName: string) => {
|
||||||
setFieldValue(fieldName, e.target.checked);
|
setFieldValue(fieldName, e.target.checked);
|
||||||
|
if (fieldName === "meta.dnsChallenge" && !e.target.checked) {
|
||||||
|
setFieldValue("meta.dnsProvider", undefined);
|
||||||
|
setFieldValue("meta.dnsProviderCredentials", undefined);
|
||||||
|
setFieldValue("meta.propagationSeconds", undefined);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const toggleClasses = "form-check-input";
|
const toggleClasses = "form-check-input";
|
||||||
@@ -31,7 +38,9 @@ export function SSLOptionsFields() {
|
|||||||
onChange={(e) => handleToggleChange(e, field.name)}
|
onChange={(e) => handleToggleChange(e, field.name)}
|
||||||
disabled={!hasCertificate}
|
disabled={!hasCertificate}
|
||||||
/>
|
/>
|
||||||
<span className="form-check-label">Force SSL</span>
|
<span className="form-check-label">
|
||||||
|
{intl.formatMessage({ id: "domains.force-ssl" })}
|
||||||
|
</span>
|
||||||
</label>
|
</label>
|
||||||
)}
|
)}
|
||||||
</Field>
|
</Field>
|
||||||
@@ -47,7 +56,9 @@ export function SSLOptionsFields() {
|
|||||||
onChange={(e) => handleToggleChange(e, field.name)}
|
onChange={(e) => handleToggleChange(e, field.name)}
|
||||||
disabled={!hasCertificate}
|
disabled={!hasCertificate}
|
||||||
/>
|
/>
|
||||||
<span className="form-check-label">HTTP/2 Support</span>
|
<span className="form-check-label">
|
||||||
|
{intl.formatMessage({ id: "domains.http2-support" })}
|
||||||
|
</span>
|
||||||
</label>
|
</label>
|
||||||
)}
|
)}
|
||||||
</Field>
|
</Field>
|
||||||
@@ -65,7 +76,9 @@ export function SSLOptionsFields() {
|
|||||||
onChange={(e) => handleToggleChange(e, field.name)}
|
onChange={(e) => handleToggleChange(e, field.name)}
|
||||||
disabled={!hasCertificate || !sslForced}
|
disabled={!hasCertificate || !sslForced}
|
||||||
/>
|
/>
|
||||||
<span className="form-check-label">HSTS Enabled</span>
|
<span className="form-check-label">
|
||||||
|
{intl.formatMessage({ id: "domains.hsts-enabled" })}
|
||||||
|
</span>
|
||||||
</label>
|
</label>
|
||||||
)}
|
)}
|
||||||
</Field>
|
</Field>
|
||||||
@@ -81,7 +94,9 @@ export function SSLOptionsFields() {
|
|||||||
onChange={(e) => handleToggleChange(e, field.name)}
|
onChange={(e) => handleToggleChange(e, field.name)}
|
||||||
disabled={!hasCertificate || !hstsEnabled}
|
disabled={!hasCertificate || !hstsEnabled}
|
||||||
/>
|
/>
|
||||||
<span className="form-check-label">HSTS Enabled</span>
|
<span className="form-check-label">
|
||||||
|
{intl.formatMessage({ id: "domains.hsts-subdomains" })}
|
||||||
|
</span>
|
||||||
</label>
|
</label>
|
||||||
)}
|
)}
|
||||||
</Field>
|
</Field>
|
||||||
@@ -89,7 +104,7 @@ export function SSLOptionsFields() {
|
|||||||
</div>
|
</div>
|
||||||
{newCertificate ? (
|
{newCertificate ? (
|
||||||
<>
|
<>
|
||||||
<Field name="dnsChallenge">
|
<Field name="meta.dnsChallenge">
|
||||||
{({ field }: any) => (
|
{({ field }: any) => (
|
||||||
<label className="form-check form-switch mt-1">
|
<label className="form-check form-switch mt-1">
|
||||||
<input
|
<input
|
||||||
@@ -98,29 +113,14 @@ export function SSLOptionsFields() {
|
|||||||
checked={!!dnsChallenge}
|
checked={!!dnsChallenge}
|
||||||
onChange={(e) => handleToggleChange(e, field.name)}
|
onChange={(e) => handleToggleChange(e, field.name)}
|
||||||
/>
|
/>
|
||||||
<span className="form-check-label">Use a DNS Challenge</span>
|
<span className="form-check-label">
|
||||||
|
{intl.formatMessage({ id: "domains.use-dns" })}
|
||||||
|
</span>
|
||||||
</label>
|
</label>
|
||||||
)}
|
)}
|
||||||
</Field>
|
</Field>
|
||||||
|
|
||||||
{dnsChallenge ? <DNSProviderFields /> : null}
|
{dnsChallenge ? <DNSProviderFields /> : null}
|
||||||
|
|
||||||
<Field name="letsencryptEmail">
|
|
||||||
{({ field }: any) => (
|
|
||||||
<div className="mt-5">
|
|
||||||
<label htmlFor="letsencryptEmail" className="form-label">
|
|
||||||
Email Address for Let's Encrypt
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
id="letsencryptEmail"
|
|
||||||
type="email"
|
|
||||||
className="form-control"
|
|
||||||
required
|
|
||||||
{...field}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</Field>
|
|
||||||
</>
|
</>
|
||||||
) : null}
|
) : null}
|
||||||
</>
|
</>
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
export * from "./DNSProviderFields";
|
export * from "./DNSProviderFields";
|
||||||
export * from "./DomainNamesField";
|
export * from "./DomainNamesField";
|
||||||
|
export * from "./NginxConfigField";
|
||||||
export * from "./SSLCertificateField";
|
export * from "./SSLCertificateField";
|
||||||
export * from "./SSLOptionsFields";
|
export * from "./SSLOptionsFields";
|
||||||
|
@@ -50,10 +50,23 @@
|
|||||||
"dead-hosts.title": "404 Hosts",
|
"dead-hosts.title": "404 Hosts",
|
||||||
"disabled": "Disabled",
|
"disabled": "Disabled",
|
||||||
"domain-names": "Domain Names",
|
"domain-names": "Domain Names",
|
||||||
|
"domain-names.max": "{count} domain names maximum",
|
||||||
|
"domain-names.placeholder": "Start typing to add domain...",
|
||||||
|
"domain-names.wildcards-not-permitted": "Wildcards not permitted for this type",
|
||||||
|
"domain-names.wildcards-not-supported": "Wildcards not supported for this CA",
|
||||||
|
"domains.force-ssl": "Force SSL",
|
||||||
|
"domains.hsts-enabled": "HSTS Enabled",
|
||||||
|
"domains.hsts-subdomains": "HSTS Sub-domains",
|
||||||
|
"domains.http2-support": "HTTP/2 Support",
|
||||||
|
"domains.use-dns": "Use DNS Challenge",
|
||||||
"email-address": "Email address",
|
"email-address": "Email address",
|
||||||
"empty-subtitle": "Why don't you create one?",
|
"empty-subtitle": "Why don't you create one?",
|
||||||
"error.invalid-auth": "Invalid email or password",
|
"error.invalid-auth": "Invalid email or password",
|
||||||
|
"error.invalid-domain": "Invalid domain: {domain}",
|
||||||
|
"error.invalid-email": "Invalid email address",
|
||||||
|
"error.max-domains": "Too many domains, max is {max}",
|
||||||
"error.passwords-must-match": "Passwords must match",
|
"error.passwords-must-match": "Passwords must match",
|
||||||
|
"error.required": "This is required",
|
||||||
"event.created-user": "Created User",
|
"event.created-user": "Created User",
|
||||||
"event.deleted-user": "Deleted User",
|
"event.deleted-user": "Deleted User",
|
||||||
"event.updated-user": "Updated User",
|
"event.updated-user": "Updated User",
|
||||||
@@ -63,10 +76,13 @@
|
|||||||
"lets-encrypt": "Let's Encrypt",
|
"lets-encrypt": "Let's Encrypt",
|
||||||
"loading": "Loading…",
|
"loading": "Loading…",
|
||||||
"login.title": "Login to your account",
|
"login.title": "Login to your account",
|
||||||
|
"nginx-config.label": "Custom Nginx Configuration",
|
||||||
|
"nginx-config.placeholder": "# Enter your custom Nginx configuration here at your own risk!",
|
||||||
"no-permission-error": "You do not have access to view this.",
|
"no-permission-error": "You do not have access to view this.",
|
||||||
"notfound.action": "Take me home",
|
"notfound.action": "Take me home",
|
||||||
"notfound.text": "We are sorry but the page you are looking for was not found",
|
"notfound.text": "We are sorry but the page you are looking for was not found",
|
||||||
"notfound.title": "Oops… You just found an error page",
|
"notfound.title": "Oops… You just found an error page",
|
||||||
|
"notification.dead-host-saved": "404 Host has been saved",
|
||||||
"notification.error": "Error",
|
"notification.error": "Error",
|
||||||
"notification.success": "Success",
|
"notification.success": "Success",
|
||||||
"notification.user-deleted": "User has been deleted",
|
"notification.user-deleted": "User has been deleted",
|
||||||
@@ -127,7 +143,5 @@
|
|||||||
"user.switch-light": "Switch to Light mode",
|
"user.switch-light": "Switch to Light mode",
|
||||||
"users.actions-title": "User #{id}",
|
"users.actions-title": "User #{id}",
|
||||||
"users.add": "Add User",
|
"users.add": "Add User",
|
||||||
"users.title": "Users",
|
"users.title": "Users"
|
||||||
"wildcards-not-permitted": "Wildcards not permitted for this type",
|
|
||||||
"wildcards-not-supported": "Wildcards not supported for this CA"
|
|
||||||
}
|
}
|
@@ -152,6 +152,33 @@
|
|||||||
"domain-names": {
|
"domain-names": {
|
||||||
"defaultMessage": "Domain Names"
|
"defaultMessage": "Domain Names"
|
||||||
},
|
},
|
||||||
|
"domain-names.max": {
|
||||||
|
"defaultMessage": "{count} domain names maximum"
|
||||||
|
},
|
||||||
|
"domain-names.placeholder": {
|
||||||
|
"defaultMessage": "Start typing to add domain..."
|
||||||
|
},
|
||||||
|
"domain-names.wildcards-not-permitted": {
|
||||||
|
"defaultMessage": "Wildcards not permitted for this type"
|
||||||
|
},
|
||||||
|
"domain-names.wildcards-not-supported": {
|
||||||
|
"defaultMessage": "Wildcards not supported for this CA"
|
||||||
|
},
|
||||||
|
"domains.force-ssl": {
|
||||||
|
"defaultMessage": "Force SSL"
|
||||||
|
},
|
||||||
|
"domains.hsts-enabled": {
|
||||||
|
"defaultMessage": "HSTS Enabled"
|
||||||
|
},
|
||||||
|
"domains.hsts-subdomains": {
|
||||||
|
"defaultMessage": "HSTS Sub-domains"
|
||||||
|
},
|
||||||
|
"domains.http2-support": {
|
||||||
|
"defaultMessage": "HTTP/2 Support"
|
||||||
|
},
|
||||||
|
"domains.use-dns": {
|
||||||
|
"defaultMessage": "Use DNS Challenge"
|
||||||
|
},
|
||||||
"email-address": {
|
"email-address": {
|
||||||
"defaultMessage": "Email address"
|
"defaultMessage": "Email address"
|
||||||
},
|
},
|
||||||
@@ -161,6 +188,18 @@
|
|||||||
"error.invalid-auth": {
|
"error.invalid-auth": {
|
||||||
"defaultMessage": "Invalid email or password"
|
"defaultMessage": "Invalid email or password"
|
||||||
},
|
},
|
||||||
|
"error.invalid-domain": {
|
||||||
|
"defaultMessage": "Invalid domain: {domain}"
|
||||||
|
},
|
||||||
|
"error.invalid-email": {
|
||||||
|
"defaultMessage": "Invalid email address"
|
||||||
|
},
|
||||||
|
"error.max-domains": {
|
||||||
|
"defaultMessage": "Too many domains, max is {max}"
|
||||||
|
},
|
||||||
|
"error.required": {
|
||||||
|
"defaultMessage": "This is required"
|
||||||
|
},
|
||||||
"event.created-user": {
|
"event.created-user": {
|
||||||
"defaultMessage": "Created User"
|
"defaultMessage": "Created User"
|
||||||
},
|
},
|
||||||
@@ -191,6 +230,12 @@
|
|||||||
"login.title": {
|
"login.title": {
|
||||||
"defaultMessage": "Login to your account"
|
"defaultMessage": "Login to your account"
|
||||||
},
|
},
|
||||||
|
"nginx-config.label": {
|
||||||
|
"defaultMessage": "Custom Nginx Configuration"
|
||||||
|
},
|
||||||
|
"nginx-config.placeholder": {
|
||||||
|
"defaultMessage": "# Enter your custom Nginx configuration here at your own risk!"
|
||||||
|
},
|
||||||
"no-permission-error": {
|
"no-permission-error": {
|
||||||
"defaultMessage": "You do not have access to view this."
|
"defaultMessage": "You do not have access to view this."
|
||||||
},
|
},
|
||||||
@@ -203,6 +248,9 @@
|
|||||||
"notfound.title": {
|
"notfound.title": {
|
||||||
"defaultMessage": "Oops… You just found an error page"
|
"defaultMessage": "Oops… You just found an error page"
|
||||||
},
|
},
|
||||||
|
"notification.dead-host-saved": {
|
||||||
|
"defaultMessage": "404 Host has been saved"
|
||||||
|
},
|
||||||
"notification.error": {
|
"notification.error": {
|
||||||
"defaultMessage": "Error"
|
"defaultMessage": "Error"
|
||||||
},
|
},
|
||||||
@@ -385,11 +433,5 @@
|
|||||||
},
|
},
|
||||||
"users.title": {
|
"users.title": {
|
||||||
"defaultMessage": "Users"
|
"defaultMessage": "Users"
|
||||||
},
|
|
||||||
"wildcards-not-permitted": {
|
|
||||||
"defaultMessage": "Wildcards not permitted for this type"
|
|
||||||
},
|
|
||||||
"wildcards-not-supported": {
|
|
||||||
"defaultMessage": "Wildcards not supported for this CA"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -13,6 +13,7 @@ interface Props {
|
|||||||
}
|
}
|
||||||
export function ChangePasswordModal({ userId, onClose }: Props) {
|
export function ChangePasswordModal({ userId, onClose }: Props) {
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||||
|
|
||||||
const onSubmit = async (values: any, { setSubmitting }: any) => {
|
const onSubmit = async (values: any, { setSubmitting }: any) => {
|
||||||
if (values.new !== values.confirm) {
|
if (values.new !== values.confirm) {
|
||||||
@@ -20,13 +21,18 @@ export function ChangePasswordModal({ userId, onClose }: Props) {
|
|||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isSubmitting) return;
|
||||||
|
setIsSubmitting(true);
|
||||||
setError(null);
|
setError(null);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await updateAuth(userId, values.new, values.current);
|
await updateAuth(userId, values.new, values.current);
|
||||||
onClose();
|
onClose();
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
setError(intl.formatMessage({ id: err.message }));
|
setError(intl.formatMessage({ id: err.message }));
|
||||||
}
|
}
|
||||||
|
setIsSubmitting(false);
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -42,7 +48,7 @@ export function ChangePasswordModal({ userId, onClose }: Props) {
|
|||||||
}
|
}
|
||||||
onSubmit={onSubmit}
|
onSubmit={onSubmit}
|
||||||
>
|
>
|
||||||
{({ isSubmitting }) => (
|
{() => (
|
||||||
<Form>
|
<Form>
|
||||||
<Modal.Header closeButton>
|
<Modal.Header closeButton>
|
||||||
<Modal.Title>{intl.formatMessage({ id: "user.change-password" })}</Modal.Title>
|
<Modal.Title>{intl.formatMessage({ id: "user.change-password" })}</Modal.Title>
|
||||||
|
@@ -3,9 +3,17 @@ import { Form, Formik } from "formik";
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { Alert } from "react-bootstrap";
|
import { Alert } from "react-bootstrap";
|
||||||
import Modal from "react-bootstrap/Modal";
|
import Modal from "react-bootstrap/Modal";
|
||||||
import { Button, DomainNamesField, Loading, SSLCertificateField, SSLOptionsFields } from "src/components";
|
import {
|
||||||
import { useDeadHost } from "src/hooks";
|
Button,
|
||||||
|
DomainNamesField,
|
||||||
|
Loading,
|
||||||
|
NginxConfigField,
|
||||||
|
SSLCertificateField,
|
||||||
|
SSLOptionsFields,
|
||||||
|
} from "src/components";
|
||||||
|
import { useDeadHost, useSetDeadHost } from "src/hooks";
|
||||||
import { intl } from "src/locale";
|
import { intl } from "src/locale";
|
||||||
|
import { showSuccess } from "src/notifications";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
id: number | "new";
|
id: number | "new";
|
||||||
@@ -13,28 +21,31 @@ interface Props {
|
|||||||
}
|
}
|
||||||
export function DeadHostModal({ id, onClose }: Props) {
|
export function DeadHostModal({ id, onClose }: Props) {
|
||||||
const { data, isLoading, error } = useDeadHost(id);
|
const { data, isLoading, error } = useDeadHost(id);
|
||||||
// const { mutate: setDeadHost } = useSetDeadHost();
|
const { mutate: setDeadHost } = useSetDeadHost();
|
||||||
const [errorMsg, setErrorMsg] = useState<string | null>(null);
|
const [errorMsg, setErrorMsg] = useState<string | null>(null);
|
||||||
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||||
|
|
||||||
const onSubmit = async (values: any, { setSubmitting }: any) => {
|
const onSubmit = async (values: any, { setSubmitting }: any) => {
|
||||||
setSubmitting(true);
|
if (isSubmitting) return;
|
||||||
|
setIsSubmitting(true);
|
||||||
setErrorMsg(null);
|
setErrorMsg(null);
|
||||||
console.log("SUBMIT:", values);
|
|
||||||
setSubmitting(false);
|
|
||||||
// const { ...payload } = {
|
|
||||||
// id: id === "new" ? undefined : id,
|
|
||||||
// roles: [],
|
|
||||||
// ...values,
|
|
||||||
// };
|
|
||||||
|
|
||||||
// setDeadHost(payload, {
|
const { ...payload } = {
|
||||||
// onError: (err: any) => setErrorMsg(err.message),
|
id: id === "new" ? undefined : id,
|
||||||
// onSuccess: () => {
|
...values,
|
||||||
// showSuccess(intl.formatMessage({ id: "notification.dead-host-saved" }));
|
};
|
||||||
// onClose();
|
|
||||||
// },
|
setDeadHost(payload, {
|
||||||
// onSettled: () => setSubmitting(false),
|
onError: (err: any) => setErrorMsg(err.message),
|
||||||
// });
|
onSuccess: () => {
|
||||||
|
showSuccess(intl.formatMessage({ id: "notification.dead-host-saved" }));
|
||||||
|
onClose();
|
||||||
|
},
|
||||||
|
onSettled: () => {
|
||||||
|
setIsSubmitting(false);
|
||||||
|
setSubmitting(false);
|
||||||
|
},
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -56,11 +67,12 @@ export function DeadHostModal({ id, onClose }: Props) {
|
|||||||
http2Support: data?.http2Support,
|
http2Support: data?.http2Support,
|
||||||
hstsEnabled: data?.hstsEnabled,
|
hstsEnabled: data?.hstsEnabled,
|
||||||
hstsSubdomains: data?.hstsSubdomains,
|
hstsSubdomains: data?.hstsSubdomains,
|
||||||
|
meta: data?.meta || {},
|
||||||
} as any
|
} as any
|
||||||
}
|
}
|
||||||
onSubmit={onSubmit}
|
onSubmit={onSubmit}
|
||||||
>
|
>
|
||||||
{({ isSubmitting }) => (
|
{() => (
|
||||||
<Form>
|
<Form>
|
||||||
<Modal.Header closeButton>
|
<Modal.Header closeButton>
|
||||||
<Modal.Title>
|
<Modal.Title>
|
||||||
@@ -127,140 +139,11 @@ export function DeadHostModal({ id, onClose }: Props) {
|
|||||||
<SSLOptionsFields />
|
<SSLOptionsFields />
|
||||||
</div>
|
</div>
|
||||||
<div className="tab-pane" id="tab-advanced" role="tabpanel">
|
<div className="tab-pane" id="tab-advanced" role="tabpanel">
|
||||||
<h4>Advanced</h4>
|
<NginxConfigField />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* <div className="row">
|
|
||||||
<div className="col-lg-6">
|
|
||||||
<div className="mb-3">
|
|
||||||
<Field name="name" validate={validateString(1, 50)}>
|
|
||||||
{({ field, form }: any) => (
|
|
||||||
<div className="form-floating mb-3">
|
|
||||||
<input
|
|
||||||
id="name"
|
|
||||||
className={`form-control ${form.errors.name && form.touched.name ? "is-invalid" : ""}`}
|
|
||||||
placeholder={intl.formatMessage({ id: "user.full-name" })}
|
|
||||||
{...field}
|
|
||||||
/>
|
|
||||||
<label htmlFor="name">
|
|
||||||
{intl.formatMessage({ id: "user.full-name" })}
|
|
||||||
</label>
|
|
||||||
{form.errors.name ? (
|
|
||||||
<div className="invalid-feedback">
|
|
||||||
{form.errors.name && form.touched.name
|
|
||||||
? form.errors.name
|
|
||||||
: null}
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</Field>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="col-lg-6">
|
|
||||||
<div className="mb-3">
|
|
||||||
<Field name="nickname" validate={validateString(1, 30)}>
|
|
||||||
{({ field, form }: any) => (
|
|
||||||
<div className="form-floating mb-3">
|
|
||||||
<input
|
|
||||||
id="nickname"
|
|
||||||
className={`form-control ${form.errors.nickname && form.touched.nickname ? "is-invalid" : ""}`}
|
|
||||||
placeholder={intl.formatMessage({ id: "user.nickname" })}
|
|
||||||
{...field}
|
|
||||||
/>
|
|
||||||
<label htmlFor="nickname">
|
|
||||||
{intl.formatMessage({ id: "user.nickname" })}
|
|
||||||
</label>
|
|
||||||
{form.errors.nickname ? (
|
|
||||||
<div className="invalid-feedback">
|
|
||||||
{form.errors.nickname && form.touched.nickname
|
|
||||||
? form.errors.nickname
|
|
||||||
: null}
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</Field>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="mb-3">
|
|
||||||
<Field name="email" validate={validateEmail()}>
|
|
||||||
{({ field, form }: any) => (
|
|
||||||
<div className="form-floating mb-3">
|
|
||||||
<input
|
|
||||||
id="email"
|
|
||||||
type="email"
|
|
||||||
className={`form-control ${form.errors.email && form.touched.email ? "is-invalid" : ""}`}
|
|
||||||
placeholder={intl.formatMessage({ id: "email-address" })}
|
|
||||||
{...field}
|
|
||||||
/>
|
|
||||||
<label htmlFor="email">
|
|
||||||
{intl.formatMessage({ id: "email-address" })}
|
|
||||||
</label>
|
|
||||||
{form.errors.email ? (
|
|
||||||
<div className="invalid-feedback">
|
|
||||||
{form.errors.email && form.touched.email
|
|
||||||
? form.errors.email
|
|
||||||
: null}
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</Field>
|
|
||||||
</div>
|
|
||||||
{currentUser && data && currentUser?.id !== data?.id ? (
|
|
||||||
<div className="my-3">
|
|
||||||
<h3 className="py-2">{intl.formatMessage({ id: "user.flags.title" })}</h3>
|
|
||||||
<div className="divide-y">
|
|
||||||
<div>
|
|
||||||
<label className="row" htmlFor="isAdmin">
|
|
||||||
<span className="col">
|
|
||||||
{intl.formatMessage({ id: "role.admin" })}
|
|
||||||
</span>
|
|
||||||
<span className="col-auto">
|
|
||||||
<Field name="isAdmin" type="checkbox">
|
|
||||||
{({ field }: any) => (
|
|
||||||
<label className="form-check form-check-single form-switch">
|
|
||||||
<input
|
|
||||||
{...field}
|
|
||||||
id="isAdmin"
|
|
||||||
className="form-check-input"
|
|
||||||
type="checkbox"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
)}
|
|
||||||
</Field>
|
|
||||||
</span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label className="row" htmlFor="isDisabled">
|
|
||||||
<span className="col">
|
|
||||||
{intl.formatMessage({ id: "disabled" })}
|
|
||||||
</span>
|
|
||||||
<span className="col-auto">
|
|
||||||
<Field name="isDisabled" type="checkbox">
|
|
||||||
{({ field }: any) => (
|
|
||||||
<label className="form-check form-check-single form-switch">
|
|
||||||
<input
|
|
||||||
{...field}
|
|
||||||
id="isDisabled"
|
|
||||||
className="form-check-input"
|
|
||||||
type="checkbox"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
)}
|
|
||||||
</Field>
|
|
||||||
</span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : null} */}
|
|
||||||
</Modal.Body>
|
</Modal.Body>
|
||||||
<Modal.Footer>
|
<Modal.Footer>
|
||||||
<Button data-bs-dismiss="modal" onClick={onClose} disabled={isSubmitting}>
|
<Button data-bs-dismiss="modal" onClick={onClose} disabled={isSubmitting}>
|
||||||
|
@@ -15,10 +15,11 @@ interface Props {
|
|||||||
export function DeleteConfirmModal({ title, children, onConfirm, onClose, invalidations }: Props) {
|
export function DeleteConfirmModal({ title, children, onConfirm, onClose, invalidations }: Props) {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
const [submitting, setSubmitting] = useState(false);
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||||
|
|
||||||
const onSubmit = async () => {
|
const onSubmit = async () => {
|
||||||
setSubmitting(true);
|
if (isSubmitting) return;
|
||||||
|
setIsSubmitting(true);
|
||||||
setError(null);
|
setError(null);
|
||||||
try {
|
try {
|
||||||
await onConfirm();
|
await onConfirm();
|
||||||
@@ -30,7 +31,7 @@ export function DeleteConfirmModal({ title, children, onConfirm, onClose, invali
|
|||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
setError(intl.formatMessage({ id: err.message }));
|
setError(intl.formatMessage({ id: err.message }));
|
||||||
}
|
}
|
||||||
setSubmitting(false);
|
setIsSubmitting(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -45,7 +46,7 @@ export function DeleteConfirmModal({ title, children, onConfirm, onClose, invali
|
|||||||
{children}
|
{children}
|
||||||
</Modal.Body>
|
</Modal.Body>
|
||||||
<Modal.Footer>
|
<Modal.Footer>
|
||||||
<Button data-bs-dismiss="modal" onClick={onClose} disabled={submitting}>
|
<Button data-bs-dismiss="modal" onClick={onClose} disabled={isSubmitting}>
|
||||||
{intl.formatMessage({ id: "cancel" })}
|
{intl.formatMessage({ id: "cancel" })}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
@@ -53,8 +54,8 @@ export function DeleteConfirmModal({ title, children, onConfirm, onClose, invali
|
|||||||
actionType="primary"
|
actionType="primary"
|
||||||
className="ms-auto btn-red"
|
className="ms-auto btn-red"
|
||||||
data-bs-dismiss="modal"
|
data-bs-dismiss="modal"
|
||||||
isLoading={submitting}
|
isLoading={isSubmitting}
|
||||||
disabled={submitting}
|
disabled={isSubmitting}
|
||||||
onClick={onSubmit}
|
onClick={onSubmit}
|
||||||
>
|
>
|
||||||
{intl.formatMessage({ id: "action.delete" })}
|
{intl.formatMessage({ id: "action.delete" })}
|
||||||
|
@@ -17,8 +17,11 @@ export function PermissionsModal({ userId, onClose }: Props) {
|
|||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const [errorMsg, setErrorMsg] = useState<string | null>(null);
|
const [errorMsg, setErrorMsg] = useState<string | null>(null);
|
||||||
const { data, isLoading, error } = useUser(userId);
|
const { data, isLoading, error } = useUser(userId);
|
||||||
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||||
|
|
||||||
const onSubmit = async (values: any, { setSubmitting }: any) => {
|
const onSubmit = async (values: any, { setSubmitting }: any) => {
|
||||||
|
if (isSubmitting) return;
|
||||||
|
setIsSubmitting(true);
|
||||||
setErrorMsg(null);
|
setErrorMsg(null);
|
||||||
try {
|
try {
|
||||||
await setPermissions(userId, values);
|
await setPermissions(userId, values);
|
||||||
@@ -29,6 +32,7 @@ export function PermissionsModal({ userId, onClose }: Props) {
|
|||||||
setErrorMsg(intl.formatMessage({ id: err.message }));
|
setErrorMsg(intl.formatMessage({ id: err.message }));
|
||||||
}
|
}
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
|
setIsSubmitting(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getPermissionButtons = (field: any, form: any) => {
|
const getPermissionButtons = (field: any, form: any) => {
|
||||||
@@ -104,7 +108,7 @@ export function PermissionsModal({ userId, onClose }: Props) {
|
|||||||
}
|
}
|
||||||
onSubmit={onSubmit}
|
onSubmit={onSubmit}
|
||||||
>
|
>
|
||||||
{({ isSubmitting }) => (
|
{() => (
|
||||||
<Form>
|
<Form>
|
||||||
<Modal.Header closeButton>
|
<Modal.Header closeButton>
|
||||||
<Modal.Title>
|
<Modal.Title>
|
||||||
|
@@ -15,8 +15,10 @@ interface Props {
|
|||||||
export function SetPasswordModal({ userId, onClose }: Props) {
|
export function SetPasswordModal({ userId, onClose }: Props) {
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
const [showPassword, setShowPassword] = useState(false);
|
const [showPassword, setShowPassword] = useState(false);
|
||||||
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||||
|
|
||||||
const onSubmit = async (values: any, { setSubmitting }: any) => {
|
const onSubmit = async (values: any, { setSubmitting }: any) => {
|
||||||
|
if (isSubmitting) return;
|
||||||
setError(null);
|
setError(null);
|
||||||
try {
|
try {
|
||||||
await updateAuth(userId, values.new);
|
await updateAuth(userId, values.new);
|
||||||
@@ -24,6 +26,7 @@ export function SetPasswordModal({ userId, onClose }: Props) {
|
|||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
setError(intl.formatMessage({ id: err.message }));
|
setError(intl.formatMessage({ id: err.message }));
|
||||||
}
|
}
|
||||||
|
setIsSubmitting(false);
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -37,7 +40,7 @@ export function SetPasswordModal({ userId, onClose }: Props) {
|
|||||||
}
|
}
|
||||||
onSubmit={onSubmit}
|
onSubmit={onSubmit}
|
||||||
>
|
>
|
||||||
{({ isSubmitting }) => (
|
{() => (
|
||||||
<Form>
|
<Form>
|
||||||
<Modal.Header closeButton>
|
<Modal.Header closeButton>
|
||||||
<Modal.Title>{intl.formatMessage({ id: "user.set-password" })}</Modal.Title>
|
<Modal.Title>{intl.formatMessage({ id: "user.set-password" })}</Modal.Title>
|
||||||
|
@@ -17,9 +17,13 @@ export function UserModal({ userId, onClose }: Props) {
|
|||||||
const { data: currentUser, isLoading: currentIsLoading } = useUser("me");
|
const { data: currentUser, isLoading: currentIsLoading } = useUser("me");
|
||||||
const { mutate: setUser } = useSetUser();
|
const { mutate: setUser } = useSetUser();
|
||||||
const [errorMsg, setErrorMsg] = useState<string | null>(null);
|
const [errorMsg, setErrorMsg] = useState<string | null>(null);
|
||||||
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||||
|
|
||||||
const onSubmit = async (values: any, { setSubmitting }: any) => {
|
const onSubmit = async (values: any, { setSubmitting }: any) => {
|
||||||
|
if (isSubmitting) return;
|
||||||
|
setIsSubmitting(true);
|
||||||
setErrorMsg(null);
|
setErrorMsg(null);
|
||||||
|
|
||||||
const { ...payload } = {
|
const { ...payload } = {
|
||||||
id: userId === "new" ? undefined : userId,
|
id: userId === "new" ? undefined : userId,
|
||||||
roles: [],
|
roles: [],
|
||||||
@@ -43,7 +47,10 @@ export function UserModal({ userId, onClose }: Props) {
|
|||||||
showSuccess(intl.formatMessage({ id: "notification.user-saved" }));
|
showSuccess(intl.formatMessage({ id: "notification.user-saved" }));
|
||||||
onClose();
|
onClose();
|
||||||
},
|
},
|
||||||
onSettled: () => setSubmitting(false),
|
onSettled: () => {
|
||||||
|
setIsSubmitting(false);
|
||||||
|
setSubmitting(false);
|
||||||
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -68,7 +75,7 @@ export function UserModal({ userId, onClose }: Props) {
|
|||||||
}
|
}
|
||||||
onSubmit={onSubmit}
|
onSubmit={onSubmit}
|
||||||
>
|
>
|
||||||
{({ isSubmitting }) => (
|
{() => (
|
||||||
<Form>
|
<Form>
|
||||||
<Modal.Header closeButton>
|
<Modal.Header closeButton>
|
||||||
<Modal.Title>
|
<Modal.Title>
|
||||||
|
@@ -1,3 +1,5 @@
|
|||||||
|
import { intl } from "src/locale";
|
||||||
|
|
||||||
const validateString = (minLength = 0, maxLength = 0) => {
|
const validateString = (minLength = 0, maxLength = 0) => {
|
||||||
if (minLength <= 0 && maxLength <= 0) {
|
if (minLength <= 0 && maxLength <= 0) {
|
||||||
// this doesn't require translation
|
// this doesn't require translation
|
||||||
@@ -6,12 +8,14 @@ const validateString = (minLength = 0, maxLength = 0) => {
|
|||||||
|
|
||||||
return (value: string): string | undefined => {
|
return (value: string): string | undefined => {
|
||||||
if (minLength && (typeof value === "undefined" || !value.length)) {
|
if (minLength && (typeof value === "undefined" || !value.length)) {
|
||||||
return "This is required";
|
return intl.formatMessage({ id: "error.required" });
|
||||||
}
|
}
|
||||||
if (minLength && value.length < minLength) {
|
if (minLength && value.length < minLength) {
|
||||||
|
// TODO: i18n
|
||||||
return `Minimum length is ${minLength} character${minLength === 1 ? "" : "s"}`;
|
return `Minimum length is ${minLength} character${minLength === 1 ? "" : "s"}`;
|
||||||
}
|
}
|
||||||
if (maxLength && (typeof value === "undefined" || value.length > maxLength)) {
|
if (maxLength && (typeof value === "undefined" || value.length > maxLength)) {
|
||||||
|
// TODO: i18n
|
||||||
return `Maximum length is ${maxLength} character${maxLength === 1 ? "" : "s"}`;
|
return `Maximum length is ${maxLength} character${maxLength === 1 ? "" : "s"}`;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -26,12 +30,14 @@ const validateNumber = (min = -1, max = -1) => {
|
|||||||
return (value: string): string | undefined => {
|
return (value: string): string | undefined => {
|
||||||
const int: number = +value;
|
const int: number = +value;
|
||||||
if (min > -1 && !int) {
|
if (min > -1 && !int) {
|
||||||
return "This is required";
|
return intl.formatMessage({ id: "error.required" });
|
||||||
}
|
}
|
||||||
if (min > -1 && int < min) {
|
if (min > -1 && int < min) {
|
||||||
|
// TODO: i18n
|
||||||
return `Minimum is ${min}`;
|
return `Minimum is ${min}`;
|
||||||
}
|
}
|
||||||
if (max > -1 && int > max) {
|
if (max > -1 && int > max) {
|
||||||
|
// TODO: i18n
|
||||||
return `Maximum is ${max}`;
|
return `Maximum is ${max}`;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -40,12 +46,62 @@ const validateNumber = (min = -1, max = -1) => {
|
|||||||
const validateEmail = () => {
|
const validateEmail = () => {
|
||||||
return (value: string): string | undefined => {
|
return (value: string): string | undefined => {
|
||||||
if (!value.length) {
|
if (!value.length) {
|
||||||
return "This is required";
|
return intl.formatMessage({ id: "error.required" });
|
||||||
}
|
}
|
||||||
if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(value)) {
|
if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(value)) {
|
||||||
return "Invalid email address";
|
return intl.formatMessage({ id: "error.invalid-email" });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export { validateEmail, validateNumber, validateString };
|
const validateDomain = (allowWildcards = false) => {
|
||||||
|
return (d: string): boolean => {
|
||||||
|
const dom = d.trim().toLowerCase();
|
||||||
|
|
||||||
|
if (dom.length < 3) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prevent wildcards
|
||||||
|
if (!allowWildcards && dom.indexOf("*") !== -1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prevent duplicate * in domain
|
||||||
|
if ((dom.match(/\*/g) || []).length > 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prevent some invalid characters
|
||||||
|
if ((dom.match(/(@|,|!|&|\$|#|%|\^|\(|\))/g) || []).length > 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This will match *.com type domains,
|
||||||
|
return dom.match(/\*\.[^.]+$/m) === null;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const validateDomains = (allowWildcards = false, maxDomains?: number) => {
|
||||||
|
const vDom = validateDomain(allowWildcards);
|
||||||
|
|
||||||
|
return (value: string[]): string | undefined => {
|
||||||
|
if (!value.length) {
|
||||||
|
return intl.formatMessage({ id: "error.required" });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deny if the list of domains is hit
|
||||||
|
if (maxDomains && value.length >= maxDomains) {
|
||||||
|
return intl.formatMessage({ id: "error.max-domains" }, { max: maxDomains });
|
||||||
|
}
|
||||||
|
|
||||||
|
// validate each domain
|
||||||
|
for (let i = 0; i < value.length; i++) {
|
||||||
|
if (!vDom(value[i])) {
|
||||||
|
return intl.formatMessage({ id: "error.invalid-domain" }, { domain: value[i] });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export { validateEmail, validateNumber, validateString, validateDomains, validateDomain };
|
||||||
|
@@ -129,6 +129,7 @@ const Dashboard = () => {
|
|||||||
- fix bad jwt not refreshing entire page
|
- fix bad jwt not refreshing entire page
|
||||||
- add help docs for host types
|
- add help docs for host types
|
||||||
- REDO SCREENSHOTS in docs folder
|
- REDO SCREENSHOTS in docs folder
|
||||||
|
- Remove letsEncryptEmail field from new certificate requests, use current user email server side
|
||||||
|
|
||||||
More for api, then implement here:
|
More for api, then implement here:
|
||||||
- Properly implement refresh tokens
|
- Properly implement refresh tokens
|
||||||
|
@@ -10,10 +10,11 @@ import Empty from "./Empty";
|
|||||||
interface Props {
|
interface Props {
|
||||||
data: DeadHost[];
|
data: DeadHost[];
|
||||||
isFetching?: boolean;
|
isFetching?: boolean;
|
||||||
|
onEdit?: (id: number) => void;
|
||||||
onDelete?: (id: number) => void;
|
onDelete?: (id: number) => void;
|
||||||
onNew?: () => void;
|
onNew?: () => void;
|
||||||
}
|
}
|
||||||
export default function Table({ data, isFetching, onDelete, onNew }: Props) {
|
export default function Table({ data, isFetching, onEdit, onDelete, onNew }: Props) {
|
||||||
const columnHelper = createColumnHelper<DeadHost>();
|
const columnHelper = createColumnHelper<DeadHost>();
|
||||||
const columns = useMemo(
|
const columns = useMemo(
|
||||||
() => [
|
() => [
|
||||||
@@ -71,7 +72,14 @@ export default function Table({ data, isFetching, onDelete, onNew }: Props) {
|
|||||||
{ id: info.row.original.id },
|
{ id: info.row.original.id },
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
<a className="dropdown-item" href="#">
|
<a
|
||||||
|
className="dropdown-item"
|
||||||
|
href="#"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
onEdit?.(info.row.original.id);
|
||||||
|
}}
|
||||||
|
>
|
||||||
<IconEdit size={16} />
|
<IconEdit size={16} />
|
||||||
{intl.formatMessage({ id: "action.edit" })}
|
{intl.formatMessage({ id: "action.edit" })}
|
||||||
</a>
|
</a>
|
||||||
@@ -100,7 +108,7 @@ export default function Table({ data, isFetching, onDelete, onNew }: Props) {
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
[columnHelper, onDelete],
|
[columnHelper, onDelete, onEdit],
|
||||||
);
|
);
|
||||||
|
|
||||||
const tableInstance = useReactTable<DeadHost>({
|
const tableInstance = useReactTable<DeadHost>({
|
||||||
|
@@ -58,6 +58,7 @@ export default function TableWrapper() {
|
|||||||
<Table
|
<Table
|
||||||
data={data ?? []}
|
data={data ?? []}
|
||||||
isFetching={isFetching}
|
isFetching={isFetching}
|
||||||
|
onEdit={(id: number) => setEditId(id)}
|
||||||
onDelete={(id: number) => setDeleteId(id)}
|
onDelete={(id: number) => setDeleteId(id)}
|
||||||
onNew={() => setEditId("new")}
|
onNew={() => setEditId("new")}
|
||||||
/>
|
/>
|
||||||
|
@@ -142,7 +142,7 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@babel/helper-plugin-utils" "^7.27.1"
|
"@babel/helper-plugin-utils" "^7.27.1"
|
||||||
|
|
||||||
"@babel/runtime@^7.12.0", "@babel/runtime@^7.18.3":
|
"@babel/runtime@^7.12.0", "@babel/runtime@^7.18.3", "@babel/runtime@^7.18.6":
|
||||||
version "7.28.4"
|
version "7.28.4"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.28.4.tgz#a70226016fabe25c5783b2f22d3e1c9bc5ca3326"
|
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.28.4.tgz#a70226016fabe25c5783b2f22d3e1c9bc5ca3326"
|
||||||
integrity sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==
|
integrity sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==
|
||||||
@@ -982,6 +982,20 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.8.tgz#958b91c991b1867ced318bedea0e215ee050726e"
|
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.8.tgz#958b91c991b1867ced318bedea0e215ee050726e"
|
||||||
integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==
|
integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==
|
||||||
|
|
||||||
|
"@types/hast@^2.0.0":
|
||||||
|
version "2.3.10"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/hast/-/hast-2.3.10.tgz#5c9d9e0b304bbb8879b857225c5ebab2d81d7643"
|
||||||
|
integrity sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==
|
||||||
|
dependencies:
|
||||||
|
"@types/unist" "^2"
|
||||||
|
|
||||||
|
"@types/hast@^3.0.0":
|
||||||
|
version "3.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/hast/-/hast-3.0.4.tgz#1d6b39993b82cea6ad783945b0508c25903e15aa"
|
||||||
|
integrity sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==
|
||||||
|
dependencies:
|
||||||
|
"@types/unist" "*"
|
||||||
|
|
||||||
"@types/hoist-non-react-statics@^3.3.1":
|
"@types/hoist-non-react-statics@^3.3.1":
|
||||||
version "3.3.7"
|
version "3.3.7"
|
||||||
resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.7.tgz#306e3a3a73828522efa1341159da4846e7573a6c"
|
resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.7.tgz#306e3a3a73828522efa1341159da4846e7573a6c"
|
||||||
@@ -994,6 +1008,13 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/humps/-/humps-2.0.6.tgz#a358688fe092e40b5f50261e0a55e2fa6d68cabe"
|
resolved "https://registry.yarnpkg.com/@types/humps/-/humps-2.0.6.tgz#a358688fe092e40b5f50261e0a55e2fa6d68cabe"
|
||||||
integrity sha512-Fagm1/a/1J9gDKzGdtlPmmTN5eSw/aaTzHtj740oSfo+MODsSY2WglxMmhTdOglC8nxqUhGGQ+5HfVtBvxo3Kg==
|
integrity sha512-Fagm1/a/1J9gDKzGdtlPmmTN5eSw/aaTzHtj740oSfo+MODsSY2WglxMmhTdOglC8nxqUhGGQ+5HfVtBvxo3Kg==
|
||||||
|
|
||||||
|
"@types/mdast@^4.0.0":
|
||||||
|
version "4.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-4.0.4.tgz#7ccf72edd2f1aa7dd3437e180c64373585804dd6"
|
||||||
|
integrity sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==
|
||||||
|
dependencies:
|
||||||
|
"@types/unist" "*"
|
||||||
|
|
||||||
"@types/node@^20.0.0":
|
"@types/node@^20.0.0":
|
||||||
version "20.19.11"
|
version "20.19.11"
|
||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.19.11.tgz#728cab53092bd5f143beed7fbba7ba99de3c16c4"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.19.11.tgz#728cab53092bd5f143beed7fbba7ba99de3c16c4"
|
||||||
@@ -1006,6 +1027,11 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.2.tgz#5950e50960793055845e956c427fc2b0d70c5239"
|
resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.2.tgz#5950e50960793055845e956c427fc2b0d70c5239"
|
||||||
integrity sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==
|
integrity sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==
|
||||||
|
|
||||||
|
"@types/prismjs@^1.0.0":
|
||||||
|
version "1.26.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/prismjs/-/prismjs-1.26.5.tgz#72499abbb4c4ec9982446509d2f14fb8483869d6"
|
||||||
|
integrity sha512-AUZTa7hQ2KY5L7AmtSiqxlhWxb4ina0yd8hNbl4TWuqnv/pFP0nDMb3YrfSBf4hJVGLh2YEIBfKaBW/9UEl6IQ==
|
||||||
|
|
||||||
"@types/prop-types@^15.7.12":
|
"@types/prop-types@^15.7.12":
|
||||||
version "15.7.15"
|
version "15.7.15"
|
||||||
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.15.tgz#e6e5a86d602beaca71ce5163fadf5f95d70931c7"
|
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.15.tgz#e6e5a86d602beaca71ce5163fadf5f95d70931c7"
|
||||||
@@ -1042,6 +1068,16 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
csstype "^3.0.2"
|
csstype "^3.0.2"
|
||||||
|
|
||||||
|
"@types/unist@*", "@types/unist@^3.0.0":
|
||||||
|
version "3.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/unist/-/unist-3.0.3.tgz#acaab0f919ce69cce629c2d4ed2eb4adc1b6c20c"
|
||||||
|
integrity sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==
|
||||||
|
|
||||||
|
"@types/unist@^2", "@types/unist@^2.0.0":
|
||||||
|
version "2.0.11"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.11.tgz#11af57b127e32487774841f7a4e54eab166d03c4"
|
||||||
|
integrity sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==
|
||||||
|
|
||||||
"@types/warning@^3.0.3":
|
"@types/warning@^3.0.3":
|
||||||
version "3.0.3"
|
version "3.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/@types/warning/-/warning-3.0.3.tgz#d1884c8cc4a426d1ac117ca2611bf333834c6798"
|
resolved "https://registry.yarnpkg.com/@types/warning/-/warning-3.0.3.tgz#d1884c8cc4a426d1ac117ca2611bf333834c6798"
|
||||||
@@ -1052,6 +1088,20 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/whatwg-mimetype/-/whatwg-mimetype-3.0.2.tgz#e5e06dcd3e92d4e622ef0129637707d66c28d6a4"
|
resolved "https://registry.yarnpkg.com/@types/whatwg-mimetype/-/whatwg-mimetype-3.0.2.tgz#e5e06dcd3e92d4e622ef0129637707d66c28d6a4"
|
||||||
integrity sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA==
|
integrity sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA==
|
||||||
|
|
||||||
|
"@uiw/react-textarea-code-editor@^3.1.1":
|
||||||
|
version "3.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@uiw/react-textarea-code-editor/-/react-textarea-code-editor-3.1.1.tgz#8ca1b706a3081a51c68bc0df91c9c3cdadd9944e"
|
||||||
|
integrity sha512-AERRbp/d85vWR+UPgsB5hEgerNXuyszdmhWl2fV2H2jN63jgOobwEnjIpb76Vwy8SaGa/AdehaoJX2XZgNXtJA==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.18.6"
|
||||||
|
rehype "~13.0.0"
|
||||||
|
rehype-prism-plus "2.0.0"
|
||||||
|
|
||||||
|
"@ungap/structured-clone@^1.0.0":
|
||||||
|
version "1.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.3.0.tgz#d06bbb384ebcf6c505fde1c3d0ed4ddffe0aaff8"
|
||||||
|
integrity sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==
|
||||||
|
|
||||||
"@vitejs/plugin-react@^5.0.3":
|
"@vitejs/plugin-react@^5.0.3":
|
||||||
version "5.0.3"
|
version "5.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/@vitejs/plugin-react/-/plugin-react-5.0.3.tgz#182ea45406d89e55b4e35c92a4a8c2c8388726c8"
|
resolved "https://registry.yarnpkg.com/@vitejs/plugin-react/-/plugin-react-5.0.3.tgz#182ea45406d89e55b4e35c92a4a8c2c8388726c8"
|
||||||
@@ -1166,6 +1216,11 @@ babel-plugin-macros@^3.1.0:
|
|||||||
cosmiconfig "^7.0.0"
|
cosmiconfig "^7.0.0"
|
||||||
resolve "^1.19.0"
|
resolve "^1.19.0"
|
||||||
|
|
||||||
|
bail@^2.0.0:
|
||||||
|
version "2.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/bail/-/bail-2.0.2.tgz#d26f5cd8fe5d6f832a31517b9f7c356040ba6d5d"
|
||||||
|
integrity sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==
|
||||||
|
|
||||||
base64-js@^1.3.1:
|
base64-js@^1.3.1:
|
||||||
version "1.5.1"
|
version "1.5.1"
|
||||||
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
|
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
|
||||||
@@ -1216,6 +1271,11 @@ caniuse-lite@^1.0.30001737:
|
|||||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001739.tgz#b34ce2d56bfc22f4352b2af0144102d623a124f4"
|
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001739.tgz#b34ce2d56bfc22f4352b2af0144102d623a124f4"
|
||||||
integrity sha512-y+j60d6ulelrNSwpPyrHdl+9mJnQzHBr08xm48Qno0nSk4h3Qojh+ziv2qE6rXf4k3tadF4o1J/1tAbVm1NtnA==
|
integrity sha512-y+j60d6ulelrNSwpPyrHdl+9mJnQzHBr08xm48Qno0nSk4h3Qojh+ziv2qE6rXf4k3tadF4o1J/1tAbVm1NtnA==
|
||||||
|
|
||||||
|
ccount@^2.0.0:
|
||||||
|
version "2.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/ccount/-/ccount-2.0.1.tgz#17a3bf82302e0870d6da43a01311a8bc02a3ecf5"
|
||||||
|
integrity sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==
|
||||||
|
|
||||||
chai@^5.2.0:
|
chai@^5.2.0:
|
||||||
version "5.3.3"
|
version "5.3.3"
|
||||||
resolved "https://registry.yarnpkg.com/chai/-/chai-5.3.3.tgz#dd3da955e270916a4bd3f625f4b919996ada7e06"
|
resolved "https://registry.yarnpkg.com/chai/-/chai-5.3.3.tgz#dd3da955e270916a4bd3f625f4b919996ada7e06"
|
||||||
@@ -1227,6 +1287,26 @@ chai@^5.2.0:
|
|||||||
loupe "^3.1.0"
|
loupe "^3.1.0"
|
||||||
pathval "^2.0.0"
|
pathval "^2.0.0"
|
||||||
|
|
||||||
|
character-entities-html4@^2.0.0:
|
||||||
|
version "2.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/character-entities-html4/-/character-entities-html4-2.1.0.tgz#1f1adb940c971a4b22ba39ddca6b618dc6e56b2b"
|
||||||
|
integrity sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==
|
||||||
|
|
||||||
|
character-entities-legacy@^3.0.0:
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz#76bc83a90738901d7bc223a9e93759fdd560125b"
|
||||||
|
integrity sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==
|
||||||
|
|
||||||
|
character-entities@^2.0.0:
|
||||||
|
version "2.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-2.0.2.tgz#2d09c2e72cd9523076ccb21157dff66ad43fcc22"
|
||||||
|
integrity sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==
|
||||||
|
|
||||||
|
character-reference-invalid@^2.0.0:
|
||||||
|
version "2.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz#85c66b041e43b47210faf401278abf808ac45cb9"
|
||||||
|
integrity sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==
|
||||||
|
|
||||||
check-error@^2.1.1:
|
check-error@^2.1.1:
|
||||||
version "2.1.1"
|
version "2.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/check-error/-/check-error-2.1.1.tgz#87eb876ae71ee388fa0471fe423f494be1d96ccc"
|
resolved "https://registry.yarnpkg.com/check-error/-/check-error-2.1.1.tgz#87eb876ae71ee388fa0471fe423f494be1d96ccc"
|
||||||
@@ -1249,6 +1329,11 @@ clsx@^2.1.1:
|
|||||||
resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.1.1.tgz#eed397c9fd8bd882bfb18deab7102049a2f32999"
|
resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.1.1.tgz#eed397c9fd8bd882bfb18deab7102049a2f32999"
|
||||||
integrity sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==
|
integrity sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==
|
||||||
|
|
||||||
|
comma-separated-tokens@^2.0.0:
|
||||||
|
version "2.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz#4e89c9458acb61bc8fef19f4529973b2392839ee"
|
||||||
|
integrity sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==
|
||||||
|
|
||||||
convert-source-map@^1.5.0:
|
convert-source-map@^1.5.0:
|
||||||
version "1.9.0"
|
version "1.9.0"
|
||||||
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f"
|
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f"
|
||||||
@@ -1307,6 +1392,13 @@ decimal.js@^10.4.3:
|
|||||||
resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.6.0.tgz#e649a43e3ab953a72192ff5983865e509f37ed9a"
|
resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.6.0.tgz#e649a43e3ab953a72192ff5983865e509f37ed9a"
|
||||||
integrity sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==
|
integrity sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==
|
||||||
|
|
||||||
|
decode-named-character-reference@^1.0.0:
|
||||||
|
version "1.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/decode-named-character-reference/-/decode-named-character-reference-1.2.0.tgz#25c32ae6dd5e21889549d40f676030e9514cc0ed"
|
||||||
|
integrity sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==
|
||||||
|
dependencies:
|
||||||
|
character-entities "^2.0.0"
|
||||||
|
|
||||||
decode-uri-component@^0.4.1:
|
decode-uri-component@^0.4.1:
|
||||||
version "0.4.1"
|
version "0.4.1"
|
||||||
resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.4.1.tgz#2ac4859663c704be22bf7db760a1494a49ab2cc5"
|
resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.4.1.tgz#2ac4859663c704be22bf7db760a1494a49ab2cc5"
|
||||||
@@ -1322,7 +1414,7 @@ deepmerge@^2.1.1:
|
|||||||
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-2.2.1.tgz#5d3ff22a01c00f645405a2fbc17d0778a1801170"
|
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-2.2.1.tgz#5d3ff22a01c00f645405a2fbc17d0778a1801170"
|
||||||
integrity sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==
|
integrity sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==
|
||||||
|
|
||||||
dequal@^2.0.3:
|
dequal@^2.0.0, dequal@^2.0.3:
|
||||||
version "2.0.3"
|
version "2.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be"
|
resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be"
|
||||||
integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==
|
integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==
|
||||||
@@ -1332,6 +1424,13 @@ detect-libc@^1.0.3:
|
|||||||
resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b"
|
resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b"
|
||||||
integrity sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==
|
integrity sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==
|
||||||
|
|
||||||
|
devlop@^1.0.0, devlop@^1.1.0:
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/devlop/-/devlop-1.1.0.tgz#4db7c2ca4dc6e0e834c30be70c94bbc976dc7018"
|
||||||
|
integrity sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==
|
||||||
|
dependencies:
|
||||||
|
dequal "^2.0.0"
|
||||||
|
|
||||||
dom-accessibility-api@^0.5.9:
|
dom-accessibility-api@^0.5.9:
|
||||||
version "0.5.16"
|
version "0.5.16"
|
||||||
resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz#5a7429e6066eb3664d911e33fb0e45de8eb08453"
|
resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz#5a7429e6066eb3664d911e33fb0e45de8eb08453"
|
||||||
@@ -1355,6 +1454,11 @@ electron-to-chromium@^1.5.211:
|
|||||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.213.tgz#f434187f227fb7e67bfcf8243b959cf3ce14013e"
|
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.213.tgz#f434187f227fb7e67bfcf8243b959cf3ce14013e"
|
||||||
integrity sha512-xr9eRzSLNa4neDO0xVFrkXu3vyIzG4Ay08dApecw42Z1NbmCt+keEpXdvlYGVe0wtvY5dhW0Ay0lY0IOfsCg0Q==
|
integrity sha512-xr9eRzSLNa4neDO0xVFrkXu3vyIzG4Ay08dApecw42Z1NbmCt+keEpXdvlYGVe0wtvY5dhW0Ay0lY0IOfsCg0Q==
|
||||||
|
|
||||||
|
entities@^6.0.0:
|
||||||
|
version "6.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/entities/-/entities-6.0.1.tgz#c28c34a43379ca7f61d074130b2f5f7020a30694"
|
||||||
|
integrity sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==
|
||||||
|
|
||||||
error-ex@^1.3.1:
|
error-ex@^1.3.1:
|
||||||
version "1.3.4"
|
version "1.3.4"
|
||||||
resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.4.tgz#b3a8d8bb6f92eecc1629e3e27d3c8607a8a32414"
|
resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.4.tgz#b3a8d8bb6f92eecc1629e3e27d3c8607a8a32414"
|
||||||
@@ -1421,6 +1525,11 @@ expect-type@^1.2.1:
|
|||||||
resolved "https://registry.yarnpkg.com/expect-type/-/expect-type-1.2.2.tgz#c030a329fb61184126c8447585bc75a7ec6fbff3"
|
resolved "https://registry.yarnpkg.com/expect-type/-/expect-type-1.2.2.tgz#c030a329fb61184126c8447585bc75a7ec6fbff3"
|
||||||
integrity sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==
|
integrity sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==
|
||||||
|
|
||||||
|
extend@^3.0.0:
|
||||||
|
version "3.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
|
||||||
|
integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==
|
||||||
|
|
||||||
fast-deep-equal@^3.1.3:
|
fast-deep-equal@^3.1.3:
|
||||||
version "3.1.3"
|
version "3.1.3"
|
||||||
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
|
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
|
||||||
@@ -1506,6 +1615,99 @@ hasown@^2.0.2:
|
|||||||
dependencies:
|
dependencies:
|
||||||
function-bind "^1.1.2"
|
function-bind "^1.1.2"
|
||||||
|
|
||||||
|
hast-util-from-html@^2.0.0:
|
||||||
|
version "2.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/hast-util-from-html/-/hast-util-from-html-2.0.3.tgz#485c74785358beb80c4ba6346299311ac4c49c82"
|
||||||
|
integrity sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==
|
||||||
|
dependencies:
|
||||||
|
"@types/hast" "^3.0.0"
|
||||||
|
devlop "^1.1.0"
|
||||||
|
hast-util-from-parse5 "^8.0.0"
|
||||||
|
parse5 "^7.0.0"
|
||||||
|
vfile "^6.0.0"
|
||||||
|
vfile-message "^4.0.0"
|
||||||
|
|
||||||
|
hast-util-from-parse5@^8.0.0:
|
||||||
|
version "8.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/hast-util-from-parse5/-/hast-util-from-parse5-8.0.3.tgz#830a35022fff28c3fea3697a98c2f4cc6b835a2e"
|
||||||
|
integrity sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==
|
||||||
|
dependencies:
|
||||||
|
"@types/hast" "^3.0.0"
|
||||||
|
"@types/unist" "^3.0.0"
|
||||||
|
devlop "^1.0.0"
|
||||||
|
hastscript "^9.0.0"
|
||||||
|
property-information "^7.0.0"
|
||||||
|
vfile "^6.0.0"
|
||||||
|
vfile-location "^5.0.0"
|
||||||
|
web-namespaces "^2.0.0"
|
||||||
|
|
||||||
|
hast-util-parse-selector@^3.0.0:
|
||||||
|
version "3.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/hast-util-parse-selector/-/hast-util-parse-selector-3.1.1.tgz#25ab00ae9e75cbc62cf7a901f68a247eade659e2"
|
||||||
|
integrity sha512-jdlwBjEexy1oGz0aJ2f4GKMaVKkA9jwjr4MjAAI22E5fM/TXVZHuS5OpONtdeIkRKqAaryQ2E9xNQxijoThSZA==
|
||||||
|
dependencies:
|
||||||
|
"@types/hast" "^2.0.0"
|
||||||
|
|
||||||
|
hast-util-parse-selector@^4.0.0:
|
||||||
|
version "4.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz#352879fa86e25616036037dd8931fb5f34cb4a27"
|
||||||
|
integrity sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==
|
||||||
|
dependencies:
|
||||||
|
"@types/hast" "^3.0.0"
|
||||||
|
|
||||||
|
hast-util-to-html@^9.0.0:
|
||||||
|
version "9.0.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz#ccc673a55bb8e85775b08ac28380f72d47167005"
|
||||||
|
integrity sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==
|
||||||
|
dependencies:
|
||||||
|
"@types/hast" "^3.0.0"
|
||||||
|
"@types/unist" "^3.0.0"
|
||||||
|
ccount "^2.0.0"
|
||||||
|
comma-separated-tokens "^2.0.0"
|
||||||
|
hast-util-whitespace "^3.0.0"
|
||||||
|
html-void-elements "^3.0.0"
|
||||||
|
mdast-util-to-hast "^13.0.0"
|
||||||
|
property-information "^7.0.0"
|
||||||
|
space-separated-tokens "^2.0.0"
|
||||||
|
stringify-entities "^4.0.0"
|
||||||
|
zwitch "^2.0.4"
|
||||||
|
|
||||||
|
hast-util-to-string@^3.0.0:
|
||||||
|
version "3.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/hast-util-to-string/-/hast-util-to-string-3.0.1.tgz#a4f15e682849326dd211c97129c94b0c3e76527c"
|
||||||
|
integrity sha512-XelQVTDWvqcl3axRfI0xSeoVKzyIFPwsAGSLIsKdJKQMXDYJS4WYrBNF/8J7RdhIcFI2BOHgAifggsvsxp/3+A==
|
||||||
|
dependencies:
|
||||||
|
"@types/hast" "^3.0.0"
|
||||||
|
|
||||||
|
hast-util-whitespace@^3.0.0:
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz#7778ed9d3c92dd9e8c5c8f648a49c21fc51cb621"
|
||||||
|
integrity sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==
|
||||||
|
dependencies:
|
||||||
|
"@types/hast" "^3.0.0"
|
||||||
|
|
||||||
|
hastscript@^7.0.0:
|
||||||
|
version "7.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/hastscript/-/hastscript-7.2.0.tgz#0eafb7afb153d047077fa2a833dc9b7ec604d10b"
|
||||||
|
integrity sha512-TtYPq24IldU8iKoJQqvZOuhi5CyCQRAbvDOX0x1eW6rsHSxa/1i2CCiptNTotGHJ3VoHRGmqiv6/D3q113ikkw==
|
||||||
|
dependencies:
|
||||||
|
"@types/hast" "^2.0.0"
|
||||||
|
comma-separated-tokens "^2.0.0"
|
||||||
|
hast-util-parse-selector "^3.0.0"
|
||||||
|
property-information "^6.0.0"
|
||||||
|
space-separated-tokens "^2.0.0"
|
||||||
|
|
||||||
|
hastscript@^9.0.0:
|
||||||
|
version "9.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/hastscript/-/hastscript-9.0.1.tgz#dbc84bef6051d40084342c229c451cd9dc567dff"
|
||||||
|
integrity sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==
|
||||||
|
dependencies:
|
||||||
|
"@types/hast" "^3.0.0"
|
||||||
|
comma-separated-tokens "^2.0.0"
|
||||||
|
hast-util-parse-selector "^4.0.0"
|
||||||
|
property-information "^7.0.0"
|
||||||
|
space-separated-tokens "^2.0.0"
|
||||||
|
|
||||||
hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.1, hoist-non-react-statics@^3.3.2:
|
hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.1, hoist-non-react-statics@^3.3.2:
|
||||||
version "3.3.2"
|
version "3.3.2"
|
||||||
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
|
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
|
||||||
@@ -1513,6 +1715,11 @@ hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.1, hoist-non-react-
|
|||||||
dependencies:
|
dependencies:
|
||||||
react-is "^16.7.0"
|
react-is "^16.7.0"
|
||||||
|
|
||||||
|
html-void-elements@^3.0.0:
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/html-void-elements/-/html-void-elements-3.0.0.tgz#fc9dbd84af9e747249034d4d62602def6517f1d7"
|
||||||
|
integrity sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==
|
||||||
|
|
||||||
humps@^2.0.1:
|
humps@^2.0.1:
|
||||||
version "2.0.1"
|
version "2.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/humps/-/humps-2.0.1.tgz#dd02ea6081bd0568dc5d073184463957ba9ef9aa"
|
resolved "https://registry.yarnpkg.com/humps/-/humps-2.0.1.tgz#dd02ea6081bd0568dc5d073184463957ba9ef9aa"
|
||||||
@@ -1558,6 +1765,19 @@ invariant@^2.2.4:
|
|||||||
dependencies:
|
dependencies:
|
||||||
loose-envify "^1.0.0"
|
loose-envify "^1.0.0"
|
||||||
|
|
||||||
|
is-alphabetical@^2.0.0:
|
||||||
|
version "2.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-alphabetical/-/is-alphabetical-2.0.1.tgz#01072053ea7c1036df3c7d19a6daaec7f19e789b"
|
||||||
|
integrity sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==
|
||||||
|
|
||||||
|
is-alphanumerical@^2.0.0:
|
||||||
|
version "2.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz#7c03fbe96e3e931113e57f964b0a368cc2dfd875"
|
||||||
|
integrity sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==
|
||||||
|
dependencies:
|
||||||
|
is-alphabetical "^2.0.0"
|
||||||
|
is-decimal "^2.0.0"
|
||||||
|
|
||||||
is-arrayish@^0.2.1:
|
is-arrayish@^0.2.1:
|
||||||
version "0.2.1"
|
version "0.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
|
resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
|
||||||
@@ -1570,6 +1790,11 @@ is-core-module@^2.16.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
hasown "^2.0.2"
|
hasown "^2.0.2"
|
||||||
|
|
||||||
|
is-decimal@^2.0.0:
|
||||||
|
version "2.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-decimal/-/is-decimal-2.0.1.tgz#9469d2dc190d0214fd87d78b78caecc0cc14eef7"
|
||||||
|
integrity sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==
|
||||||
|
|
||||||
is-extglob@^2.1.1:
|
is-extglob@^2.1.1:
|
||||||
version "2.1.1"
|
version "2.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
|
resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
|
||||||
@@ -1582,11 +1807,21 @@ is-glob@^4.0.3:
|
|||||||
dependencies:
|
dependencies:
|
||||||
is-extglob "^2.1.1"
|
is-extglob "^2.1.1"
|
||||||
|
|
||||||
|
is-hexadecimal@^2.0.0:
|
||||||
|
version "2.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz#86b5bf668fca307498d319dfc03289d781a90027"
|
||||||
|
integrity sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==
|
||||||
|
|
||||||
is-number@^7.0.0:
|
is-number@^7.0.0:
|
||||||
version "7.0.0"
|
version "7.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
|
resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
|
||||||
integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
|
integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
|
||||||
|
|
||||||
|
is-plain-obj@^4.0.0:
|
||||||
|
version "4.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-4.1.0.tgz#d65025edec3657ce032fd7db63c97883eaed71f0"
|
||||||
|
integrity sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==
|
||||||
|
|
||||||
"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"
|
||||||
@@ -1663,11 +1898,58 @@ magic-string@^0.30.17:
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@jridgewell/sourcemap-codec" "^1.5.5"
|
"@jridgewell/sourcemap-codec" "^1.5.5"
|
||||||
|
|
||||||
|
mdast-util-to-hast@^13.0.0:
|
||||||
|
version "13.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz#5ca58e5b921cc0a3ded1bc02eed79a4fe4fe41f4"
|
||||||
|
integrity sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==
|
||||||
|
dependencies:
|
||||||
|
"@types/hast" "^3.0.0"
|
||||||
|
"@types/mdast" "^4.0.0"
|
||||||
|
"@ungap/structured-clone" "^1.0.0"
|
||||||
|
devlop "^1.0.0"
|
||||||
|
micromark-util-sanitize-uri "^2.0.0"
|
||||||
|
trim-lines "^3.0.0"
|
||||||
|
unist-util-position "^5.0.0"
|
||||||
|
unist-util-visit "^5.0.0"
|
||||||
|
vfile "^6.0.0"
|
||||||
|
|
||||||
memoize-one@^6.0.0:
|
memoize-one@^6.0.0:
|
||||||
version "6.0.0"
|
version "6.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-6.0.0.tgz#b2591b871ed82948aee4727dc6abceeeac8c1045"
|
resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-6.0.0.tgz#b2591b871ed82948aee4727dc6abceeeac8c1045"
|
||||||
integrity sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==
|
integrity sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==
|
||||||
|
|
||||||
|
micromark-util-character@^2.0.0:
|
||||||
|
version "2.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/micromark-util-character/-/micromark-util-character-2.1.1.tgz#2f987831a40d4c510ac261e89852c4e9703ccda6"
|
||||||
|
integrity sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==
|
||||||
|
dependencies:
|
||||||
|
micromark-util-symbol "^2.0.0"
|
||||||
|
micromark-util-types "^2.0.0"
|
||||||
|
|
||||||
|
micromark-util-encode@^2.0.0:
|
||||||
|
version "2.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz#0d51d1c095551cfaac368326963cf55f15f540b8"
|
||||||
|
integrity sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==
|
||||||
|
|
||||||
|
micromark-util-sanitize-uri@^2.0.0:
|
||||||
|
version "2.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz#ab89789b818a58752b73d6b55238621b7faa8fd7"
|
||||||
|
integrity sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==
|
||||||
|
dependencies:
|
||||||
|
micromark-util-character "^2.0.0"
|
||||||
|
micromark-util-encode "^2.0.0"
|
||||||
|
micromark-util-symbol "^2.0.0"
|
||||||
|
|
||||||
|
micromark-util-symbol@^2.0.0:
|
||||||
|
version "2.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz#e5da494e8eb2b071a0d08fb34f6cefec6c0a19b8"
|
||||||
|
integrity sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==
|
||||||
|
|
||||||
|
micromark-util-types@^2.0.0:
|
||||||
|
version "2.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/micromark-util-types/-/micromark-util-types-2.0.2.tgz#f00225f5f5a0ebc3254f96c36b6605c4b393908e"
|
||||||
|
integrity sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==
|
||||||
|
|
||||||
micromatch@^4.0.5:
|
micromatch@^4.0.5:
|
||||||
version "4.0.8"
|
version "4.0.8"
|
||||||
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202"
|
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202"
|
||||||
@@ -1721,6 +2003,19 @@ parent-module@^1.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
callsites "^3.0.0"
|
callsites "^3.0.0"
|
||||||
|
|
||||||
|
parse-entities@^4.0.0:
|
||||||
|
version "4.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-4.0.2.tgz#61d46f5ed28e4ee62e9ddc43d6b010188443f159"
|
||||||
|
integrity sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==
|
||||||
|
dependencies:
|
||||||
|
"@types/unist" "^2.0.0"
|
||||||
|
character-entities-legacy "^3.0.0"
|
||||||
|
character-reference-invalid "^2.0.0"
|
||||||
|
decode-named-character-reference "^1.0.0"
|
||||||
|
is-alphanumerical "^2.0.0"
|
||||||
|
is-decimal "^2.0.0"
|
||||||
|
is-hexadecimal "^2.0.0"
|
||||||
|
|
||||||
parse-json@^5.0.0:
|
parse-json@^5.0.0:
|
||||||
version "5.2.0"
|
version "5.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd"
|
resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd"
|
||||||
@@ -1731,6 +2026,18 @@ parse-json@^5.0.0:
|
|||||||
json-parse-even-better-errors "^2.3.0"
|
json-parse-even-better-errors "^2.3.0"
|
||||||
lines-and-columns "^1.1.6"
|
lines-and-columns "^1.1.6"
|
||||||
|
|
||||||
|
parse-numeric-range@^1.3.0:
|
||||||
|
version "1.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/parse-numeric-range/-/parse-numeric-range-1.3.0.tgz#7c63b61190d61e4d53a1197f0c83c47bb670ffa3"
|
||||||
|
integrity sha512-twN+njEipszzlMJd4ONUYgSfZPDxgHhT9Ahed5uTigpQn90FggW4SA/AIPq/6a149fTbE9qBEcSwE3FAEp6wQQ==
|
||||||
|
|
||||||
|
parse5@^7.0.0:
|
||||||
|
version "7.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.3.0.tgz#d7e224fa72399c7a175099f45fc2ad024b05ec05"
|
||||||
|
integrity sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==
|
||||||
|
dependencies:
|
||||||
|
entities "^6.0.0"
|
||||||
|
|
||||||
path-key@^4.0.0:
|
path-key@^4.0.0:
|
||||||
version "4.0.0"
|
version "4.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/path-key/-/path-key-4.0.0.tgz#295588dc3aee64154f877adb9d780b81c554bf18"
|
resolved "https://registry.yarnpkg.com/path-key/-/path-key-4.0.0.tgz#295588dc3aee64154f877adb9d780b81c554bf18"
|
||||||
@@ -1816,6 +2123,16 @@ prop-types@^15.6.0, prop-types@^15.6.2, prop-types@^15.8.1:
|
|||||||
object-assign "^4.1.1"
|
object-assign "^4.1.1"
|
||||||
react-is "^16.13.1"
|
react-is "^16.13.1"
|
||||||
|
|
||||||
|
property-information@^6.0.0:
|
||||||
|
version "6.5.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/property-information/-/property-information-6.5.0.tgz#6212fbb52ba757e92ef4fb9d657563b933b7ffec"
|
||||||
|
integrity sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==
|
||||||
|
|
||||||
|
property-information@^7.0.0:
|
||||||
|
version "7.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/property-information/-/property-information-7.1.0.tgz#b622e8646e02b580205415586b40804d3e8bfd5d"
|
||||||
|
integrity sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==
|
||||||
|
|
||||||
query-string@^9.3.1:
|
query-string@^9.3.1:
|
||||||
version "9.3.1"
|
version "9.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/query-string/-/query-string-9.3.1.tgz#d0c93e6c7fb7c17bdf04aa09e382114580ede270"
|
resolved "https://registry.yarnpkg.com/query-string/-/query-string-9.3.1.tgz#d0c93e6c7fb7c17bdf04aa09e382114580ede270"
|
||||||
@@ -1969,6 +2286,56 @@ redent@^3.0.0:
|
|||||||
indent-string "^4.0.0"
|
indent-string "^4.0.0"
|
||||||
strip-indent "^3.0.0"
|
strip-indent "^3.0.0"
|
||||||
|
|
||||||
|
refractor@^4.8.0:
|
||||||
|
version "4.9.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/refractor/-/refractor-4.9.0.tgz#2e1c7af0157230cdd2f9086660912eadc5f68323"
|
||||||
|
integrity sha512-nEG1SPXFoGGx+dcjftjv8cAjEusIh6ED1xhf5DG3C0x/k+rmZ2duKnc3QLpt6qeHv5fPb8uwN3VWN2BT7fr3Og==
|
||||||
|
dependencies:
|
||||||
|
"@types/hast" "^2.0.0"
|
||||||
|
"@types/prismjs" "^1.0.0"
|
||||||
|
hastscript "^7.0.0"
|
||||||
|
parse-entities "^4.0.0"
|
||||||
|
|
||||||
|
rehype-parse@^9.0.0:
|
||||||
|
version "9.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/rehype-parse/-/rehype-parse-9.0.1.tgz#9993bda129acc64c417a9d3654a7be38b2a94c20"
|
||||||
|
integrity sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag==
|
||||||
|
dependencies:
|
||||||
|
"@types/hast" "^3.0.0"
|
||||||
|
hast-util-from-html "^2.0.0"
|
||||||
|
unified "^11.0.0"
|
||||||
|
|
||||||
|
rehype-prism-plus@2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/rehype-prism-plus/-/rehype-prism-plus-2.0.0.tgz#75b1e2d0dd7496125987a1732cb7d560de02a0fd"
|
||||||
|
integrity sha512-FeM/9V2N7EvDZVdR2dqhAzlw5YI49m9Tgn7ZrYJeYHIahM6gcXpH0K1y2gNnKanZCydOMluJvX2cB9z3lhY8XQ==
|
||||||
|
dependencies:
|
||||||
|
hast-util-to-string "^3.0.0"
|
||||||
|
parse-numeric-range "^1.3.0"
|
||||||
|
refractor "^4.8.0"
|
||||||
|
rehype-parse "^9.0.0"
|
||||||
|
unist-util-filter "^5.0.0"
|
||||||
|
unist-util-visit "^5.0.0"
|
||||||
|
|
||||||
|
rehype-stringify@^10.0.0:
|
||||||
|
version "10.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/rehype-stringify/-/rehype-stringify-10.0.1.tgz#2ec1ebc56c6aba07905d3b4470bdf0f684f30b75"
|
||||||
|
integrity sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA==
|
||||||
|
dependencies:
|
||||||
|
"@types/hast" "^3.0.0"
|
||||||
|
hast-util-to-html "^9.0.0"
|
||||||
|
unified "^11.0.0"
|
||||||
|
|
||||||
|
rehype@~13.0.0:
|
||||||
|
version "13.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/rehype/-/rehype-13.0.2.tgz#ab0b3ac26573d7b265a0099feffad450e4cf1952"
|
||||||
|
integrity sha512-j31mdaRFrwFRUIlxGeuPXXKWQxet52RBQRvCmzl5eCefn/KGbomK5GMHNMsOJf55fgo3qw5tST5neDuarDYR2A==
|
||||||
|
dependencies:
|
||||||
|
"@types/hast" "^3.0.0"
|
||||||
|
rehype-parse "^9.0.0"
|
||||||
|
rehype-stringify "^10.0.0"
|
||||||
|
unified "^11.0.0"
|
||||||
|
|
||||||
resolve-from@^4.0.0:
|
resolve-from@^4.0.0:
|
||||||
version "4.0.0"
|
version "4.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
|
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
|
||||||
@@ -2069,6 +2436,11 @@ source-map@^0.5.7:
|
|||||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
|
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
|
||||||
integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==
|
integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==
|
||||||
|
|
||||||
|
space-separated-tokens@^2.0.0:
|
||||||
|
version "2.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz#1ecd9d2350a3844572c3f4a312bceb018348859f"
|
||||||
|
integrity sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==
|
||||||
|
|
||||||
split-on-first@^3.0.0:
|
split-on-first@^3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/split-on-first/-/split-on-first-3.0.0.tgz#f04959c9ea8101b9b0bbf35a61b9ebea784a23e7"
|
resolved "https://registry.yarnpkg.com/split-on-first/-/split-on-first-3.0.0.tgz#f04959c9ea8101b9b0bbf35a61b9ebea784a23e7"
|
||||||
@@ -2084,6 +2456,14 @@ std-env@^3.9.0:
|
|||||||
resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.9.0.tgz#1a6f7243b339dca4c9fd55e1c7504c77ef23e8f1"
|
resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.9.0.tgz#1a6f7243b339dca4c9fd55e1c7504c77ef23e8f1"
|
||||||
integrity sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==
|
integrity sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==
|
||||||
|
|
||||||
|
stringify-entities@^4.0.0:
|
||||||
|
version "4.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/stringify-entities/-/stringify-entities-4.0.4.tgz#b3b79ef5f277cc4ac73caeb0236c5ba939b3a4f3"
|
||||||
|
integrity sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==
|
||||||
|
dependencies:
|
||||||
|
character-entities-html4 "^2.0.0"
|
||||||
|
character-entities-legacy "^3.0.0"
|
||||||
|
|
||||||
strip-ansi@^7.1.0:
|
strip-ansi@^7.1.0:
|
||||||
version "7.1.0"
|
version "7.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45"
|
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45"
|
||||||
@@ -2178,6 +2558,16 @@ to-regex-range@^5.0.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
is-number "^7.0.0"
|
is-number "^7.0.0"
|
||||||
|
|
||||||
|
trim-lines@^3.0.0:
|
||||||
|
version "3.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/trim-lines/-/trim-lines-3.0.1.tgz#d802e332a07df861c48802c04321017b1bd87338"
|
||||||
|
integrity sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==
|
||||||
|
|
||||||
|
trough@^2.0.0:
|
||||||
|
version "2.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/trough/-/trough-2.2.0.tgz#94a60bd6bd375c152c1df911a4b11d5b0256f50f"
|
||||||
|
integrity sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==
|
||||||
|
|
||||||
tsconfck@^3.0.3:
|
tsconfck@^3.0.3:
|
||||||
version "3.1.6"
|
version "3.1.6"
|
||||||
resolved "https://registry.yarnpkg.com/tsconfck/-/tsconfck-3.1.6.tgz#da1f0b10d82237ac23422374b3fce1edb23c3ead"
|
resolved "https://registry.yarnpkg.com/tsconfck/-/tsconfck-3.1.6.tgz#da1f0b10d82237ac23422374b3fce1edb23c3ead"
|
||||||
@@ -2218,6 +2608,66 @@ unicorn-magic@^0.3.0:
|
|||||||
resolved "https://registry.yarnpkg.com/unicorn-magic/-/unicorn-magic-0.3.0.tgz#4efd45c85a69e0dd576d25532fbfa22aa5c8a104"
|
resolved "https://registry.yarnpkg.com/unicorn-magic/-/unicorn-magic-0.3.0.tgz#4efd45c85a69e0dd576d25532fbfa22aa5c8a104"
|
||||||
integrity sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==
|
integrity sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==
|
||||||
|
|
||||||
|
unified@^11.0.0:
|
||||||
|
version "11.0.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/unified/-/unified-11.0.5.tgz#f66677610a5c0a9ee90cab2b8d4d66037026d9e1"
|
||||||
|
integrity sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==
|
||||||
|
dependencies:
|
||||||
|
"@types/unist" "^3.0.0"
|
||||||
|
bail "^2.0.0"
|
||||||
|
devlop "^1.0.0"
|
||||||
|
extend "^3.0.0"
|
||||||
|
is-plain-obj "^4.0.0"
|
||||||
|
trough "^2.0.0"
|
||||||
|
vfile "^6.0.0"
|
||||||
|
|
||||||
|
unist-util-filter@^5.0.0:
|
||||||
|
version "5.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/unist-util-filter/-/unist-util-filter-5.0.1.tgz#f9f3a0bdee007e040964c274dda27bac663d0a39"
|
||||||
|
integrity sha512-pHx7D4Zt6+TsfwylH9+lYhBhzyhEnCXs/lbq/Hstxno5z4gVdyc2WEW0asfjGKPyG4pEKrnBv5hdkO6+aRnQJw==
|
||||||
|
dependencies:
|
||||||
|
"@types/unist" "^3.0.0"
|
||||||
|
unist-util-is "^6.0.0"
|
||||||
|
unist-util-visit-parents "^6.0.0"
|
||||||
|
|
||||||
|
unist-util-is@^6.0.0:
|
||||||
|
version "6.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-6.0.0.tgz#b775956486aff107a9ded971d996c173374be424"
|
||||||
|
integrity sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==
|
||||||
|
dependencies:
|
||||||
|
"@types/unist" "^3.0.0"
|
||||||
|
|
||||||
|
unist-util-position@^5.0.0:
|
||||||
|
version "5.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/unist-util-position/-/unist-util-position-5.0.0.tgz#678f20ab5ca1207a97d7ea8a388373c9cf896be4"
|
||||||
|
integrity sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==
|
||||||
|
dependencies:
|
||||||
|
"@types/unist" "^3.0.0"
|
||||||
|
|
||||||
|
unist-util-stringify-position@^4.0.0:
|
||||||
|
version "4.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz#449c6e21a880e0855bf5aabadeb3a740314abac2"
|
||||||
|
integrity sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==
|
||||||
|
dependencies:
|
||||||
|
"@types/unist" "^3.0.0"
|
||||||
|
|
||||||
|
unist-util-visit-parents@^6.0.0:
|
||||||
|
version "6.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz#4d5f85755c3b8f0dc69e21eca5d6d82d22162815"
|
||||||
|
integrity sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==
|
||||||
|
dependencies:
|
||||||
|
"@types/unist" "^3.0.0"
|
||||||
|
unist-util-is "^6.0.0"
|
||||||
|
|
||||||
|
unist-util-visit@^5.0.0:
|
||||||
|
version "5.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-5.0.0.tgz#a7de1f31f72ffd3519ea71814cccf5fd6a9217d6"
|
||||||
|
integrity sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==
|
||||||
|
dependencies:
|
||||||
|
"@types/unist" "^3.0.0"
|
||||||
|
unist-util-is "^6.0.0"
|
||||||
|
unist-util-visit-parents "^6.0.0"
|
||||||
|
|
||||||
update-browserslist-db@^1.1.3:
|
update-browserslist-db@^1.1.3:
|
||||||
version "1.1.3"
|
version "1.1.3"
|
||||||
resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz#348377dd245216f9e7060ff50b15a1b740b75420"
|
resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz#348377dd245216f9e7060ff50b15a1b740b75420"
|
||||||
@@ -2236,6 +2686,30 @@ use-sync-external-store@^1.4.0:
|
|||||||
resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz#55122e2a3edd2a6c106174c27485e0fd59bcfca0"
|
resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz#55122e2a3edd2a6c106174c27485e0fd59bcfca0"
|
||||||
integrity sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==
|
integrity sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==
|
||||||
|
|
||||||
|
vfile-location@^5.0.0:
|
||||||
|
version "5.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/vfile-location/-/vfile-location-5.0.3.tgz#cb9eacd20f2b6426d19451e0eafa3d0a846225c3"
|
||||||
|
integrity sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==
|
||||||
|
dependencies:
|
||||||
|
"@types/unist" "^3.0.0"
|
||||||
|
vfile "^6.0.0"
|
||||||
|
|
||||||
|
vfile-message@^4.0.0:
|
||||||
|
version "4.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-4.0.3.tgz#87b44dddd7b70f0641c2e3ed0864ba73e2ea8df4"
|
||||||
|
integrity sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==
|
||||||
|
dependencies:
|
||||||
|
"@types/unist" "^3.0.0"
|
||||||
|
unist-util-stringify-position "^4.0.0"
|
||||||
|
|
||||||
|
vfile@^6.0.0:
|
||||||
|
version "6.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/vfile/-/vfile-6.0.3.tgz#3652ab1c496531852bf55a6bac57af981ebc38ab"
|
||||||
|
integrity sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==
|
||||||
|
dependencies:
|
||||||
|
"@types/unist" "^3.0.0"
|
||||||
|
vfile-message "^4.0.0"
|
||||||
|
|
||||||
vite-node@3.2.4:
|
vite-node@3.2.4:
|
||||||
version "3.2.4"
|
version "3.2.4"
|
||||||
resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-3.2.4.tgz#f3676d94c4af1e76898c162c92728bca65f7bb07"
|
resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-3.2.4.tgz#f3676d94c4af1e76898c162c92728bca65f7bb07"
|
||||||
@@ -2340,6 +2814,11 @@ warning@^4.0.0, warning@^4.0.3:
|
|||||||
dependencies:
|
dependencies:
|
||||||
loose-envify "^1.0.0"
|
loose-envify "^1.0.0"
|
||||||
|
|
||||||
|
web-namespaces@^2.0.0:
|
||||||
|
version "2.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/web-namespaces/-/web-namespaces-2.0.1.tgz#1010ff7c650eccb2592cebeeaf9a1b253fd40692"
|
||||||
|
integrity sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==
|
||||||
|
|
||||||
whatwg-mimetype@^3.0.0:
|
whatwg-mimetype@^3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz#5fa1a7623867ff1af6ca3dc72ad6b8a4208beba7"
|
resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz#5fa1a7623867ff1af6ca3dc72ad6b8a4208beba7"
|
||||||
@@ -2362,3 +2841,8 @@ yaml@^1.10.0:
|
|||||||
version "1.10.2"
|
version "1.10.2"
|
||||||
resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b"
|
resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b"
|
||||||
integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==
|
integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==
|
||||||
|
|
||||||
|
zwitch@^2.0.4:
|
||||||
|
version "2.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-2.0.4.tgz#c827d4b0acb76fc3e685a4c6ec2902d51070e9d7"
|
||||||
|
integrity sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==
|
||||||
|
@@ -82,8 +82,6 @@ describe('Certificates endpoints', () => {
|
|||||||
domain_names: ['test.com"||echo hello-world||\\\\n test.com"'],
|
domain_names: ['test.com"||echo hello-world||\\\\n test.com"'],
|
||||||
meta: {
|
meta: {
|
||||||
dns_challenge: false,
|
dns_challenge: false,
|
||||||
letsencrypt_agree: true,
|
|
||||||
letsencrypt_email: 'admin@example.com',
|
|
||||||
},
|
},
|
||||||
provider: 'letsencrypt',
|
provider: 'letsencrypt',
|
||||||
},
|
},
|
||||||
@@ -97,28 +95,4 @@ describe('Certificates endpoints', () => {
|
|||||||
expect(data.error.message).to.contain('data/domain_names/0 must match pattern');
|
expect(data.error.message).to.contain('data/domain_names/0 must match pattern');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Request Certificate - LE Email Escaped', () => {
|
|
||||||
cy.task('backendApiPost', {
|
|
||||||
token: token,
|
|
||||||
path: '/api/nginx/certificates',
|
|
||||||
data: {
|
|
||||||
domain_names: ['test.com"||echo hello-world||\\\\n test.com"'],
|
|
||||||
meta: {
|
|
||||||
dns_challenge: false,
|
|
||||||
letsencrypt_agree: true,
|
|
||||||
letsencrypt_email: "admin@example.com' --version;echo hello-world",
|
|
||||||
},
|
|
||||||
provider: 'letsencrypt',
|
|
||||||
},
|
|
||||||
returnOnError: true,
|
|
||||||
}).then((data) => {
|
|
||||||
cy.validateSwaggerSchema('post', 400, '/nginx/certificates', data);
|
|
||||||
expect(data).to.have.property('error');
|
|
||||||
expect(data.error).to.have.property('message');
|
|
||||||
expect(data.error).to.have.property('code');
|
|
||||||
expect(data.error.code).to.equal(400);
|
|
||||||
expect(data.error.message).to.contain('data/meta/letsencrypt_email must match pattern');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
@@ -19,8 +19,6 @@ describe('Full Certificate Provisions', () => {
|
|||||||
'website1.example.com'
|
'website1.example.com'
|
||||||
],
|
],
|
||||||
meta: {
|
meta: {
|
||||||
letsencrypt_email: 'admin@example.com',
|
|
||||||
letsencrypt_agree: true,
|
|
||||||
dns_challenge: false
|
dns_challenge: false
|
||||||
},
|
},
|
||||||
provider: 'letsencrypt'
|
provider: 'letsencrypt'
|
||||||
@@ -42,11 +40,9 @@ describe('Full Certificate Provisions', () => {
|
|||||||
'website2.example.com'
|
'website2.example.com'
|
||||||
],
|
],
|
||||||
meta: {
|
meta: {
|
||||||
letsencrypt_email: "admin@example.com",
|
|
||||||
dns_challenge: true,
|
dns_challenge: true,
|
||||||
dns_provider: 'powerdns',
|
dns_provider: 'powerdns',
|
||||||
dns_provider_credentials: 'dns_powerdns_api_url = http://ns1.pdns:8081\r\ndns_powerdns_api_key = npm',
|
dns_provider_credentials: 'dns_powerdns_api_url = http://ns1.pdns:8081\r\ndns_powerdns_api_key = npm',
|
||||||
letsencrypt_agree: true,
|
|
||||||
propagation_seconds: 5,
|
propagation_seconds: 5,
|
||||||
},
|
},
|
||||||
provider: 'letsencrypt'
|
provider: 'letsencrypt'
|
||||||
|
Reference in New Issue
Block a user