Merge branch 'master' into clear-monitor-data

pull/290/head
Ponkhy 3 years ago committed by GitHub
commit b604910bbb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -2,6 +2,13 @@
FROM node:14-alpine3.12 AS release
WORKDIR /app
# split the sqlite install here, so that it can caches the arm prebuilt
RUN apk add --no-cache --virtual .build-deps make g++ python3 python3-dev git && \
ln -s /usr/bin/python3 /usr/bin/python && \
npm install mapbox/node-sqlite3#593c9d && \
apk del .build-deps && \
rm -f /usr/bin/python
# Install apprise
RUN apk add --no-cache python3 py3-cryptography py3-pip py3-six py3-yaml py3-click py3-markdown py3-requests py3-requests-oauthlib
RUN pip3 --no-cache-dir install apprise && \

@ -212,8 +212,8 @@ if (type == "local") {
bash("check=$(docker info)");
bash("if [[ \"$check\" == *\"Is the docker daemon running\"* ]]; then
echo \"Error: docker is not running\"
exit 1
\"echo\" \"Error: docker is not running\"
\"exit\" \"1\"
fi");
if ("$3" != "") {

@ -0,0 +1,3 @@
package-lock.json
test.js
languages/

@ -0,0 +1,78 @@
// Need to use es6 to read language files
import fs from "fs";
import path from "path";
import util from "util";
// https://stackoverflow.com/questions/13786160/copy-folder-recursively-in-node-js
/**
* Look ma, it's cp -R.
* @param {string} src The path to the thing to copy.
* @param {string} dest The path to the new copy.
*/
const copyRecursiveSync = function (src, dest) {
let exists = fs.existsSync(src);
let stats = exists && fs.statSync(src);
let isDirectory = exists && stats.isDirectory();
if (isDirectory) {
fs.mkdirSync(dest);
fs.readdirSync(src).forEach(function (childItemName) {
copyRecursiveSync(path.join(src, childItemName),
path.join(dest, childItemName));
});
} else {
fs.copyFileSync(src, dest);
}
};
console.log(process.argv)
const baseLangCode = process.argv[2] || "zh-HK";
console.log("Base Lang: " + baseLangCode);
fs.rmdirSync("./languages", { recursive: true });
copyRecursiveSync("../../src/languages", "./languages");
const en = (await import("./languages/en.js")).default;
const baseLang = (await import(`./languages/${baseLangCode}.js`)).default;
const files = fs.readdirSync("./languages");
console.log(files);
for (const file of files) {
if (file.endsWith(".js")) {
console.log("Processing " + file);
const lang = await import("./languages/" + file);
let obj;
if (lang.default) {
console.log("is js module");
obj = lang.default;
} else {
console.log("empty file");
obj = {
languageName: "<Your Language name in your language (not in English)>"
};
}
// En first
for (const key in en) {
if (! obj[key]) {
obj[key] = en[key];
}
}
// Base second
for (const key in baseLang) {
if (! obj[key]) {
obj[key] = key;
}
}
const code = "export default " + util.inspect(obj, {
depth: null,
});
fs.writeFileSync(`../../src/languages/${file}`, code);
}
}
fs.rmdirSync("./languages", { recursive: true });
console.log("Done, fix the format by eslint now");

@ -0,0 +1,12 @@
{
"name": "update-language-files",
"type": "module",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC"
}

@ -176,8 +176,8 @@ else
fi
check=$(docker info)
if [[ "$check" == *"Is the docker daemon running"* ]]; then
echo "Error: docker is not running"
exit 1
"echo" "Error: docker is not running"
"exit" "1"
fi
if [ "$3" != "" ]; then
port="$3"

1576
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -1,6 +1,6 @@
{
"name": "uptime-kuma",
"version": "1.5.0",
"version": "1.5.2",
"license": "MIT",
"repository": {
"type": "git",
@ -20,10 +20,11 @@
"update": "",
"build": "vite build",
"vite-preview-dist": "vite preview --host",
"build-docker": "docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma -t louislam/uptime-kuma:1 -t louislam/uptime-kuma:1.5.0 --target release . --push",
"build-docker": "docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma -t louislam/uptime-kuma:1 -t louislam/uptime-kuma:1.5.2 --target release . --push",
"build-docker-nightly": "docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:nightly --target nightly . --push",
"build-docker-nightly-amd64": "docker buildx build --platform linux/amd64 -t louislam/uptime-kuma:nightly-amd64 --target nightly . --push --progress plain",
"setup": "git checkout 1.5.0 && npm install --legacy-peer-deps && npm run build && npm prune",
"build-docker-1.5.0-debian": "docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:1.5.0-debian --target release . --push",
"setup": "git checkout 1.5.2 && npm install --legacy-peer-deps && node node_modules/esbuild/install.js && npm run build && npm prune",
"update-version": "node extra/update-version.js",
"mark-as-nightly": "node extra/mark-as-nightly.js",
"reset-password": "node extra/reset-password.js",
@ -32,15 +33,14 @@
"test-install-script-alpine3": "npm run compile-install-script && docker build --progress plain -f test/test_install_script/alpine3.dockerfile .",
"test-install-script-ubuntu": "npm run compile-install-script && docker build --progress plain -f test/test_install_script/ubuntu.dockerfile .",
"test-install-script-ubuntu1604": "npm run compile-install-script && docker build --progress plain -f test/test_install_script/ubuntu1604.dockerfile .",
"test-install-script-debian": "npm run compile-install-script && docker build --progress plain -f test/test_install_script/debian.dockerfile .",
"simple-dns-server": "node extra/simple-dns-server.js"
"simple-dns-server": "node extra/simple-dns-server.js",
"update-language-files": "cd extra/update-language-files && node index.js %npm_config_base_lang% && eslint ../../src/languages/**.js --fix"
},
"dependencies": {
"@fortawesome/fontawesome-svg-core": "^1.2.36",
"@fortawesome/free-regular-svg-icons": "^5.15.4",
"@fortawesome/free-solid-svg-icons": "^5.15.4",
"@fortawesome/vue-fontawesome": "^3.0.0-4",
"@louislam/better-sqlite3-with-prebuilds": "^7.4.3",
"@popperjs/core": "^2.9.3",
"args-parser": "^1.3.0",
"axios": "^0.21.1",
@ -63,6 +63,7 @@
"redbean-node": "0.1.2",
"socket.io": "^4.1.3",
"socket.io-client": "^4.1.3",
"sqlite3": "github:mapbox/node-sqlite3#593c9d",
"tcp-ping": "^0.1.1",
"v-pagination-3": "^0.1.6",
"vue": "^3.2.2",

@ -13,9 +13,6 @@ class Database {
static async connect() {
const acquireConnectionTimeout = 120 * 1000;
R.useBetterSQLite3 = true;
R.betterSQLite3Options.timeout = acquireConnectionTimeout;
R.setup("sqlite", {
filename: Database.path,
useNullAsDefault: true,
@ -124,11 +121,8 @@ class Database {
return statement !== "";
})
// Use better-sqlite3 to run, prevent "This statement does not return data. Use run() instead"
const db = await this.getBetterSQLite3Database();
for (let statement of statements) {
db.prepare(statement).run();
await R.exec(statement);
}
}

@ -0,0 +1,10 @@
# How to translate
1. Fork this repo.
2. Create a language file. (e.g. `zh-TW.js`) The filename must be ISO language code: http://www.lingoes.net/en/translator/langcode.htm
3. `npm run update-language-files --base-lang=de-DE`
6. Your language file should be filled in. You can translate now.
7. Make a [pull request](https://github.com/louislam/uptime-kuma/pulls) when you have done.
If you do not have programming skills, let me know in [Issues section](https://github.com/louislam/uptime-kuma/issues). I will assist you. 😏

@ -0,0 +1,110 @@
export default {
languageName: "Danish",
Settings: "Indstillinger",
Dashboard: "Dashboard",
"New Update": "Opdatering tilgængelig",
Language: "Sprog",
Appearance: "Udseende",
Theme: "Tema",
General: "Generelt",
Version: "Version",
"Check Update On GitHub": "Tjek efter opdateringer på Github",
List: "Liste",
Add: "Tilføj",
"Add New Monitor": "Tilføj ny Overvåger",
"Quick Stats": "Oversigt",
Up: "Aktiv",
Down: "Inaktiv",
Pending: "Afventer",
Unknown: "Ukendt",
Pause: "Pause",
pauseDashboardHome: "Pauset",
Name: "Navn",
Status: "Status",
DateTime: "Dato / Tid",
Message: "Beskeder",
"No important events": "Inden vigtige begivenheder",
Resume: "Fortsæt",
Edit: "Rediger",
Delete: "Slet",
Current: "Aktuelt",
Uptime: "Oppetid",
"Cert Exp.": "Certifikatets udløb",
days: "Dage",
day: "Dag",
"-day": "-Dage",
hour: "Timer",
"-hour": "-Timer",
checkEverySecond: "Tjek hvert {0} sekund",
"Avg.": "Gennemsnit",
Response: " Respons",
Ping: "Ping",
"Monitor Type": "Overvåger Type",
Keyword: "Nøgleord",
"Friendly Name": "Visningsnavn",
URL: "URL",
Hostname: "Hostname",
Port: "Port",
"Heartbeat Interval": "Taktinterval",
Retries: "Gentagelser",
retriesDescription: "Maksimalt antal gentagelser, før tjenesten markeres som inaktiv og sender en meddelelse.",
Advanced: "Avanceret",
ignoreTLSError: "Ignorere TLS/SSL web fejl",
"Upside Down Mode": "Omvendt tilstand",
upsideDownModeDescription: "Håndter tilstanden omvendt. Hvis tjenesten er tilgængelig, vises den som inaktiv.",
"Max. Redirects": "Maks. Omdirigeringer",
maxRedirectDescription: "Maksimalt antal omdirigeringer, der skal følges. Indstil til 0 for at deaktivere omdirigeringer.",
"Accepted Status Codes": "Tilladte HTTP-Statuskoder",
acceptedStatusCodesDescription: "Vælg de statuskoder, der stadig skal vurderes som vellykkede.",
Save: "Gem",
Notifications: "Underretninger",
"Not available, please setup.": "Ikke tilgængelige, opsæt venligst.",
"Setup Notification": "Opsæt underretninger",
Light: "Lys",
Dark: "Mørk",
Auto: "Auto",
"Theme - Heartbeat Bar": "Tema - Tidslinje",
Normal: "Normal",
Bottom: "Bunden",
None: "Ingen",
Timezone: "Tidszone",
"Search Engine Visibility": "Søgemaskine synlighed",
"Allow indexing": "Tillad indeksering",
"Discourage search engines from indexing site": "Frabed søgemaskiner at indeksere webstedet",
"Change Password": "Ændre adgangskode",
"Current Password": "Nuværende adgangskode",
"New Password": "Ny adgangskode",
"Repeat New Password": "Gentag den nye adgangskode",
passwordNotMatchMsg: "Adgangskoderne er ikke ens.",
"Update Password": "Opdater adgangskode",
"Disable Auth": "Deaktiver autentificering",
"Enable Auth": "Aktiver autentificering",
Logout: "Log ud",
notificationDescription: "Tildel underretninger til Overvåger(e), så denne funktion træder i kraft.",
Leave: "Verlassen",
"I understand, please disable": "Jeg er indforstået, deaktiver venligst",
Confirm: "Bekræft",
Yes: "Ja",
No: "Nej",
Username: "Brugernavn",
Password: "Adgangskode",
"Remember me": "Husk mig",
Login: "Log ind",
"No Monitors, please": "Ingen Overvågere",
"add one": "tilføj en",
"Notification Type": "Underretningstype",
Email: "E-Mail",
Test: "Test",
"Certificate Info": "Certifikatoplysninger",
keywordDescription: "Søg efter et søgeord i almindelig HTML- eller JSON -output. Bemærk, at der skelnes mellem store og små bogstaver.",
deleteMonitorMsg: "Er du sikker på, at du vil slette overvågeren?",
deleteNotificationMsg: "Er du sikker på, at du vil slette denne underretning for alle overvågere? ",
resoverserverDescription: "Cloudflare er standardserveren, den kan til enhver tid ændres.",
"Resolver Server": "Navne-server",
rrtypeDescription: "Vælg den type RR, du vil overvåge.",
"Last Result": "Seneste resultat",
pauseMonitorMsg: "Er du sikker på, at du vil pause Overvågeren?",
"Create your admin account": "Opret din administratorkonto",
"Repeat Password": "Gentag adgangskoden",
"Resource Record Type": "Resource Record Type"
}

@ -93,8 +93,8 @@ export default {
"No Monitors, please": "Keine Monitore, bitte",
"add one": "hinzufügen",
"Notification Type": "Benachrichtigungs Dienst",
"Email": "E-Mail",
"Test": "Test",
Email: "E-Mail",
Test: "Test",
"Certificate Info": "Zertifikatsinfo",
keywordDescription: "Suche nach einen Schlüsselwort in einer schlichten HTML oder JSON Ausgabe. Bitte beachte, es wird in der Groß-/Kleinschreibung unterschieden.",
deleteMonitorMsg: "Bist du sicher das du den Monitor löschen möchtest?",
@ -110,4 +110,7 @@ export default {
"Events": "Ereignisse",
"Heartbeats": "Statistiken",
confirmClearStatisticsMsg: "Bist du sicher das du ALLE Statistiken löschen möchtest?",
"Create your admin account": "Erstelle dein Admin Konto",
"Repeat Password": "Wiederhole das Passwort",
"Resource Record Type": "Resource Record Type"
}

@ -19,4 +19,95 @@ export default {
clearEventsMsg: "Are you sure want to delete all events for this monitor?",
clearHeartbeatsMsg: "Are you sure want to delete all heartbeats for this monitor?",
confirmClearStatisticsMsg: "Are you sure want to delete ALL statistics?",
Settings: "Settings",
Dashboard: "Dashboard",
"New Update": "New Update",
Language: "Language",
Appearance: "Appearance",
Theme: "Theme",
General: "General",
Version: "Version",
"Check Update On GitHub": "Check Update On GitHub",
List: "List",
Add: "Add",
"Add New Monitor": "Add New Monitor",
"Quick Stats": "Quick Stats",
Up: "Up",
Down: "Down",
Pending: "Pending",
Unknown: "Unknown",
Pause: "Pause",
Name: "Name",
Status: "Status",
DateTime: "DateTime",
Message: "Message",
"No important events": "No important events",
Resume: "Resume",
Edit: "Edit",
Delete: "Delete",
Current: "Current",
Uptime: "Uptime",
"Cert Exp.": "Cert Exp.",
days: "days",
day: "day",
"-day": "-day",
hour: "hour",
"-hour": "-hour",
Response: "Response",
Ping: "Ping",
"Monitor Type": "Monitor Type",
Keyword: "Keyword",
"Friendly Name": "Friendly Name",
URL: "URL",
Hostname: "Hostname",
Port: "Port",
"Heartbeat Interval": "Heartbeat Interval",
Retries: "Retries",
Advanced: "Advanced",
"Upside Down Mode": "Upside Down Mode",
"Max. Redirects": "Max. Redirects",
"Accepted Status Codes": "Accepted Status Codes",
Save: "Save",
Notifications: "Notifications",
"Not available, please setup.": "Not available, please setup.",
"Setup Notification": "Setup Notification",
Light: "Light",
Dark: "Dark",
Auto: "Auto",
"Theme - Heartbeat Bar": "Theme - Heartbeat Bar",
Normal: "Normal",
Bottom: "Bottom",
None: "None",
Timezone: "Timezone",
"Search Engine Visibility": "Search Engine Visibility",
"Allow indexing": "Allow indexing",
"Discourage search engines from indexing site": "Discourage search engines from indexing site",
"Change Password": "Change Password",
"Current Password": "Current Password",
"New Password": "New Password",
"Repeat New Password": "Repeat New Password",
"Update Password": "Update Password",
"Disable Auth": "Disable Auth",
"Enable Auth": "Enable Auth",
Logout: "Logout",
Leave: "Leave",
"I understand, please disable": "I understand, please disable",
Confirm: "Confirm",
Yes: "Yes",
No: "No",
Username: "Username",
Password: "Password",
"Remember me": "Remember me",
Login: "Login",
"No Monitors, please": "No Monitors, please",
"add one": "add one",
"Notification Type": "Notification Type",
Email: "Email",
Test: "Test",
"Certificate Info": "Certificate Info",
"Resolver Server": "Resolver Server",
"Resource Record Type": "Resource Record Type",
"Last Result": "Last Result",
"Create your admin account": "Create your admin account",
"Repeat Password": "Repeat Password"
}

@ -93,8 +93,8 @@ export default {
"No Monitors, please": "Pas de monitor, veuillez ",
"add one": "en ajouter un.",
"Notification Type": "Type de notification",
"Email": "Email",
"Test": "Tester",
Email: "Email",
Test: "Tester",
keywordDescription: "Le mot clé sera cherché dans la réponse HTML/JSON reçue du site internet.",
"Certificate Info": "Des informations sur le certificat SSL",
deleteMonitorMsg: "Êtes-vous sûr de vouloir supprimer ce monitor ?",
@ -103,4 +103,8 @@ export default {
"Resource Record Type": "Type d'enregistrement DNS recherché",
resoverserverDescription: "Le DNS de cloudflare est utilisé par défaut, mais vous pouvez le changer si vous le souhaitez.",
rrtypeDescription: "Veuillez séléctionner un type d'enregistrement DNS",
pauseMonitorMsg: "Are you sure want to pause?",
"Last Result": "Last Result",
"Create your admin account": "Create your admin account",
"Repeat Password": "Repeat Password"
}

@ -0,0 +1,110 @@
export default {
languageName: "日本語",
checkEverySecond: "{0}秒ごとにチェックします。",
"Avg.": "平均 ",
retriesDescription: "サービスがダウンとしてマークされ、通知が送信されるまでの最大リトライ数",
ignoreTLSError: "HTTPS ウェブサイトの TLS/SSL エラーを無視する",
upsideDownModeDescription: "ステータスの扱いを逆にします。サービスに到達可能な場合は、DOWNとなる。",
maxRedirectDescription: "フォローするリダイレクトの最大数。リダイレクトを無効にするには0を設定する。",
acceptedStatusCodesDescription: "成功した応答とみなされるステータスコードを選択する。",
passwordNotMatchMsg: "繰り返しのパスワードが一致しません。",
notificationDescription: "監視を機能させるには、監視に通知を割り当ててください。",
keywordDescription: "プレーンHTMLまたはJSON応答でキーワードを検索し、大文字と小文字を区別します",
pauseDashboardHome: "一時停止",
deleteMonitorMsg: "この監視を削除してよろしいですか?",
deleteNotificationMsg: "全ての監視のこの通知を削除してよろしいですか?",
resoverserverDescription: "Cloudflareがデフォルトのサーバーですが、いつでもリゾルバサーバーを変更できます。",
rrtypeDescription: "監視するRRタイプを選択します",
pauseMonitorMsg: "一時停止しますか?",
Settings: "設定",
Dashboard: "ダッシュボード",
"New Update": "New Update",
Language: "言語",
Appearance: "外観",
Theme: "テーマ",
General: "General",
Version: "バージョン",
"Check Update On GitHub": "GitHubでアップデートを確認する",
List: "一覧",
Add: "追加",
"Add New Monitor": "監視の追加",
"Quick Stats": "統計",
Up: "Up",
Down: "Down",
Pending: "中止",
Unknown: "不明",
Pause: "一時停止",
Name: "名前",
Status: "ステータス",
DateTime: "日時",
Message: "メッセージ",
"No important events": "重要なイベントなし",
Resume: "再開",
Edit: "編集",
Delete: "削除",
Current: "現在",
Uptime: "起動時間",
"Cert Exp.": "証明書有効期限",
days: "日間",
day: "日",
"-day": "-日",
hour: "時間",
"-hour": "-時間",
Response: "レスポンス",
Ping: "Ping",
"Monitor Type": "監視タイプ",
Keyword: "キーワード",
"Friendly Name": "Friendly Name",
URL: "URL",
Hostname: "ホスト名",
Port: "ポート",
"Heartbeat Interval": "監視間隔",
Retries: "Retries",
Advanced: "Advanced",
"Upside Down Mode": "Upside Down Mode",
"Max. Redirects": "最大リダイレクト数",
"Accepted Status Codes": "承認されたステータスコード",
Save: "保存",
Notifications: "通知",
"Not available, please setup.": "利用できません。設定してください。",
"Setup Notification": "通知設定",
Light: "Light",
Dark: "Dark",
Auto: "Auto",
"Theme - Heartbeat Bar": "Theme - Heartbeat Bar",
Normal: "通常",
Bottom: "下部",
None: "なし",
Timezone: "タイムゾーン",
"Search Engine Visibility": "検索エンジンでの表示",
"Allow indexing": "インデックス作成を許可する",
"Discourage search engines from indexing site": "検索エンジンにインデックスさせないようにする",
"Change Password": "パスワード変更",
"Current Password": "現在のパスワード",
"New Password": "新しいパスワード",
"Repeat New Password": "確認のため新しいパスワードをもう一度",
"Update Password": "パスワードの更新",
"Disable Auth": "認証の無効化",
"Enable Auth": "認証の有効化",
Logout: "ログアウト",
Leave: "作業を中止する",
"I understand, please disable": "理解した上で無効化する",
Confirm: "確認",
Yes: "はい",
No: "いいえ",
Username: "ユーザー名",
Password: "パスワード",
"Remember me": "パスワードを忘れた場合",
Login: "ログイン",
"No Monitors, please": "監視がありません",
"add one": "add one",
"Notification Type": "通知タイプ",
Email: "Eメール",
Test: "テスト",
"Certificate Info": "証明書情報",
"Resolver Server": "問い合わせ先DNSサーバ",
"Resource Record Type": "DNSレコード設定",
"Last Result": "最終結果",
"Create your admin account": "Create your admin account",
"Repeat Password": "Repeat Password"
}

@ -93,8 +93,8 @@ export default {
"No Monitors, please": "沒有監測器,請",
"add one": "新增",
"Notification Type": "通知類型",
"Email": "電郵",
"Test": "測試",
Email: "電郵",
Test: "測試",
keywordDescription: "搜索 HTML 或 JSON 裡是否有出現關鍵字(注意英文大細階)",
"Certificate Info": "憑證詳細資料",
deleteMonitorMsg: "是否確定刪除這個監測器",
@ -103,4 +103,8 @@ export default {
"Resource Record Type": "DNS 記錄類型",
resoverserverDescription: "預設值為 Cloudflare DNS 伺服器,你可以轉用其他 DNS 伺服器。",
rrtypeDescription: "請選擇 DNS 記錄類型",
pauseMonitorMsg: "Are you sure want to pause?",
"Last Result": "Last Result",
"Create your admin account": "Create your admin account",
"Repeat Password": "Repeat Password"
}

@ -27,6 +27,8 @@ import en from "./languages/en";
import zhHK from "./languages/zh-HK";
import deDE from "./languages/de-DE";
import fr from "./languages/fr";
import ja from "./languages/ja";
import daDK from "./languages/da-DK";
const routes = [
{
@ -94,6 +96,8 @@ const languageList = {
"zh-HK": zhHK,
"de-DE": deDE,
"fr": fr,
"ja": ja,
"da-DK": daDK,
};
const i18n = createI18n({

@ -10,22 +10,22 @@
</div>
<p class="mt-3">
Create your admin account
{{ $t("Create your admin account") }}
</p>
<div class="form-floating">
<input id="floatingInput" v-model="username" type="text" class="form-control" placeholder="Username" required>
<label for="floatingInput">Username</label>
<label for="floatingInput">{{ $t("Username") }}</label>
</div>
<div class="form-floating mt-3">
<input id="floatingPassword" v-model="password" type="password" class="form-control" placeholder="Password" required>
<label for="floatingPassword">Password</label>
<label for="floatingPassword">{{ $t("Password") }}</label>
</div>
<div class="form-floating mt-3">
<input id="repeat" v-model="repeatPassword" type="password" class="form-control" placeholder="Repeat Password" required>
<label for="repeat">Repeat Password</label>
<label for="repeat">{{ $t("Repeat Password") }}</label>
</div>
<button class="w-100 btn btn-primary mt-3" type="submit" :disabled="processing">

Loading…
Cancel
Save