This commit is contained in:
lokeshkato 2026-06-11 10:03:12 +02:00 committed by GitHub
commit 4e3ad6df42
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
31 changed files with 454 additions and 72 deletions

View file

@ -255,13 +255,81 @@ const internalCertificate = {
);
}
const savedRow = await certificateModel
let patchPayload = data;
// Let's Encrypt DNS: allow updating only dns_provider, dns_provider_credentials, propagation_seconds
const isLetsEncryptDns =
row.provider === "letsencrypt" &&
row.meta &&
row.meta.dns_challenge &&
data.meta &&
typeof data.meta === "object";
if (isLetsEncryptDns) {
const mergedMeta = { ...row.meta };
if (data.meta.dns_provider !== undefined) mergedMeta.dns_provider = data.meta.dns_provider;
if (data.meta.dns_provider_credentials !== undefined) {
mergedMeta.dns_provider_credentials = data.meta.dns_provider_credentials;
}
if (data.meta.propagation_seconds !== undefined) {
mergedMeta.propagation_seconds = data.meta.propagation_seconds;
}
patchPayload = { ...data, meta: mergedMeta };
}
let savedRow = await certificateModel
.query()
.patchAndFetchById(row.id, data)
.patchAndFetchById(row.id, patchPayload)
.then(utils.omitRow(omissions()));
if (isLetsEncryptDns) {
// Request a new Cert from LE via DNS challenge. Let the fun begin.
// 1. Find out any hosts that are using any of the hostnames in this cert
// 2. Disable them in nginx temporarily
// 3. Request cert
// 4. Re-instate previously disabled hosts
const inUseResult = await internalHost.getHostsWithDomains(row.domain_names);
await internalCertificate.disableInUseHosts(inUseResult);
const user = await userModel
.query()
.where("is_deleted", 0)
.andWhere("id", row.owner_user_id)
.first();
if (!user || !user.email) {
await internalCertificate.enableInUseHosts(inUseResult);
await internalNginx.reload();
throw new error.ValidationError(
"A valid email address must be set on your user account to use Let's Encrypt",
);
}
const certWithNewMeta = { ...savedRow, meta: patchPayload.meta };
try {
await internalNginx.reload();
await internalCertificate.requestLetsEncryptSslWithDnsChallenge(certWithNewMeta, user.email);
await internalNginx.reload();
await internalCertificate.enableInUseHosts(inUseResult);
} catch (err) {
await internalCertificate.enableInUseHosts(inUseResult);
await internalNginx.reload();
throw err;
}
const certInfo = await internalCertificate.getCertificateInfoFromFile(
`${internalCertificate.getLiveCertPath(row.id)}/fullchain.pem`,
);
savedRow = await certificateModel
.query()
.patchAndFetchById(row.id, {
expires_on: moment(certInfo.dates.to, "X").format("YYYY-MM-DD HH:mm:ss"),
})
.then(utils.omitRow(omissions()));
}
savedRow.meta = internalCertificate.cleanMeta(savedRow.meta);
data.meta = internalCertificate.cleanMeta(data.meta);
data.meta = internalCertificate.cleanMeta(patchPayload.meta);
// Add row.nice_name for custom certs
if (savedRow.provider === "other") {
@ -961,7 +1029,7 @@ const internalCertificate = {
args.push(...adds.args);
logger.info(`Command: ${certbotCommand} ${args ? args.join(" ") : ""}`);
const result = await utils.execFile(certbotCommand, args, adds.opts);
logger.info(result);
return result;

View file

@ -241,10 +241,34 @@ router
}
})
/**
* PUT /api/nginx/certificates/123
*
* Update an existing certificate (e.g. DNS provider API credentials for Let's Encrypt)
*/
.put(async (req, res, next) => {
try {
req.setTimeout(900000); // 15 minutes (update may run certbot renew)
const certificateId = Number.parseInt(req.params.certificate_id, 10);
const payload = await apiValidator(
getValidationSchema("/nginx/certificates/{certID}", "put"),
req.body,
);
const result = await internalCertificate.update(res.locals.access, {
id: certificateId,
meta: payload.meta,
});
res.status(200).send(result);
} catch (err) {
debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err);
}
})
/**
* DELETE /api/nginx/certificates/123
*
* Update and existing certificate
* Delete a certificate
*/
.delete(async (req, res, next) => {
try {

View file

@ -0,0 +1,65 @@
{
"operationId": "updateCertificate",
"summary": "Update a Certificate (e.g. DNS provider credentials for Let's Encrypt)",
"tags": ["certificates"],
"security": [
{
"bearerAuth": ["certificates.manage"]
}
],
"parameters": [
{
"in": "path",
"name": "certID",
"description": "Certificate ID",
"schema": {
"type": "integer",
"minimum": 1
},
"required": true,
"example": 1
}
],
"requestBody": {
"description": "Certificate update payload (DNS provider and credentials for Let's Encrypt DNS certificates)",
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"additionalProperties": false,
"properties": {
"meta": {
"type": "object",
"additionalProperties": false,
"properties": {
"dns_provider": {
"type": "string"
},
"dns_provider_credentials": {
"type": "string"
},
"propagation_seconds": {
"type": "integer",
"minimum": 0
}
}
}
}
}
}
}
},
"responses": {
"200": {
"description": "200 response",
"content": {
"application/json": {
"schema": {
"$ref": "../../../../components/certificate-object.json"
}
}
}
}
}
}

View file

@ -127,6 +127,9 @@
"get": {
"$ref": "./paths/nginx/certificates/certID/get.json"
},
"put": {
"$ref": "./paths/nginx/certificates/certID/put.json"
},
"delete": {
"$ref": "./paths/nginx/certificates/certID/delete.json"
}

View file

@ -51,6 +51,7 @@ export * from "./toggleRedirectionHost";
export * from "./toggleStream";
export * from "./toggleUser";
export * from "./updateAccessList";
export * from "./updateCertificate";
export * from "./updateAuth";
export * from "./updateDeadHost";
export * from "./updateProxyHost";

View file

@ -0,0 +1,20 @@
import * as api from "./base";
import type { Certificate } from "./models";
export interface UpdateCertificatePayload {
meta?: {
dnsProvider?: string;
dnsProviderCredentials?: string;
propagationSeconds?: number;
};
}
export async function updateCertificate(
id: number,
payload: UpdateCertificatePayload,
): Promise<Certificate> {
return await api.put({
url: `/nginx/certificates/${id}`,
data: payload,
});
}

View file

@ -1,7 +1,7 @@
import { IconAlertTriangle } from "@tabler/icons-react";
import CodeEditor from "@uiw/react-textarea-code-editor";
import { Field, useFormikContext } from "formik";
import { useState } from "react";
import { useEffect, useState } from "react";
import Select, { type ActionMeta } from "react-select";
import type { DNSProvider } from "src/api/backend";
import { useDnsProviders } from "src/hooks";
@ -16,8 +16,10 @@ interface DNSProviderOption {
interface Props {
showBoundaryBox?: boolean;
/** When true (edit mode), credentials field is for new credentials only; existing credentials are never displayed */
editMode?: boolean;
}
export function DNSProviderFields({ showBoundaryBox = false }: Props) {
export function DNSProviderFields({ showBoundaryBox = false, editMode = false }: Props) {
const { values, setFieldValue } = useFormikContext();
const { data: dnsProviders, isLoading } = useDnsProviders();
const [dnsProviderId, setDnsProviderId] = useState<string | null>(null);
@ -37,6 +39,16 @@ export function DNSProviderFields({ showBoundaryBox = false }: Props) {
credentials: p.credentials,
})) || [];
const selectedOption = options.find((o) => o.value === v.meta?.dnsProvider) ?? null;
const showCredentials = dnsProviderId ?? v.meta?.dnsProvider;
// When a provider is selected and credentials are empty, use the template from dns-plugins.json as the value
useEffect(() => {
if (selectedOption && (v.meta?.dnsProviderCredentials ?? "") === "") {
setFieldValue("meta.dnsProviderCredentials", selectedOption.credentials);
}
}, [selectedOption, selectedOption?.credentials, v.meta?.dnsProviderCredentials, setFieldValue]);
return (
<div className={showBoundaryBox ? styles.dnsChallengeWarning : undefined}>
<p className="text-warning">
@ -60,6 +72,7 @@ export function DNSProviderFields({ showBoundaryBox = false }: Props) {
placeholder={intl.formatMessage({ id: "certificates.dns.provider.placeholder" })}
isLoading={isLoading}
isSearchable
value={selectedOption}
onChange={handleChange}
options={options}
/>
@ -67,13 +80,13 @@ export function DNSProviderFields({ showBoundaryBox = false }: Props) {
)}
</Field>
{dnsProviderId ? (
{showCredentials ? (
<>
<Field name="meta.dnsProviderCredentials">
{({ field }: any) => (
<div className="mt-3">
<label htmlFor="dnsProviderCredentials" className="form-label">
<T id="certificates.dns.credentials" />
<T id={editMode ? "certificates.dns.credentials-update" : "certificates.dns.credentials"} />
</label>
<CodeEditor
language="bash"

View file

@ -128,6 +128,9 @@
"certificates.dns.credentials-note": {
"defaultMessage": "Този плъгин изисква конфигурационен файл с API токен или други идентификационни данни."
},
"certificates.dns.credentials-update": {
"defaultMessage": "Ново съдържание на файл с идентификационни данни (заменя съществуващото)"
},
"certificates.dns.credentials-warning": {
"defaultMessage": "Тези данни ще бъдат съхранени като обикновен текст в базата и във файл!"
},
@ -146,6 +149,9 @@
"certificates.dns.warning": {
"defaultMessage": "Този раздел изисква познания за Certbot и неговите DNS плъгини. Моля, консултирайте се с документацията."
},
"certificates.edit-dns-settings": {
"defaultMessage": "Редактиране на DNS настройките"
},
"certificates.http.reachability-404": {
"defaultMessage": "Сървър е намерен на този домейн, но не изглежда да е Nginx Proxy Manager. Уверете се, че домейнът сочи към IP адреса, където работи NPM."
},

View file

@ -185,6 +185,9 @@
"certificates.dns.credentials-note": {
"defaultMessage": "Tento doplněk vyžaduje konfigurační soubor obsahující API token nebo jiné přihlašovací údaje vašeho poskytovatele"
},
"certificates.dns.credentials-update": {
"defaultMessage": "Nový obsah souboru s přihlašovacími údaji (nahradí stávající)"
},
"certificates.dns.credentials-warning": {
"defaultMessage": "Tyto údaje budou uloženy v databázi a v souboru jako obyčejný text!"
},
@ -203,6 +206,9 @@
"certificates.dns.warning": {
"defaultMessage": "Tato sekce vyžaduje znalost Certbotu a jeho DNS doplňků. Prosím, podívejte se do dokumentace příslušného doplňku."
},
"certificates.edit-dns-settings": {
"defaultMessage": "Upravit DNS nastavení"
},
"certificates.http.reachability-404": {
"defaultMessage": "Na této doméně byl nalezen server, ale nezdá se, že jde o Nginx Proxy Manager. Ujistěte se, že vaše doména směřuje na IP, kde běží vaše instance NPM."
},

View file

@ -116,6 +116,9 @@
"certificates.dns.credentials-note": {
"defaultMessage": "Dieses Plugin erfordert eine Konfigurationsdatei, die einen API-Token oder andere Anmeldedaten für Ihren Anbieter enthält."
},
"certificates.dns.credentials-update": {
"defaultMessage": "Neuer Inhalt der Anmeldedaten-Datei (ersetzt die bestehende)"
},
"certificates.dns.credentials-warning": {
"defaultMessage": "Diese Daten werden als Klartext in der Datenbank und in einer Datei gespeichert!"
},
@ -131,6 +134,9 @@
"certificates.dns.warning": {
"defaultMessage": "Dieser Abschnitt erfordert einige Kenntnisse über Certbot und seine DNS-Plugins. Bitte konsultieren Sie die jeweilige Plugin-Dokumentation."
},
"certificates.edit-dns-settings": {
"defaultMessage": "DNS-Einstellungen bearbeiten"
},
"certificates.http.reachability-404": {
"defaultMessage": "Unter dieser Domain wurde ein Server gefunden, aber es scheint sich nicht um Nginx Proxy Manager zu handeln. Bitte stellen Sie sicher, dass Ihre Domain auf die IP-Adresse verweist, unter der Ihre NPM-Instanz ausgeführt wird."
},

View file

@ -185,6 +185,9 @@
"certificates.dns.credentials-note": {
"defaultMessage": "This plugin requires a configuration file containing an API token or other credentials for your provider"
},
"certificates.dns.credentials-update": {
"defaultMessage": "New Credentials File Content (replaces existing)"
},
"certificates.dns.credentials-warning": {
"defaultMessage": "This data will be stored as plaintext in the database and in a file!"
},
@ -203,6 +206,9 @@
"certificates.dns.warning": {
"defaultMessage": "This section requires some knowledge about Certbot and its DNS plugins. Please consult the respective plugins documentation."
},
"certificates.edit-dns-settings": {
"defaultMessage": "Edit DNS Settings"
},
"certificates.http.reachability-404": {
"defaultMessage": "There is a server found at this domain but it does not seem to be Nginx Proxy Manager. Please make sure your domain points to the IP where your NPM instance is running."
},

View file

@ -128,6 +128,9 @@
"certificates.dns.credentials-note": {
"defaultMessage": "Este plugin requiere un archivo de configuración que contenga un token de API u otras credenciales para tu proveedor"
},
"certificates.dns.credentials-update": {
"defaultMessage": "Nuevo contenido del archivo de credenciales (reemplaza el existente)"
},
"certificates.dns.credentials-warning": {
"defaultMessage": "¡Estos datos se almacenarán como texto plano en la base de datos y en un archivo!"
},
@ -146,6 +149,9 @@
"certificates.dns.warning": {
"defaultMessage": "Esta sección requiere algunos conocimientos sobre Certbot y sus plugins DNS. Consulta la documentación de los plugins respectivos."
},
"certificates.edit-dns-settings": {
"defaultMessage": "Editar configuración DNS"
},
"certificates.http.reachability-404": {
"defaultMessage": "Se encontró un servidor en este dominio pero no parece ser Nginx Proxy Manager. Asegúrate de que tu dominio apunte a la IP donde se está ejecutando tu instancia de NPM."
},

View file

@ -185,6 +185,9 @@
"certificates.dns.credentials-note": {
"defaultMessage": "This plugin requires a configuration file containing an API token or other credentials for your provider"
},
"certificates.dns.credentials-update": {
"defaultMessage": "New Credentials File Content (replaces existing)"
},
"certificates.dns.credentials-warning": {
"defaultMessage": "This data will be stored as plaintext in the database and in a file!"
},
@ -203,6 +206,9 @@
"certificates.dns.warning": {
"defaultMessage": "This section requires some knowledge about Certbot and its DNS plugins. Please consult the respective plugins documentation."
},
"certificates.edit-dns-settings": {
"defaultMessage": "Edit DNS Settings"
},
"certificates.http.reachability-404": {
"defaultMessage": "There is a server found at this domain but it does not seem to be Nginx Proxy Manager. Please make sure your domain points to the IP where your NPM instance is running."
},

View file

@ -185,6 +185,9 @@
"certificates.dns.credentials-note": {
"defaultMessage": "Ce plugin nécessite un fichier de configuration contenant un jeton d'API ou d'autres informations d'identification pour votre fournisseur."
},
"certificates.dns.credentials-update": {
"defaultMessage": "Nouveau contenu du fichier d'identifiants (remplace l'existant)"
},
"certificates.dns.credentials-warning": {
"defaultMessage": "Ces données seront stockées en clair dans la base de données et dans un fichier!"
},
@ -203,6 +206,9 @@
"certificates.dns.warning": {
"defaultMessage": "Cette section requiert une certaine connaissance de Certbot et de ses plugins DNS. Veuillez consulter la documentation des plugins correspondants."
},
"certificates.edit-dns-settings": {
"defaultMessage": "Modifier les paramètres DNS"
},
"certificates.http.reachability-404": {
"defaultMessage": "Un serveur a été trouvé sur ce domaine, mais il ne semble pas s'agir de Nginx Proxy Manager. Veuillez vérifier que votre domaine pointe bien vers l'adresse IP où votre instance NPM est exécutée."
},

View file

@ -128,6 +128,9 @@
"certificates.dns.credentials-note": {
"defaultMessage": "Éilíonn an breiseán seo comhad cumraíochta ina bhfuil comhartha API nó dintiúir eile do do sholáthraí."
},
"certificates.dns.credentials-update": {
"defaultMessage": "Cuid nua an chomhaid dintiúir (ionann agus an ceann atá ann)"
},
"certificates.dns.credentials-warning": {
"defaultMessage": "Stórálfar an fhaisnéis seo mar théacs simplí sa bhunachar sonraí agus i gcomhad!"
},
@ -146,6 +149,9 @@
"certificates.dns.warning": {
"defaultMessage": "Éilíonn an chuid seo roinnt eolais faoi Certbot agus a bhreiseáin DNS. Féach ar dhoiciméadacht na mbreiseán faoi seach, le do thoil."
},
"certificates.edit-dns-settings": {
"defaultMessage": "Cuir socruithe DNS in eagar"
},
"certificates.http.reachability-404": {
"defaultMessage": "Tá freastalaí aimsithe ag an bhfearann seo ach ní cosúil gur Bainisteoir Proxy Nginx atá ann. Déan cinnte go bhfuil do fhearann ag pointeáil chuig an seoladh IP ina bhfuil d'eispéireas NPM ag rith."
},

View file

@ -185,6 +185,9 @@
"certificates.dns.credentials-note": {
"defaultMessage": "Ez a plugin egy konfigurációs fájlt igényel, amely API tokent vagy egyéb hitelesítő adatokat tartalmaz a szolgáltatóhoz"
},
"certificates.dns.credentials-update": {
"defaultMessage": "Új hitelesítő adatok fájltartalma (lecseréli a meglévőt)"
},
"certificates.dns.credentials-warning": {
"defaultMessage": "Ezek az adatok sima szövegként lesznek tárolva az adatbázisban és egy fájlban!"
},
@ -203,6 +206,9 @@
"certificates.dns.warning": {
"defaultMessage": "Ez a szakasz némi ismeretet igényel a Certbot-ról és a DNS plugin-jeiről. Kérjük, olvassa el a megfelelő plugin dokumentációját."
},
"certificates.edit-dns-settings": {
"defaultMessage": "DNS beállítások szerkesztése"
},
"certificates.http.reachability-404": {
"defaultMessage": "Található szerver ezen a domain-en, de nem úgy tűnik, hogy Nginx Proxy Manager lenne. Kérjük, győződjön meg róla, hogy a domain arra az IP címre mutat, ahol az NPM példánya fut."
},

View file

@ -128,6 +128,9 @@
"certificates.dns.credentials-note": {
"defaultMessage": "Plugin ini memerlukan file konfigurasi yang berisi token API atau kredensial lain untuk penyedia Anda"
},
"certificates.dns.credentials-update": {
"defaultMessage": "Konten file kredensial baru (menggantikan yang ada)"
},
"certificates.dns.credentials-warning": {
"defaultMessage": "Data ini akan disimpan sebagai teks biasa di database dan dalam file!"
},
@ -146,6 +149,9 @@
"certificates.dns.warning": {
"defaultMessage": "Bagian ini memerlukan pengetahuan tentang Certbot dan plugin DNS-nya. Silakan merujuk dokumentasi plugin terkait."
},
"certificates.edit-dns-settings": {
"defaultMessage": "Edit Pengaturan DNS"
},
"certificates.http.reachability-404": {
"defaultMessage": "Ada server yang ditemukan pada domain ini tetapi tampaknya bukan Nginx Proxy Manager. Pastikan domain Anda mengarah ke IP tempat instance NPM berjalan."
},

View file

@ -116,6 +116,9 @@
"certificates.dns.credentials-note": {
"defaultMessage": "Questo plugin richiede un file di configurazione contenente un token API o altre credenziali per il tuo provider"
},
"certificates.dns.credentials-update": {
"defaultMessage": "Nuovo contenuto del file credenziali (sostituisce l'esistente)"
},
"certificates.dns.credentials-warning": {
"defaultMessage": "Questi dati saranno memorizzati in chiaro nel database e in un file!"
},
@ -131,6 +134,9 @@
"certificates.dns.warning": {
"defaultMessage": "Questa sezione richiede conoscenze su Certbot e i relativi plugin DNS. Consulta la documentazione del plugin."
},
"certificates.edit-dns-settings": {
"defaultMessage": "Modifica impostazioni DNS"
},
"certificates.http.reachability-404": {
"defaultMessage": "È stato trovato un server su questo dominio, ma non sembra essere Nginx Proxy Manager. Assicurati che il dominio punti all'IP dove è in esecuzione NPM."
},

View file

@ -116,6 +116,9 @@
"certificates.dns.credentials-note": {
"defaultMessage": "このプラグインはプロバイダーのAPIキーか認証情報を含む設定ファイルが必要です"
},
"certificates.dns.credentials-update": {
"defaultMessage": "新しい認証情報ファイルの内容(既存のものを置き換えます)"
},
"certificates.dns.credentials-warning": {
"defaultMessage": "このデータはファイルとデータベースにプレーンテキストとして保存されます"
},
@ -131,6 +134,9 @@
"certificates.dns.warning": {
"defaultMessage": "このセクションはCertbotとそのDNSプラグインの知識が必要です。各プラグインのドキュメントを参照してください。"
},
"certificates.edit-dns-settings": {
"defaultMessage": "DNS設定を編集"
},
"certificates.http.reachability-404": {
"defaultMessage": "このドメインはNginx Proxy Managerではないサーバーを指しているようです。ドメインがこのNPMインスタンスを指していることを確認してください。"
},

View file

@ -128,6 +128,9 @@
"certificates.dns.credentials-note": {
"defaultMessage": "이 플러그인은 API 토큰 등이 포함된 설정 파일이 필요합니다."
},
"certificates.dns.credentials-update": {
"defaultMessage": "새 자격 증명 파일 내용(기존 항목 대체)"
},
"certificates.dns.credentials-warning": {
"defaultMessage": "입력한 정보는 데이터베이스와 파일에 평문으로 저장됩니다."
},
@ -146,6 +149,9 @@
"certificates.dns.warning": {
"defaultMessage": "이 기능을 사용하려면 Certbot과 DNS 플러그인에 대한 기본적인 이해가 필요합니다. 자세한 내용은 관련 문서를 참고해 주세요."
},
"certificates.edit-dns-settings": {
"defaultMessage": "DNS 설정 편집"
},
"certificates.http.reachability-404": {
"defaultMessage": "해당 도메인에서 서버가 탐지되었지만 Nginx Proxy Manager가 아닌 것으로 보입니다. 도메인이 NPM이 실행 중인 IP를 가리키는지 확인하세요."
},

View file

@ -185,6 +185,9 @@
"certificates.dns.credentials-note": {
"defaultMessage": "Deze plugin vereist een configuratiebestand met een API-token of andere gegevens van je aanbieder."
},
"certificates.dns.credentials-update": {
"defaultMessage": "Nieuwe inhoud van het bestand met inloggegevens (vervangt bestaande)"
},
"certificates.dns.credentials-warning": {
"defaultMessage": "Deze gegevens worden opgeslagen als plaintext in de database en in een bestand!"
},
@ -203,6 +206,9 @@
"certificates.dns.warning": {
"defaultMessage": "Deze sectie vereist wat informatie over Certbot en zijn DNS plugins. Gebruik de documentatie van de bijbehorende plugins."
},
"certificates.edit-dns-settings": {
"defaultMessage": "DNS-instellingen bewerken"
},
"certificates.http.reachability-404": {
"defaultMessage": "Er is een server gevonden op deze domeinnaam, maar dat lijkt niet Nginx Proxy Manager te zijn. Zorg ervoor dat je domein verwijst naar het IP waar je NPM-instantie draait."
},

View file

@ -185,6 +185,9 @@
"certificates.dns.credentials-note": {
"defaultMessage": "Denne pluginen krever en konfigurasjonsfil som inneholder en API-token eller andre legitimasjoner for leverandøren din"
},
"certificates.dns.credentials-update": {
"defaultMessage": "Nytt innhold for legitimasjonsfil (erstatter eksisterende)"
},
"certificates.dns.credentials-warning": {
"defaultMessage": "Disse dataene vil bli lagret som ren tekst i databasen og i en fil!"
},
@ -203,6 +206,9 @@
"certificates.dns.warning": {
"defaultMessage": "Denne seksjonen krever noe kunnskap om Certbot og dets DNS-plugins. Vennligst konsulter dokumentasjonen for de respektive pluginene."
},
"certificates.edit-dns-settings": {
"defaultMessage": "Rediger DNS-innstillinger"
},
"certificates.http.reachability-404": {
"defaultMessage": "Det finnes en server på dette domenet, men det ser ikke ut til å være Nginx Proxy Manager. Vennligst sørg for at domenet ditt peker til IP-en der NPM-instansen kjører."
},

View file

@ -122,6 +122,9 @@
"certificates.dns.credentials-note": {
"defaultMessage": "Ta wtyczka wymaga pliku konfiguracyjnego zawierającego token API lub inne poświadczenia dla twojego dostawcy"
},
"certificates.dns.credentials-update": {
"defaultMessage": "Nowa zawartość pliku poświadczeń (zastępuje istniejącą)"
},
"certificates.dns.credentials-warning": {
"defaultMessage": "Te dane zostaną zapisane jako zwykły tekst w bazie danych i pliku!"
},
@ -137,6 +140,9 @@
"certificates.dns.warning": {
"defaultMessage": "Ta sekcja wymaga pewnej wiedzy na temat Certbot i jego wtyczek DNS. Zapoznaj się z dokumentacją odpowiednich wtyczek."
},
"certificates.edit-dns-settings": {
"defaultMessage": "Edytuj ustawienia DNS"
},
"certificates.http.reachability-404": {
"defaultMessage": "Znaleziono serwer pod tą domeną, ale nie wygląda na to, że jest to Nginx Proxy Manager. Upewnij się, że twoja domena wskazuje na adres IP, gdzie działa twoja instancja NPM."
},

View file

@ -128,6 +128,9 @@
"certificates.dns.credentials-note": {
"defaultMessage": "Este plugin requer um ficheiro de configuração contendo um token API ou outras credenciais do fornecedor DNS."
},
"certificates.dns.credentials-update": {
"defaultMessage": "Novo conteúdo do ficheiro de credenciais (substitui o existente)"
},
"certificates.dns.credentials-warning": {
"defaultMessage": "Estes dados serão guardados em texto simples na base de dados e num ficheiro!"
},
@ -146,6 +149,9 @@
"certificates.dns.warning": {
"defaultMessage": "Esta secção requer conhecimentos sobre o Certbot e os seus plugins DNS. Consulte a documentação dos plugins."
},
"certificates.edit-dns-settings": {
"defaultMessage": "Editar definições DNS"
},
"certificates.http.reachability-404": {
"defaultMessage": "Foi encontrado um servidor neste domínio, mas não parece ser o Nginx Proxy Manager. Certifique-se de que o domínio aponta para o IP onde a sua instância está a correr."
},

View file

@ -185,6 +185,9 @@
"certificates.dns.credentials-note": {
"defaultMessage": "Этот плагин требует файл конфигурации, содержащий API-токен или другие учётные данные вашего провайдера"
},
"certificates.dns.credentials-update": {
"defaultMessage": "Новое содержимое файла учётных данных (заменяет существующее)"
},
"certificates.dns.credentials-warning": {
"defaultMessage": "Эти данные будут храниться в незашифрованном виде в базе данных и файле!"
},
@ -203,6 +206,9 @@
"certificates.dns.warning": {
"defaultMessage": "Этот раздел требует знаний о Certbot и его DNS-плагинах. Пожалуйста, обратитесь к документации соответствующих плагинов."
},
"certificates.edit-dns-settings": {
"defaultMessage": "Изменить настройки DNS"
},
"certificates.http.reachability-404": {
"defaultMessage": "На этом домене найден сервер, но, похоже, это не Nginx Proxy Manager. Убедитесь, что ваш домен указывает на IP-адрес, где запущен ваш экземпляр NPM."
},

View file

@ -185,6 +185,9 @@
"certificates.dns.credentials-note": {
"defaultMessage": "Tento doplnok vyžaduje konfiguračný súbor obsahujúci API token alebo iné prihlasovacie údaje vášho poskytovateľa"
},
"certificates.dns.credentials-update": {
"defaultMessage": "Nový obsah súboru s prihlasovacími údajmi (nahradí existujúci)"
},
"certificates.dns.credentials-warning": {
"defaultMessage": "Tieto údaje budú uložené v databáze a v súbore ako obyčajný text!"
},
@ -203,6 +206,9 @@
"certificates.dns.warning": {
"defaultMessage": "Táto sekcia vyžaduje znalosť Certbotu a jeho DNS doplnkov. Prosím, pozrite si dokumentáciu príslušného doplnku."
},
"certificates.edit-dns-settings": {
"defaultMessage": "Upraviť nastavenia DNS"
},
"certificates.http.reachability-404": {
"defaultMessage": "Na tejto doméne bol nájdený server, ale nezdá sa, že ide o Nginx Proxy Manager. Uistite sa, že vaša doména smeruje na IP, kde beží vaša inštancia NPM."
},

View file

@ -128,6 +128,9 @@
"certificates.dns.credentials-note": {
"defaultMessage": "Bu eklenti, sağlayıcınız için bir API token'ı veya diğer kimlik bilgilerini içeren bir yapılandırma dosyası gerektirir"
},
"certificates.dns.credentials-update": {
"defaultMessage": "Yeni kimlik bilgileri dosyası içeriği (mevcut olanın yerini alır)"
},
"certificates.dns.credentials-warning": {
"defaultMessage": "Bu veriler veritabanında ve bir dosyada düz metin olarak saklanacak!"
},
@ -146,6 +149,9 @@
"certificates.dns.warning": {
"defaultMessage": "Bu bölüm Certbot ve DNS eklentileri hakkında bazı bilgiler gerektirir. Lütfen ilgili eklenti dokümantasyonuna bakın."
},
"certificates.edit-dns-settings": {
"defaultMessage": "DNS ayarlarını düzenle"
},
"certificates.http.reachability-404": {
"defaultMessage": "Bu alan adında bir sunucu bulundu ancak Nginx Proxy Manager gibi görünmüyor. Lütfen alan adınızın NPM örneğinizin çalıştığı IP'ye işaret ettiğinden emin olun."
},

View file

@ -116,6 +116,9 @@
"certificates.dns.credentials-note": {
"defaultMessage": "Plugin này yêu cầu tệp cấu hình chứa mã thông báo API hoặc thông tin xác thực khác cho nhà cung cấp của bạn"
},
"certificates.dns.credentials-update": {
"defaultMessage": "Nội dung file thông tin xác thực mới (thay thế nội dung hiện có)"
},
"certificates.dns.credentials-warning": {
"defaultMessage": "Dữ liệu này sẽ được lưu trữ dưới dạng bản rõ trong cơ sở dữ liệu và trong một tệp!"
},
@ -131,6 +134,9 @@
"certificates.dns.warning": {
"defaultMessage": "Phần này yêu cầu một số kiến thức về Certbot và các plugin DNS của nó. Vui lòng tham khảo tài liệu plugin tương ứng."
},
"certificates.edit-dns-settings": {
"defaultMessage": "Chỉnh sửa cài đặt DNS"
},
"certificates.http.reachability-404": {
"defaultMessage": "Có một máy chủ được tìm thấy ở miền này nhưng có vẻ như nó không phải là NPM. Vui lòng đảm bảo tên miền của bạn trỏ đến IP nơi phiên bản NPM của bạn đang chạy."
},

View file

@ -116,6 +116,9 @@
"certificates.dns.credentials-note": {
"defaultMessage": "此插件需要一个包含 API 令牌或提供商其他凭证的配置文件"
},
"certificates.dns.credentials-update": {
"defaultMessage": "新凭证文件内容(将替换现有内容)"
},
"certificates.dns.credentials-warning": {
"defaultMessage": "此数据将以明文形式存储在数据库和文件中!"
},
@ -131,6 +134,9 @@
"certificates.dns.warning": {
"defaultMessage": "本节需要您具备一些关于 Certbot 及其 DNS 插件的知识,请参阅相应插件的官方文档。"
},
"certificates.edit-dns-settings": {
"defaultMessage": "编辑 DNS 设置"
},
"certificates.http.reachability-404": {
"defaultMessage": "在此域名下找到了一个服务器,但它似乎不是 Nginx 代理管理器。请确保您的域名指向 NPM 实例运行的 IP 地址。"
},

View file

@ -4,57 +4,92 @@ import { Form, Formik, Field } from "formik";
import { type ReactNode, useState } from "react";
import { Alert } from "react-bootstrap";
import Modal from "react-bootstrap/Modal";
import { createCertificate } from "src/api/backend";
import type { Certificate } from "src/api/backend";
import { createCertificate, updateCertificate } from "src/api/backend";
import { Button, DNSProviderFields, DomainNamesField } from "src/components";
import { T } from "src/locale";
import { showObjectSuccess } from "src/notifications";
const showDNSCertificateModal = () => {
EasyModal.show(DNSCertificateModal);
export interface DNSCertificateModalProps {
certificate?: Certificate;
}
interface Props extends InnerModalProps, DNSCertificateModalProps {}
const showDNSCertificateModal = (certificate?: Certificate) => {
EasyModal.show(DNSCertificateModal, { certificate });
};
const DNSCertificateModal = EasyModal.create(({ visible, remove }: InnerModalProps) => {
const queryClient = useQueryClient();
const [errorMsg, setErrorMsg] = useState<ReactNode | null>(null);
const [isSubmitting, setIsSubmitting] = useState(false);
const onSubmit = async (values: any, { setSubmitting }: any) => {
if (isSubmitting) return;
setIsSubmitting(true);
setErrorMsg(null);
try {
await createCertificate(values);
showObjectSuccess("certificate", "saved");
remove();
} catch (err: any) {
setErrorMsg(<T id={err.message} />);
}
queryClient.invalidateQueries({ queryKey: ["certificates"] });
setIsSubmitting(false);
setSubmitting(false);
const getInitialValues = (certificate?: Certificate) => {
if (certificate) {
return {
id: certificate.id,
domainNames: certificate.domainNames ?? [],
provider: "letsencrypt" as const,
meta: {
dnsChallenge: true,
dnsProvider: certificate.meta?.dnsProvider ?? "",
dnsProviderCredentials: (certificate.meta?.dnsProviderCredentials as string) ?? "",
propagationSeconds: certificate.meta?.propagationSeconds ?? undefined,
keyType: (certificate.meta?.keyType as string) ?? "ecdsa",
},
};
}
return {
domainNames: [] as string[],
provider: "letsencrypt" as const,
meta: {
dnsChallenge: true,
keyType: "ecdsa",
},
};
};
return (
<Modal show={visible} onHide={remove}>
<Formik
initialValues={
{
domainNames: [],
provider: "letsencrypt",
const DNSCertificateModal = EasyModal.create(({ visible, remove, certificate }: Props) => {
const queryClient = useQueryClient();
const [errorMsg, setErrorMsg] = useState<ReactNode | null>(null);
const [isSubmitting, setIsSubmitting] = useState(false);
const isEdit = Boolean(certificate?.id);
const onSubmit = async (values: any, { setSubmitting }: any) => {
if (isSubmitting) return;
setIsSubmitting(true);
setErrorMsg(null);
try {
if (isEdit && values.id) {
await updateCertificate(values.id, {
meta: {
dnsChallenge: true,
keyType: "ecdsa",
dnsProvider: values.meta?.dnsProvider,
dnsProviderCredentials: values.meta?.dnsProviderCredentials,
propagationSeconds: values.meta?.propagationSeconds,
},
} as any
});
} else {
await createCertificate(values);
}
onSubmit={onSubmit}
>
showObjectSuccess("certificate", "saved");
remove();
} catch (err: any) {
setErrorMsg(<T id={err.message} />);
}
queryClient.invalidateQueries({ queryKey: ["certificates"] });
setIsSubmitting(false);
setSubmitting(false);
};
return (
<Modal show={visible} onHide={remove}>
<Formik
initialValues={getInitialValues(certificate) as any}
onSubmit={onSubmit}
enableReinitialize={isEdit}
>
{() => (
<Form>
<Modal.Header closeButton>
<Modal.Title>
<T id="object.add" tData={{ object: "lets-encrypt-via-dns" }} />
<T id={isEdit ? "object.edit" : "object.add"} tData={{ object: "lets-encrypt-via-dns" }} />
</Modal.Title>
</Modal.Header>
<Modal.Body className="p-0">
@ -63,32 +98,36 @@ const DNSCertificateModal = EasyModal.create(({ visible, remove }: InnerModalPro
</Alert>
<div className="card m-0 border-0">
<div className="card-body">
<DomainNamesField isWildcardPermitted dnsProviderWildcardSupported />
<Field name="meta.keyType">
{({ field }: any) => (
<div className="mb-3">
<label htmlFor="keyType" className="form-label">
<T id="certificates.key-type" />
</label>
<select
id="keyType"
className="form-select"
{...field}
>
<option value="rsa">
<T id="certificates.key-type-rsa" />
</option>
<option value="ecdsa">
<T id="certificates.key-type-ecdsa" />
</option>
</select>
<small className="form-text text-muted">
<T id="certificates.key-type-description" />
</small>
</div>
)}
</Field>
<DNSProviderFields />
{!isEdit && (
<>
<DomainNamesField isWildcardPermitted dnsProviderWildcardSupported />
<Field name="meta.keyType">
{({ field }: any) => (
<div className="mb-3">
<label htmlFor="keyType" className="form-label">
<T id="certificates.key-type" />
</label>
<select
id="keyType"
className="form-select"
{...field}
>
<option value="rsa">
<T id="certificates.key-type-rsa" />
</option>
<option value="ecdsa">
<T id="certificates.key-type-ecdsa" />
</option>
</select>
<small className="form-text text-muted">
<T id="certificates.key-type-description" />
</small>
</div>
)}
</Field>
</>
)}
<DNSProviderFields editMode={isEdit} />
</div>
</div>
</Modal.Body>

View file

@ -1,4 +1,4 @@
import { IconDotsVertical, IconDownload, IconRefresh, IconTrash } from "@tabler/icons-react";
import { IconDotsVertical, IconDownload, IconEdit, IconRefresh, IconTrash } from "@tabler/icons-react";
import { createColumnHelper, getCoreRowModel, useReactTable } from "@tanstack/react-table";
import { useMemo } from "react";
import type { Certificate } from "src/api/backend";
@ -127,6 +127,23 @@ export default function Table({ data, isFetching, onDelete, onRenew, onDownload,
<IconRefresh size={16} />
<T id="action.renew" />
</a>
{info.row.original.provider === "letsencrypt" &&
info.row.original.meta?.dnsChallenge &&
info.row.original.meta?.dnsProvider && (
<HasPermission section={CERTIFICATES} permission={MANAGE} hideError>
<a
className="dropdown-item"
href="#"
onClick={(e) => {
e.preventDefault();
showDNSCertificateModal(info.row.original);
}}
>
<IconEdit size={16} />
<T id="certificates.edit-dns-settings" />
</a>
</HasPermission>
)}
<HasPermission section={CERTIFICATES} permission={MANAGE} hideError>
<a
className="dropdown-item"