mirror of
https://github.com/louislam/uptime-kuma.git
synced 2025-08-12 04:12:25 +08:00
Merge branch 'louislam:master' into master
This commit is contained in:
@@ -22,6 +22,18 @@ textarea.form-control {
|
||||
width: 10px;
|
||||
}
|
||||
|
||||
.list-group {
|
||||
border-radius: 0.75rem;
|
||||
|
||||
.dark & {
|
||||
.list-group-item {
|
||||
background-color: $dark-bg;
|
||||
color: $dark-font-color;
|
||||
border-color: $dark-border-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: #ccc;
|
||||
border-radius: 20px;
|
||||
@@ -412,6 +424,10 @@ textarea.form-control {
|
||||
background-color: rgba(239, 239, 239, 0.7);
|
||||
border-radius: 8px;
|
||||
|
||||
&.no-bg {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
outline: 0 solid #eee;
|
||||
background-color: rgba(245, 245, 245, 0.9);
|
||||
|
@@ -11,23 +11,23 @@
|
||||
<table class="text-start">
|
||||
<tbody>
|
||||
<tr class="my-3">
|
||||
<td class="px-3">Subject:</td>
|
||||
<td class="px-3">{{ $t("Subject:") }}</td>
|
||||
<td>{{ formatSubject(cert.subject) }}</td>
|
||||
</tr>
|
||||
<tr class="my-3">
|
||||
<td class="px-3">Valid To:</td>
|
||||
<td class="px-3">{{ $t("Valid To:") }}</td>
|
||||
<td><Datetime :value="cert.validTo" /></td>
|
||||
</tr>
|
||||
<tr class="my-3">
|
||||
<td class="px-3">Days Remaining:</td>
|
||||
<td class="px-3">{{ $t("Days Remaining:") }}</td>
|
||||
<td>{{ cert.daysRemaining }}</td>
|
||||
</tr>
|
||||
<tr class="my-3">
|
||||
<td class="px-3">Issuer:</td>
|
||||
<td class="px-3">{{ $t("Issuer:") }}</td>
|
||||
<td>{{ formatSubject(cert.issuer) }}</td>
|
||||
</tr>
|
||||
<tr class="my-3">
|
||||
<td class="px-3">Fingerprint:</td>
|
||||
<td class="px-3">{{ $t("Fingerprint:") }}</td>
|
||||
<td>{{ cert.fingerprint }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
@@ -36,7 +36,7 @@
|
||||
</div>
|
||||
|
||||
<div v-if="$root.userHeartbeatBar == 'bottom'" class="row">
|
||||
<div class="col-12">
|
||||
<div class="col-12 bottom-style">
|
||||
<HeartbeatBar size="small" :monitor-id="item.id" />
|
||||
</div>
|
||||
</div>
|
||||
@@ -203,9 +203,16 @@ export default {
|
||||
}
|
||||
|
||||
.tags {
|
||||
padding-left: 62px;
|
||||
margin-top: 4px;
|
||||
padding-left: 67px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
.bottom-style {
|
||||
padding-left: 67px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
@@ -20,11 +20,16 @@
|
||||
</div>
|
||||
|
||||
<div v-if="errorMessage" class="mt-3">
|
||||
Message:
|
||||
{{ $t("Message:") }}
|
||||
<textarea v-model="errorMessage" class="form-control" readonly></textarea>
|
||||
</div>
|
||||
|
||||
<p v-if="installed === false">(Download cloudflared from <a href="https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/installation/">Cloudflare Website</a>)</p>
|
||||
<i18n-t v-if="installed === false" tag="p" keypath="wayToGetCloudflaredURL">
|
||||
<a
|
||||
href="https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/installation/"
|
||||
target="_blank"
|
||||
>{{ $t("cloudflareWebsite") }}</a>
|
||||
</i18n-t>
|
||||
</div>
|
||||
|
||||
<!-- If installed show token input -->
|
||||
@@ -44,7 +49,7 @@
|
||||
<span v-if="!running" class="remove-token" @click="removeToken">{{ $t("Remove Token") }}</span>
|
||||
</div>
|
||||
|
||||
Don't know how to get the token? Please read the guide:<br />
|
||||
{{ $t("Don't know how to get the token? Please read the guide:") }}<br />
|
||||
<a href="https://github.com/louislam/uptime-kuma/wiki/Reverse-Proxy-with-Cloudflare-Tunnel" target="_blank">
|
||||
https://github.com/louislam/uptime-kuma/wiki/Reverse-Proxy-with-Cloudflare-Tunnel
|
||||
</a>
|
||||
@@ -61,7 +66,7 @@
|
||||
</button>
|
||||
|
||||
<Confirm ref="confirmStop" btn-style="btn-danger" :yes-text="$t('Stop') + ' cloudflared'" :no-text="$t('Cancel')" @yes="stop">
|
||||
The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.
|
||||
{{ $t("The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.") }}
|
||||
|
||||
<div class="mt-3">
|
||||
<label for="current-password2" class="form-label">
|
||||
@@ -79,10 +84,10 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h4 class="mt-4">Other Software</h4>
|
||||
<h4 class="mt-4">{{ $t("Other Software") }}</h4>
|
||||
<div>
|
||||
For example: nginx, Apache and Traefik. <br />
|
||||
Please read <a href="https://github.com/louislam/uptime-kuma/wiki/Reverse-Proxy" target="_blank">https://github.com/louislam/uptime-kuma/wiki/Reverse-Proxy</a>.
|
||||
{{ $t("For example: nginx, Apache and Traefik.") }} <br />
|
||||
{{ $t("Please read") }} <a href="https://github.com/louislam/uptime-kuma/wiki/Reverse-Proxy" target="_blank">https://github.com/louislam/uptime-kuma/wiki/Reverse-Proxy</a>.
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
@@ -37,6 +37,8 @@ import {
|
||||
faPen,
|
||||
faExternalLinkSquareAlt,
|
||||
faSpinner,
|
||||
faUndo,
|
||||
faPlusCircle,
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
|
||||
library.add(
|
||||
@@ -73,6 +75,8 @@ library.add(
|
||||
faPen,
|
||||
faExternalLinkSquareAlt,
|
||||
faSpinner,
|
||||
faUndo,
|
||||
faPlusCircle,
|
||||
);
|
||||
|
||||
export { FontAwesomeIcon };
|
||||
|
@@ -331,21 +331,21 @@ export default {
|
||||
dark: "dark",
|
||||
Post: "Post",
|
||||
"Please input title and content": "Please input title and content",
|
||||
"Created": "Created",
|
||||
Created: "Created",
|
||||
"Last Updated": "Last Updated",
|
||||
"Unpin": "Unpin",
|
||||
Unpin: "Unpin",
|
||||
"Switch to Light Theme": "Switch to Light Theme",
|
||||
"Switch to Dark Theme": "Switch to Dark Theme",
|
||||
"Show Tags": "Show Tags",
|
||||
"Hide Tags": "Hide Tags",
|
||||
"Description": "Description",
|
||||
Description: "Description",
|
||||
"No monitors available.": "No monitors available.",
|
||||
"Add one": "Add one",
|
||||
"No Monitors": "No Monitors",
|
||||
"Untitled Group": "Untitled Group",
|
||||
"Services": "Services",
|
||||
"Discard": "Discard",
|
||||
"Cancel": "Cancel",
|
||||
Services: "Services",
|
||||
Discard: "Discard",
|
||||
Cancel: "Cancel",
|
||||
"Powered by": "Powered by",
|
||||
shrinkDatabaseDescription: "Trigger database VACUUM for SQLite. If your database is created after 1.10.0, AUTO_VACUUM is already enabled and this action is not needed.",
|
||||
serwersms: "SerwerSMS.pl",
|
||||
@@ -385,4 +385,67 @@ export default {
|
||||
proxyDescription: "Proxies must be assigned to a monitor to function.",
|
||||
enableProxyDescription: "This proxy will not effect on monitor requests until it is activated. You can control temporarily disable the proxy from all monitors by activation status.",
|
||||
setAsDefaultProxyDescription: "This proxy will be enabled by default for new monitors. You can still disable the proxy separately for each monitor.",
|
||||
"Certificate Chain": "Certificate Chain",
|
||||
Valid: "Valid",
|
||||
Invalid: "Invalid",
|
||||
AccessKeyId: "AccessKey ID",
|
||||
SecretAccessKey: "AccessKey Secret",
|
||||
PhoneNumbers: "PhoneNumbers",
|
||||
TemplateCode: "TemplateCode",
|
||||
SignName: "SignName",
|
||||
"Sms template must contain parameters: ": "Sms template must contain parameters: ",
|
||||
"Bark Endpoint": "Bark Endpoint",
|
||||
WebHookUrl: "WebHookUrl",
|
||||
SecretKey: "SecretKey",
|
||||
"For safety, must use secret key": "For safety, must use secret key",
|
||||
"Device Token": "Device Token",
|
||||
Platform: "Platform",
|
||||
iOS: "iOS",
|
||||
Android: "Android",
|
||||
Huawei: "Huawei",
|
||||
High: "High",
|
||||
Retry: "Retry",
|
||||
Topic: "Topic",
|
||||
"WeCom Bot Key": "WeCom Bot Key",
|
||||
"Setup Proxy": "Setup Proxy",
|
||||
"Proxy Protocol": "Proxy Protocol",
|
||||
"Proxy Server": "Proxy Server",
|
||||
"Proxy server has authentication": "Proxy server has authentication",
|
||||
User: "User",
|
||||
Installed: "Installed",
|
||||
"Not installed": "Not installed",
|
||||
Running: "Running",
|
||||
"Not running": "Not running",
|
||||
"Remove Token": "Remove Token",
|
||||
Start: "Start",
|
||||
Stop: "Stop",
|
||||
"Uptime Kuma": "Uptime Kuma",
|
||||
"Add New Status Page": "Add New Status Page",
|
||||
Slug: "Slug",
|
||||
"Accept characters:": "Accept characters:",
|
||||
"startOrEndWithOnly": "Start or end with {0} only",
|
||||
"No consecutive dashes": "No consecutive dashes",
|
||||
Next: "Next",
|
||||
"The slug is already taken. Please choose another slug.": "The slug is already taken. Please choose another slug.",
|
||||
"No Proxy": "No Proxy",
|
||||
"HTTP Basic Auth": "HTTP Basic Auth",
|
||||
"New Status Page": "New Status Page",
|
||||
"Page Not Found": "Page Not Found",
|
||||
"Reverse Proxy": "Reverse Proxy",
|
||||
Backup: "Backup",
|
||||
About: "About",
|
||||
wayToGetCloudflaredURL: "(Download cloudflared from {0})",
|
||||
cloudflareWebsite: "Cloudflare Website",
|
||||
"Message:": "Message:",
|
||||
"Don't know how to get the token? Please read the guide:": "Don't know how to get the token? Please read the guide:",
|
||||
"The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.",
|
||||
"Other Software": "Other Software",
|
||||
"For example: nginx, Apache and Traefik.": "For example: nginx, Apache and Traefik.",
|
||||
"Please read": "Please read",
|
||||
"Subject:": "Subject:",
|
||||
"Valid To:": "Valid To:",
|
||||
"Days Remaining:": "Days Remaining:",
|
||||
"Issuer:": "Issuer:",
|
||||
"Fingerprint:": "Fingerprint:",
|
||||
"No status pages": "No status pages",
|
||||
};
|
||||
|
@@ -88,8 +88,8 @@ export default {
|
||||
Dark: "黑暗",
|
||||
Auto: "自动",
|
||||
"Theme - Heartbeat Bar": "主题 - 心跳栏",
|
||||
Normal: "正常显示",
|
||||
Bottom: "靠下显示",
|
||||
Normal: "正常", // 此处还供 Gorush 的通知优先级功能使用,不应翻译为“正常显示”
|
||||
Bottom: "靠下",
|
||||
None: "不显示",
|
||||
Timezone: "时区",
|
||||
"Search Engine Visibility": "搜索引擎可见性",
|
||||
@@ -373,4 +373,80 @@ export default {
|
||||
"For safety, must use secret key": "出于安全考虑,必须使用加签密钥",
|
||||
WeCom: "企业微信群机器人",
|
||||
"WeCom Bot Key": "企业微信群机器人 Key",
|
||||
PushByTechulus: "Push by Techulus",
|
||||
gorush: "Gorush",
|
||||
alerta: "Alerta",
|
||||
alertaApiEndpoint: "API 接入点",
|
||||
alertaEnvironment: "环境参数",
|
||||
alertaApiKey: "API Key",
|
||||
alertaAlertState: "报警时的严重性",
|
||||
alertaRecoverState: "恢复后的严重性",
|
||||
deleteStatusPageMsg: "您确认要删除此状态页吗?",
|
||||
Proxies: "代理",
|
||||
default: "默认",
|
||||
enabled: "启用",
|
||||
setAsDefault: "设为默认",
|
||||
deleteProxyMsg: "您确认要在所有监控项中删除此代理吗?",
|
||||
proxyDescription: "代理必须配置到至少一个监控项后才会工作。",
|
||||
enableProxyDescription: "此代理必须启用才能对监控项的网络请求起作用。您可以通过修改激活状态,临时在所有监控项中禁用此代理。",
|
||||
setAsDefaultProxyDescription: "此代理会对新创建的监控项默认激活,您仍可以在监控项配置中单独禁用此代理。",
|
||||
"Proxy Protocol": "代理协议",
|
||||
"Proxy Server": "代理服务器",
|
||||
"Server Address": "服务器地址",
|
||||
"Certificate Chain": "证书链",
|
||||
Valid: "有效",
|
||||
Invalid: "无效",
|
||||
AccessKeyId: "AccessKey ID",
|
||||
SecretAccessKey: "AccessKey Secret",
|
||||
/* 以下为阿里云短信服务 API Dysms#SendSms 的参数 */
|
||||
PhoneNumbers: "PhoneNumbers",
|
||||
TemplateCode: "TemplateCode",
|
||||
SignName: "SignName",
|
||||
/* 以上为阿里云短信服务 API Dysms#SendSms 的参数 */
|
||||
"Bark Endpoint": "Bark 接入点",
|
||||
"Device Token": "Apple Device Token",
|
||||
Platform: "平台",
|
||||
iOS: "iOS",
|
||||
Android: "Android",
|
||||
Huawei: "华为",
|
||||
High: "高",
|
||||
Retry: "重试次数",
|
||||
Topic: "Gorush Topic",
|
||||
"Setup Proxy": "设置代理",
|
||||
"Proxy server has authentication": "代理服务器启用了身份验证功能",
|
||||
User: "用户名",
|
||||
Installed: "已安装",
|
||||
"Not installed": "未安装",
|
||||
Running: "运行中",
|
||||
"Not running": "未运行",
|
||||
"Message:": "信息:",
|
||||
wayToGetCloudflaredURL: "(可从 {0} 下载 cloudflared)",
|
||||
cloudflareWebsite: "Cloudflare 网站",
|
||||
"Don't know how to get the token? Please read the guide:": "不知道如何获取 Token?请阅读指南:",
|
||||
"The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "如果您正在通过 Cloudflare Tunnel 访问网站,则停止可能会导致当前连接断开。您确定要停止吗?请输入密码以确认。",
|
||||
"Other Software": "其他软件",
|
||||
"For example: nginx, Apache and Traefik.": "例如:nginx、Apache 和 Traefik。",
|
||||
"Please read": "请阅读",
|
||||
"Remove Token": "移除 Token",
|
||||
Start: "启动",
|
||||
Stop: "停止",
|
||||
"Uptime Kuma": "Uptime Kuma",
|
||||
"Add New Status Page": "添加新的状态页",
|
||||
Slug: "路径",
|
||||
"Accept characters:": "可接受的字符:",
|
||||
"startOrEndWithOnly": "开头和结尾必须为 {0}",
|
||||
"No consecutive dashes": "不能有连续的破折号",
|
||||
Next: "下一步",
|
||||
"The slug is already taken. Please choose another slug.": "该路径已被使用。请选择其他路径。",
|
||||
"No Proxy": "无代理",
|
||||
"HTTP Basic Auth": "HTTP 基础身份验证",
|
||||
"New Status Page": "新的状态页",
|
||||
"Page Not Found": "状态页未找到",
|
||||
"Reverse Proxy": "反向代理",
|
||||
"Subject:": "颁发给:",
|
||||
"Valid To:": "有效期至:",
|
||||
"Days Remaining:": "剩余有效天数:",
|
||||
"Issuer:": "颁发者:",
|
||||
"Fingerprint:": "指纹:",
|
||||
"No status pages": "无状态页",
|
||||
};
|
||||
|
@@ -200,4 +200,182 @@ export default {
|
||||
line: "Line Messenger",
|
||||
mattermost: "Mattermost",
|
||||
deleteStatusPageMsg: "是否確定刪除這個 Status Page?",
|
||||
"Push URL": "推送網址",
|
||||
needPushEvery: "您應每 {0} 秒呼叫此網址。",
|
||||
pushOptionalParams: "選填參數:{0}",
|
||||
defaultNotificationName: "我的 {notification} 通知 ({number})",
|
||||
here: "此處",
|
||||
Required: "必填",
|
||||
"Bot Token": "機器人權杖",
|
||||
wayToGetTelegramToken: "您可以從 {0} 取得 Token。",
|
||||
"Chat ID": "聊天 ID",
|
||||
supportTelegramChatID: "支援 對話/群組/頻道的聊天 ID",
|
||||
wayToGetTelegramChatID: "傳送訊息給機器人,並前往以下網址以取得您的 chat ID:",
|
||||
"YOUR BOT TOKEN HERE": "在此填入您的機器人權杖",
|
||||
chatIDNotFound: "找不到 Chat ID;請先傳送訊息給機器人",
|
||||
"Post URL": "Post 網址",
|
||||
"Content Type": "Content Type",
|
||||
webhookJsonDesc: "{0} 適合任何現代的 HTTP 伺服器,如 Express.js",
|
||||
webhookFormDataDesc: "{multipart} 適合 PHP。 JSON 必須先經由 {decodeFunction} 剖析。",
|
||||
secureOptionNone: "無 / STARTTLS (25, 587)",
|
||||
secureOptionTLS: "TLS (465)",
|
||||
"Ignore TLS Error": "忽略 TLS 錯誤",
|
||||
"From Email": "寄件人",
|
||||
emailCustomSubject: "自訂主旨",
|
||||
"To Email": "收件人",
|
||||
smtpCC: "CC",
|
||||
smtpBCC: "BCC",
|
||||
"Discord Webhook URL": "Discord Webhook 網址",
|
||||
wayToGetDiscordURL: "您可以前往伺服器設定 -> 整合 -> Webhook -> 新 Webhook 以取得",
|
||||
"Bot Display Name": "機器人顯示名稱",
|
||||
"Prefix Custom Message": "前綴自訂訊息",
|
||||
"Webhook URL": "Webhook 網址",
|
||||
wayToGetTeamsURL: "您可以前往此頁面以了解如何建立 Webhook 網址 {0}。",
|
||||
Number: "號碼",
|
||||
Recipients: "收件人",
|
||||
needSignalAPI: "您需要有 REST API 的 Signal 客戶端。",
|
||||
wayToCheckSignalURL: "您可以前往下列網址以了解如何設定:",
|
||||
signalImportant: "注意: 不得混合收件人的群組和號碼!",
|
||||
"Application Token": "應用程式權杖",
|
||||
"Server URL": "伺服器網址",
|
||||
Priority: "優先度",
|
||||
"Icon Emoji": "Emoji 圖示",
|
||||
"Channel Name": "頻道名稱",
|
||||
"Uptime Kuma URL": "Uptime Kuma 網址",
|
||||
aboutWebhooks: "更多關於 Webhook 的資訊: {0}",
|
||||
aboutChannelName: "如果您不想使用 Webhook 頻道,請在 {0} 頻道名稱欄位填入您想使用的頻道。例如: #其他頻道",
|
||||
aboutKumaURL: "如果您未填入 Uptime Kuma 網址。將預設使用專案 Github 頁面。",
|
||||
emojiCheatSheet: "Emoji 一覽表: {0}",
|
||||
PushByTechulus: "Push by Techulus",
|
||||
clicksendsms: "ClickSend SMS",
|
||||
GoogleChat: "Google Chat (僅限 Google Workspace)",
|
||||
"User Key": "使用者金鑰",
|
||||
Device: "裝置",
|
||||
"Message Title": "訊息標題",
|
||||
"Notification Sound": "通知音效",
|
||||
"More info on:": "更多資訊: {0}",
|
||||
pushoverDesc1: "緊急優先度 (2) 的重試間隔為 30 秒並且會在 1 小時後過期。",
|
||||
pushoverDesc2: "如果您想要傳送通知到不同裝置,請填寫裝置欄位。",
|
||||
"SMS Type": "簡訊類型",
|
||||
octopushTypePremium: "Premium (快速 - 建議用於警報)",
|
||||
octopushTypeLowCost: "Low Cost (緩慢 - 有時會被營運商阻擋)",
|
||||
checkPrice: "查看 {0} 價格:",
|
||||
apiCredentials: "API 認證",
|
||||
octopushLegacyHint: "您使用的是舊版的 Octopush (2011-2020) 還是新版?",
|
||||
"Check octopush prices": "查看 octopush 價格 {0}。",
|
||||
octopushPhoneNumber: "電話號碼 (intl 格式,例如:+33612345678) ",
|
||||
octopushSMSSender: "簡訊寄件人名稱:3-11位英數字元及空白 (a-zA-Z0-9)",
|
||||
"LunaSea Device ID": "LunaSea 裝置 ID",
|
||||
"Apprise URL": "Apprise 網址",
|
||||
"Example:": "範例:{0}",
|
||||
"Read more:": "深入瞭解:{0}",
|
||||
"Status:": "狀態:{0}",
|
||||
"Read more": "深入瞭解",
|
||||
appriseInstalled: "已安裝 Apprise。",
|
||||
appriseNotInstalled: "尚未安裝 Apprise。{0}",
|
||||
"Access Token": "存取權杖",
|
||||
"Channel access token": "頻道存取權杖",
|
||||
"Line Developers Console": "Line 開發者控制台",
|
||||
lineDevConsoleTo: "Line 開發者控制台 - {0}",
|
||||
"Basic Settings": "基本設定",
|
||||
"User ID": "使用者 ID",
|
||||
"Messaging API": "Messaging API",
|
||||
wayToGetLineChannelToken: "首先,前往 {0},建立 provider 和 channel (Messaging API)。接著您就可以從上面提到的選單項目中取得頻道存取權杖及使用者 ID。",
|
||||
"Icon URL": "圖示網址",
|
||||
aboutIconURL: "您可以在 \"圖示網址\" 中提供圖片網址以覆蓋預設個人檔案圖片。若已設定 Emoji 圖示,將忽略此設定。",
|
||||
aboutMattermostChannelName: "您可以在 \"頻道名稱\" 欄位中填寫頻道名稱以覆蓋 Webhook 的預設頻道。必須在 Mattermost 的 Webhook 設定中啟用。例如:#其他頻道",
|
||||
matrix: "Matrix",
|
||||
promosmsTypeEco: "SMS ECO - 便宜,但是很慢且經常過載。僅限位於波蘭的收件人。",
|
||||
promosmsTypeFlash: "SMS FLASH - 訊息會自動在收件人的裝置上顯示。僅限位於波蘭的收件人。",
|
||||
promosmsTypeFull: "SMS FULL - 高級版,您可以使用您的寄件人名稱 (必須先註冊名稱。對於警報來說十分可靠。",
|
||||
promosmsTypeSpeed: "SMS SPEED - 系統中的最高優先度。快速、可靠,但昂貴 (約 SMS FULL 的兩倍價格)。",
|
||||
promosmsPhoneNumber: "電話號碼 (若收件人位於波蘭則無需輸入區域代碼)",
|
||||
promosmsSMSSender: "簡訊寄件人名稱:預先註冊的名稱或以下的預設名稱:InfoSMS、SMS Info、MaxSMS、INFO、SMS",
|
||||
"Feishu WebHookUrl": "飛書 WebHook 網址",
|
||||
matrixHomeserverURL: "Homeserver 網址 (開頭為 http(s)://,結尾可能帶連接埠)",
|
||||
"Internal Room Id": "Internal Room ID",
|
||||
matrixDesc1: "您可以在 Matrix 客戶端的房間設定中的進階選項找到 internal room ID。應該看起來像 !QMdRCpUIfLwsfjxye6:home.server。",
|
||||
matrixDesc2: "使用您自己的 Matrix 使用者存取權杖將賦予存取您的帳號和您加入的房間的完整權限。建議建立新使用者,並邀請至您想要接收通知的房間中。您可以執行 {0} 以取得存取權杖",
|
||||
Method: "方法",
|
||||
Body: "主體",
|
||||
Headers: "標頭",
|
||||
PushUrl: "Push URL",
|
||||
HeadersInvalidFormat: "要求標頭不是有效的 JSON:",
|
||||
BodyInvalidFormat: "請求主體不是有效的 JSON:",
|
||||
"Monitor History": "監測器歷史紀錄",
|
||||
clearDataOlderThan: "保留 {0} 天內的監測器歷史紀錄。",
|
||||
PasswordsDoNotMatch: "密碼不相符。",
|
||||
records: "記錄",
|
||||
"One record": "一項記錄",
|
||||
"Showing {from} to {to} of {count} records": "正在顯示 {count} 項記錄中的 {from} 至 {to} 項",
|
||||
steamApiKeyDescription: "若要監測 Steam 遊戲伺服器,您將需要 Steam Web-API 金鑰。您可以在此註冊您的 API 金鑰:",
|
||||
"Current User": "目前使用者",
|
||||
recent: "最近",
|
||||
Done: "完成",
|
||||
Info: "資訊",
|
||||
Security: "安全性",
|
||||
"Steam API Key": "Steam API 金鑰",
|
||||
"Shrink Database": "壓縮資料庫",
|
||||
"Pick a RR-Type...": "選擇資源記錄類型...",
|
||||
"Pick Accepted Status Codes...": "選擇可接受的狀態碼...",
|
||||
Default: "預設",
|
||||
"HTTP Options": "HTTP 選項",
|
||||
"Create Incident": "建立事件",
|
||||
Title: "標題",
|
||||
Content: "內容",
|
||||
Style: "樣式",
|
||||
info: "資訊",
|
||||
warning: "警告",
|
||||
danger: "危險",
|
||||
primary: "主要",
|
||||
light: "淺色",
|
||||
dark: "暗色",
|
||||
Post: "發佈",
|
||||
"Please input title and content": "請輸入標題及內容",
|
||||
Created: "建立",
|
||||
"Last Updated": "最後更新",
|
||||
Unpin: "取消釘選",
|
||||
"Switch to Light Theme": "切換至淺色佈景主題",
|
||||
"Switch to Dark Theme": "切換至深色佈景主題",
|
||||
"Show Tags": "顯示標籤",
|
||||
"Hide Tags": "隱藏標籤",
|
||||
Description: "描述",
|
||||
"No monitors available.": "沒有可用的監測器。",
|
||||
"Add one": "新增一個",
|
||||
"No Monitors": "無監測器",
|
||||
"Untitled Group": "未命名群組",
|
||||
Services: "服務",
|
||||
Discard: "捨棄",
|
||||
Cancel: "取消",
|
||||
shrinkDatabaseDescription: "觸發 SQLite 的資料庫清理 (VACUUM)。如果您的資料庫是在 1.10.0 版本後建立,AUTO_VACUUM 已自動啟用,則無需此操作。",
|
||||
serwersms: "SerwerSMS.pl",
|
||||
serwersmsAPIUser: "API 使用者名稱 (包括 webapi_ 前綴)",
|
||||
serwersmsAPIPassword: "API 密碼",
|
||||
serwersmsPhoneNumber: "電話號碼",
|
||||
serwersmsSenderName: "SMS 寄件人名稱 (由客戶入口網站註冊)",
|
||||
stackfield: "Stackfield",
|
||||
smtpDkimSettings: "DKIM 設定",
|
||||
smtpDkimDesc: "請參考 Nodemailer DKIM {0} 使用方式。",
|
||||
documentation: "文件",
|
||||
smtpDkimDomain: "網域名稱",
|
||||
smtpDkimKeySelector: "DKIM 選取器",
|
||||
smtpDkimPrivateKey: "私密金鑰",
|
||||
smtpDkimHashAlgo: "雜湊演算法 (選填)",
|
||||
smtpDkimheaderFieldNames: "要簽署的郵件標頭 (選填)",
|
||||
smtpDkimskipFields: "不簽署的郵件標頭 (選填)",
|
||||
gorush: "Gorush",
|
||||
alerta: "Alerta",
|
||||
alertaApiEndpoint: "API Endpoint",
|
||||
alertaEnvironment: "環境",
|
||||
alertaApiKey: "API 金鑰",
|
||||
alertaAlertState: "警示狀態",
|
||||
alertaRecoverState: "恢復狀態",
|
||||
Proxies: "代理伺服器",
|
||||
default: "預設",
|
||||
enabled: "啟用",
|
||||
setAsDefault: "設為預設",
|
||||
deleteProxyMsg: "您確定要為所有監測器刪除此代理伺服器嗎?",
|
||||
proxyDescription: "必須將代理伺服器指派給監測器才能運作。",
|
||||
enableProxyDescription: "此代理伺服器在啟用前不會在監測器上生效,您可以藉由控制啟用狀態來暫時對所有的監測器停用代理伺服器。",
|
||||
setAsDefaultProxyDescription: "預設情況下,新監測器將啟用此代理伺服器。您仍可分別停用各監測器的代理伺服器。",
|
||||
};
|
||||
|
@@ -239,11 +239,13 @@ export default {
|
||||
"rocket.chat": "Rocket.Chat",
|
||||
pushover: "Pushover",
|
||||
pushy: "Pushy",
|
||||
PushByTechulus: "Push by Techulus",
|
||||
octopush: "Octopush",
|
||||
promosms: "PromoSMS",
|
||||
clicksendsms: "ClickSend SMS",
|
||||
lunasea: "LunaSea",
|
||||
apprise: "Apprise (支援 50 種以上的通知服務)",
|
||||
GoogleChat: "Google Chat (僅限 Google Workspace)",
|
||||
pushbullet: "Pushbullet",
|
||||
line: "Line Messenger",
|
||||
mattermost: "Mattermost",
|
||||
@@ -352,5 +354,30 @@ export default {
|
||||
serwersmsAPIPassword: "API 密碼",
|
||||
serwersmsPhoneNumber: "電話號碼",
|
||||
serwersmsSenderName: "SMS 寄件人名稱 (由客戶入口網站註冊)",
|
||||
"stackfield": "Stackfield",
|
||||
stackfield: "Stackfield",
|
||||
smtpDkimSettings: "DKIM 設定",
|
||||
smtpDkimDesc: "請參考 Nodemailer DKIM {0} 使用方式。",
|
||||
documentation: "文件",
|
||||
smtpDkimDomain: "網域名稱",
|
||||
smtpDkimKeySelector: "DKIM 選取器",
|
||||
smtpDkimPrivateKey: "私密金鑰",
|
||||
smtpDkimHashAlgo: "雜湊演算法 (選填)",
|
||||
smtpDkimheaderFieldNames: "要簽署的郵件標頭 (選填)",
|
||||
smtpDkimskipFields: "不簽署的郵件標頭 (選填)",
|
||||
gorush: "Gorush",
|
||||
alerta: "Alerta",
|
||||
alertaApiEndpoint: "API Endpoint",
|
||||
alertaEnvironment: "環境",
|
||||
alertaApiKey: "API 金鑰",
|
||||
alertaAlertState: "警示狀態",
|
||||
alertaRecoverState: "恢復狀態",
|
||||
deleteStatusPageMsg: "您確定要刪除此狀態頁嗎?",
|
||||
Proxies: "代理伺服器",
|
||||
default: "預設",
|
||||
enabled: "啟用",
|
||||
setAsDefault: "設為預設",
|
||||
deleteProxyMsg: "您確定要為所有監測器刪除此代理伺服器嗎?",
|
||||
proxyDescription: "必須將代理伺服器指派給監測器才能運作。",
|
||||
enableProxyDescription: "此代理伺服器在啟用前不會在監測器上生效,您可以藉由控制啟用狀態來暫時對所有的監測器停用代理伺服器。",
|
||||
setAsDefaultProxyDescription: "預設情況下,新監測器將啟用此代理伺服器。您仍可分別停用各監測器的代理伺服器。",
|
||||
};
|
||||
|
@@ -6,6 +6,7 @@ export default {
|
||||
userTheme: localStorage.theme,
|
||||
userHeartbeatBar: localStorage.heartbeatBarTheme,
|
||||
statusPageTheme: "light",
|
||||
forceStatusPageTheme: false,
|
||||
path: "",
|
||||
};
|
||||
},
|
||||
@@ -27,6 +28,10 @@ export default {
|
||||
|
||||
computed: {
|
||||
theme() {
|
||||
// As entry can be status page now, set forceStatusPageTheme to true to use status page theme
|
||||
if (this.forceStatusPageTheme) {
|
||||
return this.statusPageTheme;
|
||||
}
|
||||
|
||||
// Entry no need dark
|
||||
if (this.path === "") {
|
||||
|
@@ -21,7 +21,9 @@
|
||||
<div class="form-text">
|
||||
<ul>
|
||||
<li>{{ $t("Accept characters:") }} <mark>a-z</mark> <mark>0-9</mark> <mark>-</mark></li>
|
||||
<li>{{ $t("Start or end with") }} <mark>a-z</mark> <mark>0-9</mark> only</li>
|
||||
<i18n-t tag="li" keypath="startOrEndWithOnly">
|
||||
<mark>a-z</mark> <mark>0-9</mark>
|
||||
</i18n-t>
|
||||
<li>{{ $t("No consecutive dashes") }} <mark>--</mark></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
@@ -170,6 +170,15 @@
|
||||
|
||||
<h2 v-if="monitor.type !== 'push'" class="mt-5 mb-2">{{ $t("Advanced") }}</h2>
|
||||
|
||||
<div class="my-3 form-check">
|
||||
<input id="expiry-notification" v-model="monitor.expiryNotification" class="form-check-input" type="checkbox">
|
||||
<label class="form-check-label" for="expiry-notification">
|
||||
{{ $t("Domain Name Expiry Notification") }}
|
||||
</label>
|
||||
<div class="form-text">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="monitor.type === 'http' || monitor.type === 'keyword' " class="my-3 form-check">
|
||||
<input id="ignore-tls" v-model="monitor.ignoreTls" class="form-check-input" type="checkbox" value="">
|
||||
<label class="form-check-label" for="ignore-tls">
|
||||
@@ -254,31 +263,33 @@
|
||||
</button>
|
||||
|
||||
<!-- Proxies -->
|
||||
<h2 class="mt-5 mb-2">{{ $t("Proxies") }}</h2>
|
||||
<p v-if="$root.proxyList.length === 0">
|
||||
{{ $t("Not available, please setup.") }}
|
||||
</p>
|
||||
<div v-if="monitor.type === 'http' || monitor.type === 'keyword'">
|
||||
<h2 class="mt-5 mb-2">{{ $t("Proxy") }}</h2>
|
||||
<p v-if="$root.proxyList.length === 0">
|
||||
{{ $t("Not available, please setup.") }}
|
||||
</p>
|
||||
|
||||
<div v-if="$root.proxyList.length > 0" class="form-check form-switch my-3">
|
||||
<input id="proxy-disable" v-model="monitor.proxyId" :value="null" name="proxy" class="form-check-input" type="radio">
|
||||
<label class="form-check-label" for="proxy-disable">{{ $t("No Proxy") }}</label>
|
||||
<div v-if="$root.proxyList.length > 0" class="form-check my-3">
|
||||
<input id="proxy-disable" v-model="monitor.proxyId" :value="null" name="proxy" class="form-check-input" type="radio">
|
||||
<label class="form-check-label" for="proxy-disable">{{ $t("No Proxy") }}</label>
|
||||
</div>
|
||||
|
||||
<div v-for="proxy in $root.proxyList" :key="proxy.id" class="form-check my-3">
|
||||
<input :id="`proxy-${proxy.id}`" v-model="monitor.proxyId" :value="proxy.id" name="proxy" class="form-check-input" type="radio">
|
||||
|
||||
<label class="form-check-label" :for="`proxy-${proxy.id}`">
|
||||
{{ proxy.host }}:{{ proxy.port }} ({{ proxy.protocol }})
|
||||
<a href="#" @click="$refs.proxyDialog.show(proxy.id)">{{ $t("Edit") }}</a>
|
||||
</label>
|
||||
|
||||
<span v-if="proxy.default === true" class="badge bg-primary ms-2">{{ $t("default") }}</span>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary me-2" type="button" @click="$refs.proxyDialog.show()">
|
||||
{{ $t("Setup Proxy") }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div v-for="proxy in $root.proxyList" :key="proxy.id" class="form-check form-switch my-3">
|
||||
<input :id="`proxy-${proxy.id}`" v-model="monitor.proxyId" :value="proxy.id" name="proxy" class="form-check-input" type="radio">
|
||||
|
||||
<label class="form-check-label" :for="`proxy-${proxy.id}`">
|
||||
{{ proxy.host }}:{{ proxy.port }} ({{ proxy.protocol }})
|
||||
<a href="#" @click="$refs.proxyDialog.show(proxy.id)">{{ $t("Edit") }}</a>
|
||||
</label>
|
||||
|
||||
<span v-if="proxy.default === true" class="badge bg-primary ms-2">{{ $t("default") }}</span>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary me-2" type="button" @click="$refs.proxyDialog.show()">
|
||||
{{ $t("Setup Proxy") }}
|
||||
</button>
|
||||
|
||||
<!-- HTTP Options -->
|
||||
<template v-if="monitor.type === 'http' || monitor.type === 'keyword' ">
|
||||
<h2 class="mt-5 mb-2">{{ $t("HTTP Options") }}</h2>
|
||||
@@ -506,6 +517,7 @@ export default {
|
||||
notificationIDList: {},
|
||||
ignoreTls: false,
|
||||
upsideDown: false,
|
||||
expiryNotification: false,
|
||||
maxredirects: 10,
|
||||
accepted_statuscodes: ["200-299"],
|
||||
dns_resolve_type: "A",
|
||||
|
@@ -1,19 +1,45 @@
|
||||
<template>
|
||||
<div></div>
|
||||
<div>
|
||||
<StatusPage v-if="statusPageSlug" :override-slug="statusPageSlug" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import axios from "axios";
|
||||
import StatusPage from "./StatusPage.vue";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
StatusPage,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
statusPageSlug: null,
|
||||
};
|
||||
},
|
||||
async mounted() {
|
||||
let entryPage = (await axios.get("/api/entry-page")).data;
|
||||
|
||||
if (entryPage === "statusPage") {
|
||||
this.$router.push("/status");
|
||||
// There are only 2 cases that could come in here.
|
||||
// 1. Matched status Page domain name
|
||||
// 2. Vue Frontend Dev
|
||||
let res = (await axios.get("/api/entry-page")).data;
|
||||
|
||||
if (res.type === "statusPageMatchedDomain") {
|
||||
this.statusPageSlug = res.statusPageSlug;
|
||||
this.$root.forceStatusPageTheme = true;
|
||||
|
||||
} else if (res.type === "entryPage") { // Dev only. For production, the logic is in the server side
|
||||
const entryPage = res.entryPage;
|
||||
|
||||
if (entryPage === "statusPage") {
|
||||
this.$router.push("/status");
|
||||
} else {
|
||||
this.$router.push("/dashboard");
|
||||
}
|
||||
} else {
|
||||
this.$router.push("/dashboard");
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
};
|
||||
|
@@ -12,7 +12,7 @@
|
||||
<div class="shadow-box">
|
||||
<template v-if="$root.statusPageListLoaded">
|
||||
<span v-if="Object.keys($root.statusPageList).length === 0" class="d-flex align-items-center justify-content-center my-3">
|
||||
No status pages
|
||||
{{ $t("No status pages") }}
|
||||
</span>
|
||||
|
||||
<!-- use <a> instead of <router-link>, because the heartbeat won't load. -->
|
||||
|
@@ -121,6 +121,10 @@ export default {
|
||||
this.$root.getSocket().emit("getSettings", (res) => {
|
||||
this.settings = res.data;
|
||||
|
||||
if (this.settings.checkUpdate === undefined) {
|
||||
this.settings.checkUpdate = true;
|
||||
}
|
||||
|
||||
if (this.settings.searchEngineIndex === undefined) {
|
||||
this.settings.searchEngineIndex = false;
|
||||
}
|
||||
|
@@ -2,49 +2,61 @@
|
||||
<div v-if="loadedTheme" class="container mt-3">
|
||||
<!-- Sidebar for edit mode -->
|
||||
<div v-if="enableEditMode" class="sidebar">
|
||||
<div class="my-3">
|
||||
<label for="slug" class="form-label">{{ $t("Slug") }}</label>
|
||||
<div class="input-group">
|
||||
<span id="basic-addon3" class="input-group-text">/status/</span>
|
||||
<input id="slug" v-model="config.slug" type="text" class="form-control">
|
||||
<div class="sidebar-body">
|
||||
<div class="my-3">
|
||||
<label for="slug" class="form-label">{{ $t("Slug") }}</label>
|
||||
<div class="input-group">
|
||||
<span id="basic-addon3" class="input-group-text">/status/</span>
|
||||
<input id="slug" v-model="config.slug" type="text" class="form-control">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="my-3">
|
||||
<label for="title" class="form-label">{{ $t("Title") }}</label>
|
||||
<input id="title" v-model="config.title" type="text" class="form-control">
|
||||
</div>
|
||||
<div class="my-3">
|
||||
<label for="title" class="form-label">{{ $t("Title") }}</label>
|
||||
<input id="title" v-model="config.title" type="text" class="form-control">
|
||||
</div>
|
||||
|
||||
<div class="my-3">
|
||||
<label for="description" class="form-label">{{ $t("Description") }}</label>
|
||||
<textarea id="description" v-model="config.description" class="form-control"></textarea>
|
||||
</div>
|
||||
<div class="my-3">
|
||||
<label for="description" class="form-label">{{ $t("Description") }}</label>
|
||||
<textarea id="description" v-model="config.description" class="form-control"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="my-3 form-check form-switch">
|
||||
<input id="switch-theme" v-model="config.theme" class="form-check-input" type="checkbox" true-value="dark" false-value="light">
|
||||
<label class="form-check-label" for="switch-theme">{{ $t("Switch to Dark Theme") }}</label>
|
||||
</div>
|
||||
<div class="my-3 form-check form-switch">
|
||||
<input id="switch-theme" v-model="config.theme" class="form-check-input" type="checkbox" true-value="dark" false-value="light">
|
||||
<label class="form-check-label" for="switch-theme">{{ $t("Switch to Dark Theme") }}</label>
|
||||
</div>
|
||||
|
||||
<div class="my-3 form-check form-switch">
|
||||
<input id="showTags" v-model="config.showTags" class="form-check-input" type="checkbox">
|
||||
<label class="form-check-label" for="showTags">{{ $t("Show Tags") }}</label>
|
||||
</div>
|
||||
<div class="my-3 form-check form-switch">
|
||||
<input id="showTags" v-model="config.showTags" class="form-check-input" type="checkbox">
|
||||
<label class="form-check-label" for="showTags">{{ $t("Show Tags") }}</label>
|
||||
</div>
|
||||
|
||||
<div v-if="false" class="my-3">
|
||||
<label for="password" class="form-label">{{ $t("Password") }} <sup>Coming Soon</sup></label>
|
||||
<input id="password" v-model="config.password" disabled type="password" autocomplete="new-password" class="form-control">
|
||||
</div>
|
||||
<div v-if="false" class="my-3">
|
||||
<label for="password" class="form-label">{{ $t("Password") }} <sup>Coming Soon</sup></label>
|
||||
<input id="password" v-model="config.password" disabled type="password" autocomplete="new-password" class="form-control">
|
||||
</div>
|
||||
|
||||
<div v-if="false" class="my-3">
|
||||
<label for="cname" class="form-label">Domain Names <sup>Coming Soon</sup></label>
|
||||
<textarea id="cname" v-model="config.domanNames" rows="3" disabled class="form-control" :placeholder="domainNamesPlaceholder"></textarea>
|
||||
</div>
|
||||
<!-- Domain Name List -->
|
||||
<div class="my-3">
|
||||
<label class="form-label">
|
||||
Domain Names
|
||||
<font-awesome-icon icon="plus-circle" class="btn-add-domain action text-primary" @click="addDomainField" />
|
||||
</label>
|
||||
|
||||
<div class="danger-zone">
|
||||
<button class="btn btn-danger me-2" @click="deleteDialog">
|
||||
<font-awesome-icon icon="trash" />
|
||||
{{ $t("Delete") }}
|
||||
</button>
|
||||
<ul class="list-group domain-name-list">
|
||||
<li v-for="(domain, index) in config.domainNameList" :key="index" class="list-group-item">
|
||||
<input v-model="config.domainNameList[index]" type="text" class="no-bg domain-input" placeholder="example.com" />
|
||||
<font-awesome-icon icon="times" class="action remove ms-2 me-3 text-danger" @click="removeDomain(index)" />
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="danger-zone">
|
||||
<button class="btn btn-danger me-2" @click="deleteDialog">
|
||||
<font-awesome-icon icon="trash" />
|
||||
{{ $t("Delete") }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sidebar Footer -->
|
||||
@@ -55,7 +67,7 @@
|
||||
</button>
|
||||
|
||||
<button class="btn btn-danger me-2" @click="discard">
|
||||
<font-awesome-icon icon="save" />
|
||||
<font-awesome-icon icon="undo" />
|
||||
{{ $t("Discard") }}
|
||||
</button>
|
||||
</div>
|
||||
@@ -120,7 +132,7 @@
|
||||
|
||||
<!-- Incident Date -->
|
||||
<div class="date mt-3">
|
||||
{{ $t("Created") }}: {{ $root.datetime(incident.createdDate) }} ({{ dateFromNow(incident.createdDate) }})<br />
|
||||
{{ $t("Date Created") }}: {{ $root.datetime(incident.createdDate) }} ({{ dateFromNow(incident.createdDate) }})<br />
|
||||
<span v-if="incident.lastUpdatedDate">
|
||||
{{ $t("Last Updated") }}: {{ $root.datetime(incident.lastUpdatedDate) }} ({{ dateFromNow(incident.lastUpdatedDate) }})
|
||||
</span>
|
||||
@@ -259,6 +271,7 @@ const favicon = new Favico({
|
||||
});
|
||||
|
||||
export default {
|
||||
|
||||
components: {
|
||||
PublicGroupList,
|
||||
ImageCropUpload,
|
||||
@@ -278,6 +291,14 @@ export default {
|
||||
next();
|
||||
},
|
||||
|
||||
props: {
|
||||
overrideSlug: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
slug: null,
|
||||
@@ -294,7 +315,6 @@ export default {
|
||||
loadedData: false,
|
||||
baseURL: "",
|
||||
clickedEditButton: false,
|
||||
domainNamesPlaceholder: "domain1.com\ndomain2.com\n..."
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -389,6 +409,22 @@ export default {
|
||||
},
|
||||
watch: {
|
||||
|
||||
/**
|
||||
* If connected to the socket and logged in, request private data of this statusPage
|
||||
* @param connected
|
||||
*/
|
||||
"$root.loggedIn"(loggedIn) {
|
||||
if (loggedIn) {
|
||||
this.$root.getSocket().emit("getStatusPage", this.slug, (res) => {
|
||||
if (res.ok) {
|
||||
this.config = res.config;
|
||||
} else {
|
||||
toast.error(res.msg);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Selected a monitor and add to the list.
|
||||
*/
|
||||
@@ -449,7 +485,7 @@ export default {
|
||||
this.baseURL = getResBaseURL();
|
||||
},
|
||||
async mounted() {
|
||||
this.slug = this.$route.params.slug;
|
||||
this.slug = this.overrideSlug || this.$route.params.slug;
|
||||
|
||||
if (!this.slug) {
|
||||
this.slug = "default";
|
||||
@@ -458,6 +494,10 @@ export default {
|
||||
axios.get("/api/status-page/" + this.slug).then((res) => {
|
||||
this.config = res.data.config;
|
||||
|
||||
if (!this.config.domainNameList) {
|
||||
this.config.domainNameList = [];
|
||||
}
|
||||
|
||||
if (this.config.icon) {
|
||||
this.imgDataUrl = this.config.icon;
|
||||
}
|
||||
@@ -575,6 +615,10 @@ export default {
|
||||
});
|
||||
},
|
||||
|
||||
addDomainField() {
|
||||
this.config.domainNameList.push("");
|
||||
},
|
||||
|
||||
discard() {
|
||||
location.href = "/status/" + this.slug;
|
||||
},
|
||||
@@ -657,6 +701,10 @@ export default {
|
||||
return dayjs.utc(date).fromNow();
|
||||
},
|
||||
|
||||
removeDomain(index) {
|
||||
this.config.domainNameList.splice(index, 1);
|
||||
},
|
||||
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -705,9 +753,7 @@ h1 {
|
||||
top: 0;
|
||||
width: 300px;
|
||||
height: 100vh;
|
||||
padding: 15px 15px 68px 15px;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
|
||||
border-right: 1px solid #ededed;
|
||||
|
||||
.danger-zone {
|
||||
@@ -715,13 +761,25 @@ h1 {
|
||||
padding-top: 15px;
|
||||
}
|
||||
|
||||
.sidebar-body {
|
||||
padding: 0 10px 10px 10px;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
height: calc(100% - 70px);
|
||||
}
|
||||
|
||||
.sidebar-footer {
|
||||
width: 100%;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
padding: 15px;
|
||||
position: absolute;
|
||||
border-top: 1px solid #ededed;
|
||||
border-right: 1px solid #ededed;
|
||||
padding: 10px;
|
||||
width: 300px;
|
||||
height: 70px;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
background-color: white;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -808,7 +866,29 @@ footer {
|
||||
}
|
||||
|
||||
.sidebar-footer {
|
||||
border-right-color: $dark-border-color;
|
||||
border-top-color: $dark-border-color;
|
||||
background-color: $dark-header-bg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.domain-name-list {
|
||||
li {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px 0 10px 10px;
|
||||
|
||||
.domain-input {
|
||||
flex-grow: 1;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
color: $dark-font-color;
|
||||
outline: none;
|
||||
|
||||
&::placeholder {
|
||||
color: #1d2634;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -7,6 +7,12 @@ import { localeDirection, currentLocale } from "./i18n";
|
||||
dayjs.extend(utc);
|
||||
dayjs.extend(timezone);
|
||||
|
||||
/**
|
||||
* Returns the offset from UTC in hours for the current locale.
|
||||
* @returns {number} The offset from UTC in hours.
|
||||
*
|
||||
* Generated by Trelent
|
||||
*/
|
||||
function getTimezoneOffset(timeZone) {
|
||||
const now = new Date();
|
||||
const tzString = now.toLocaleString("en-US", {
|
||||
@@ -18,6 +24,13 @@ function getTimezoneOffset(timeZone) {
|
||||
return -offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of timezones sorted by their offset from UTC.
|
||||
* @param {Array} timezones - An array of timezone objects.
|
||||
* @returns {Array} A list of the given timezones sorted by their offset from UTC.
|
||||
*
|
||||
* Generated by Trelent
|
||||
*/
|
||||
export function timezoneList() {
|
||||
let result = [];
|
||||
|
||||
|
14
src/util.js
14
src/util.js
@@ -121,6 +121,13 @@ let getRandomBytes = ((typeof window !== 'undefined' && window.crypto)
|
||||
: function () {
|
||||
return require("crypto").randomBytes;
|
||||
})();
|
||||
/**
|
||||
* Returns a random integer between min (inclusive) and max (exclusive).
|
||||
* @param {number} min The minimum value.
|
||||
* @param {number} max The maximum value.
|
||||
*
|
||||
* Generated by Trelent
|
||||
*/
|
||||
function getCryptoRandomInt(min, max) {
|
||||
// synchronous version of: https://github.com/joepie91/node-random-number-csprng
|
||||
const range = max - min;
|
||||
@@ -151,6 +158,13 @@ function getCryptoRandomInt(min, max) {
|
||||
}
|
||||
}
|
||||
exports.getCryptoRandomInt = getCryptoRandomInt;
|
||||
/**
|
||||
* Generates a random string of length `length` from the characters in `chars`.
|
||||
* @param {number} length The number of characters to generate.
|
||||
* @param {string} chars A string containing all the possible characters to use for generating the random string.
|
||||
*
|
||||
* Generated by Trelent
|
||||
*/
|
||||
function genSecret(length = 64) {
|
||||
let secret = "";
|
||||
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
|
Reference in New Issue
Block a user