mirror of
https://github.com/Dvorinka/beszel.git
synced 2026-07-29 15:23:48 +00:00
Compare commits
8
Commits
21657abe38
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
18046aee71 | ||
|
|
fe5c7eaa95 | ||
|
|
0dd7db8a82 | ||
|
|
b6f40af67f | ||
|
|
1af18872d5 | ||
|
|
298c514087 | ||
|
|
77b24ae9e5 | ||
|
|
7ea9a069f9 |
@@ -14,6 +14,7 @@ MAX_STATUS_PAGES=10
|
|||||||
|
|
||||||
# Optional Features
|
# Optional Features
|
||||||
PAGESPEED_ENABLED=true
|
PAGESPEED_ENABLED=true
|
||||||
|
# PAGESPEED_API_KEY=your_google_api_key_here
|
||||||
SUBDOMAIN_DISCOVERY=true
|
SUBDOMAIN_DISCOVERY=true
|
||||||
STATUS_PAGES_ENABLED=true
|
STATUS_PAGES_ENABLED=true
|
||||||
BADGES_ENABLED=true
|
BADGES_ENABLED=true
|
||||||
|
|||||||
+7
-8
@@ -38,16 +38,15 @@ __debug_*
|
|||||||
*timestamp*
|
*timestamp*
|
||||||
.playwright-cli/
|
.playwright-cli/
|
||||||
|
|
||||||
# Reference code (external projects)
|
# Reference code (external projects) - cleaned up
|
||||||
reference/
|
reference/
|
||||||
|
|
||||||
# Graphify output - only keep json, md, html in root
|
|
||||||
graphify-out/*/
|
|
||||||
graphify-out/*.svg
|
|
||||||
!graphify-out/*.json
|
|
||||||
!graphify-out/*.md
|
|
||||||
!graphify-out/*.html
|
|
||||||
|
|
||||||
# Environment
|
# Environment
|
||||||
.env
|
.env
|
||||||
.env.local
|
.env.local
|
||||||
|
|
||||||
|
graphify-out/*
|
||||||
|
!graphify-out/*.svg
|
||||||
|
!graphify-out/*.json
|
||||||
|
!graphify-out/*.md
|
||||||
|
!graphify-out/*.html
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
# Security Policy
|
|
||||||
|
|
||||||
## Reporting a Vulnerability
|
|
||||||
|
|
||||||
If you find a vulnerability in the latest version, please [submit a private advisory](https://github.com/henrygd/beszel/security/advisories/new).
|
|
||||||
|
|
||||||
If it's low severity (use best judgement) you may open an issue instead of an advisory.
|
|
||||||
+246046
File diff suppressed because it is too large
Load Diff
|
After Width: | Height: | Size: 14 MiB |
@@ -71,6 +71,33 @@ type Domain struct {
|
|||||||
AbuseEmail string `json:"abuse_email" db:"abuse_email"`
|
AbuseEmail string `json:"abuse_email" db:"abuse_email"`
|
||||||
AbusePhone string `json:"abuse_phone" db:"abuse_phone"`
|
AbusePhone string `json:"abuse_phone" db:"abuse_phone"`
|
||||||
|
|
||||||
|
// Provider Detection
|
||||||
|
DNSProvider string `json:"dns_provider" db:"dns_provider"`
|
||||||
|
HostingProvider string `json:"hosting_provider" db:"hosting_provider"`
|
||||||
|
EmailProvider string `json:"email_provider" db:"email_provider"`
|
||||||
|
CAProvider string `json:"ca_provider" db:"ca_provider"`
|
||||||
|
|
||||||
|
// HTTP Headers
|
||||||
|
Headers []Header `json:"headers" db:"headers"`
|
||||||
|
|
||||||
|
// Certificate Chain
|
||||||
|
Certificates []Certificate `json:"certificates" db:"certificates"`
|
||||||
|
|
||||||
|
// SEO Metadata
|
||||||
|
SEOMeta *SEOMeta `json:"seo_meta" db:"seo_meta"`
|
||||||
|
|
||||||
|
// Raw WHOIS Response
|
||||||
|
WHOISRaw string `json:"whois_raw" db:"whois_raw"`
|
||||||
|
|
||||||
|
// Registration Details
|
||||||
|
PrivacyEnabled bool `json:"privacy_enabled" db:"privacy_enabled"`
|
||||||
|
TransferLock bool `json:"transfer_lock" db:"transfer_lock"`
|
||||||
|
TLD string `json:"tld" db:"tld"`
|
||||||
|
DomainStatuses []string `json:"domain_statuses" db:"domain_statuses"`
|
||||||
|
|
||||||
|
// Enhanced Geo
|
||||||
|
HostCountryCode string `json:"host_country_code" db:"host_country_code"`
|
||||||
|
|
||||||
// Metadata
|
// Metadata
|
||||||
Tags []string `json:"tags" db:"tags"`
|
Tags []string `json:"tags" db:"tags"`
|
||||||
Notes string `json:"notes" db:"notes"`
|
Notes string `json:"notes" db:"notes"`
|
||||||
@@ -176,6 +203,76 @@ type IPInfo struct {
|
|||||||
IPv6 []string `json:"ipv6"`
|
IPv6 []string `json:"ipv6"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Header represents an HTTP response header
|
||||||
|
type Header struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Value string `json:"value"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Certificate represents a TLS certificate in the chain
|
||||||
|
type Certificate struct {
|
||||||
|
Issuer string `json:"issuer"`
|
||||||
|
Subject string `json:"subject"`
|
||||||
|
AltNames []string `json:"alt_names"`
|
||||||
|
ValidFrom time.Time `json:"valid_from"`
|
||||||
|
ValidTo time.Time `json:"valid_to"`
|
||||||
|
CAProvider string `json:"ca_provider"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenGraphMeta represents Open Graph metadata
|
||||||
|
type OpenGraphMeta struct {
|
||||||
|
URL string `json:"url"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
Images []string `json:"images"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TwitterMeta represents Twitter card metadata
|
||||||
|
type TwitterMeta struct {
|
||||||
|
Title string `json:"title"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Image string `json:"image"`
|
||||||
|
Card string `json:"card"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GeneralMeta represents general HTML meta tags
|
||||||
|
type GeneralMeta struct {
|
||||||
|
Title string `json:"title"`
|
||||||
|
Author string `json:"author"`
|
||||||
|
Robots string `json:"robots"`
|
||||||
|
Keywords string `json:"keywords"`
|
||||||
|
Canonical string `json:"canonical"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// RobotsTxt represents parsed robots.txt data
|
||||||
|
type RobotsTxt struct {
|
||||||
|
Fetched bool `json:"fetched"`
|
||||||
|
Groups []RobotsGroup `json:"groups"`
|
||||||
|
Sitemaps []string `json:"sitemaps"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// RobotsGroup represents a user-agent group in robots.txt
|
||||||
|
type RobotsGroup struct {
|
||||||
|
UserAgents []string `json:"userAgents"`
|
||||||
|
Rules []RobotsRule `json:"rules"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// RobotsRule represents a single rule in robots.txt
|
||||||
|
type RobotsRule struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
Value string `json:"value"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SEOMeta represents all SEO-related metadata
|
||||||
|
type SEOMeta struct {
|
||||||
|
OpenGraph OpenGraphMeta `json:"openGraph"`
|
||||||
|
Twitter TwitterMeta `json:"twitter"`
|
||||||
|
General GeneralMeta `json:"general"`
|
||||||
|
Robots RobotsTxt `json:"robots"`
|
||||||
|
}
|
||||||
|
|
||||||
// ChangeType constants for domain history
|
// ChangeType constants for domain history
|
||||||
const (
|
const (
|
||||||
ChangeTypeExpiry = "expiry"
|
ChangeTypeExpiry = "expiry"
|
||||||
@@ -185,6 +282,9 @@ const (
|
|||||||
ChangeTypeIP = "ip"
|
ChangeTypeIP = "ip"
|
||||||
ChangeTypeHost = "host"
|
ChangeTypeHost = "host"
|
||||||
ChangeTypeStatus = "status"
|
ChangeTypeStatus = "status"
|
||||||
|
ChangeTypeProvider = "provider"
|
||||||
|
ChangeTypeSecurity = "security"
|
||||||
|
ChangeTypeSEO = "seo"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Domain status constants
|
// Domain status constants
|
||||||
|
|||||||
@@ -43,6 +43,11 @@ func (h *APIHandler) RegisterRoutes(se *core.ServeEvent) {
|
|||||||
api.GET("/{id}/stats", h.getDomainStats)
|
api.GET("/{id}/stats", h.getDomainStats)
|
||||||
api.POST("/{id}/pause", h.pauseDomain)
|
api.POST("/{id}/pause", h.pauseDomain)
|
||||||
api.POST("/{id}/resume", h.resumeDomain)
|
api.POST("/{id}/resume", h.resumeDomain)
|
||||||
|
api.GET("/{id}/subdomains", h.getDomainSubdomains)
|
||||||
|
api.POST("/{id}/discover-subdomains", h.discoverSubdomains)
|
||||||
|
|
||||||
|
// Subdomain routes
|
||||||
|
api.DELETE("/subdomains/{subdomainId}", h.deleteSubdomain)
|
||||||
}
|
}
|
||||||
|
|
||||||
// listDomains lists all domains for the authenticated user
|
// listDomains lists all domains for the authenticated user
|
||||||
@@ -123,6 +128,9 @@ func (h *APIHandler) createDomain(e *core.RequestEvent) error {
|
|||||||
NotifyOnSSL bool `json:"notify_on_ssl_expiry"`
|
NotifyOnSSL bool `json:"notify_on_ssl_expiry"`
|
||||||
NotifyOnDNS bool `json:"notify_on_dns_change"`
|
NotifyOnDNS bool `json:"notify_on_dns_change"`
|
||||||
NotifyOnReg bool `json:"notify_on_registrar_change"`
|
NotifyOnReg bool `json:"notify_on_registrar_change"`
|
||||||
|
// Manual expiry override when WHOIS fails
|
||||||
|
ExpiryDate string `json:"expiry_date,omitempty"`
|
||||||
|
CreationDate string `json:"creation_date,omitempty"`
|
||||||
}
|
}
|
||||||
if err := json.NewDecoder(e.Request.Body).Decode(&req); err != nil {
|
if err := json.NewDecoder(e.Request.Body).Decode(&req); err != nil {
|
||||||
return e.BadRequestError("invalid request body", err)
|
return e.BadRequestError("invalid request body", err)
|
||||||
@@ -174,6 +182,7 @@ func (h *APIHandler) createDomain(e *core.RequestEvent) error {
|
|||||||
record.Set("user", authRecord.Id)
|
record.Set("user", authRecord.Id)
|
||||||
|
|
||||||
// Auto-lookup if requested
|
// Auto-lookup if requested
|
||||||
|
lookupHadExpiry := false
|
||||||
if req.AutoLookup {
|
if req.AutoLookup {
|
||||||
lookupSvc := whois.NewLookupService("")
|
lookupSvc := whois.NewLookupService("")
|
||||||
ctx := e.Request.Context()
|
ctx := e.Request.Context()
|
||||||
@@ -183,6 +192,7 @@ func (h *APIHandler) createDomain(e *core.RequestEvent) error {
|
|||||||
// Calculate status based on lookup results
|
// Calculate status based on lookup results
|
||||||
status := domain.DomainStatusUnknown
|
status := domain.DomainStatusUnknown
|
||||||
if domainData.ExpiryDate != nil {
|
if domainData.ExpiryDate != nil {
|
||||||
|
lookupHadExpiry = true
|
||||||
daysUntil := int(time.Until(*domainData.ExpiryDate).Hours() / 24)
|
daysUntil := int(time.Until(*domainData.ExpiryDate).Hours() / 24)
|
||||||
if daysUntil < 0 {
|
if daysUntil < 0 {
|
||||||
status = domain.DomainStatusExpired
|
status = domain.DomainStatusExpired
|
||||||
@@ -199,6 +209,29 @@ func (h *APIHandler) createDomain(e *core.RequestEvent) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply manual expiry/creation dates if WHOIS didn't find them
|
||||||
|
if !lookupHadExpiry {
|
||||||
|
if req.ExpiryDate != "" {
|
||||||
|
if t, err := time.Parse("2006-01-02", req.ExpiryDate); err == nil {
|
||||||
|
record.Set("expiry_date", t)
|
||||||
|
// Recalculate status with manual expiry
|
||||||
|
daysUntil := int(time.Until(t).Hours() / 24)
|
||||||
|
status := domain.DomainStatusActive
|
||||||
|
if daysUntil < 0 {
|
||||||
|
status = domain.DomainStatusExpired
|
||||||
|
} else if daysUntil <= req.AlertDaysBefore {
|
||||||
|
status = domain.DomainStatusExpiring
|
||||||
|
}
|
||||||
|
record.Set("status", status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if req.CreationDate != "" {
|
||||||
|
if t, err := time.Parse("2006-01-02", req.CreationDate); err == nil {
|
||||||
|
record.Set("creation_date", t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if err := h.app.Save(record); err != nil {
|
if err := h.app.Save(record); err != nil {
|
||||||
return e.InternalServerError("failed to create domain", err)
|
return e.InternalServerError("failed to create domain", err)
|
||||||
}
|
}
|
||||||
@@ -602,6 +635,19 @@ func (h *APIHandler) recordToResponse(record *core.Record) map[string]interface{
|
|||||||
"registrant_state": record.GetString("registrant_state"),
|
"registrant_state": record.GetString("registrant_state"),
|
||||||
"abuse_email": record.GetString("abuse_email"),
|
"abuse_email": record.GetString("abuse_email"),
|
||||||
"abuse_phone": record.GetString("abuse_phone"),
|
"abuse_phone": record.GetString("abuse_phone"),
|
||||||
|
"dns_provider": record.GetString("dns_provider"),
|
||||||
|
"hosting_provider": record.GetString("hosting_provider"),
|
||||||
|
"email_provider": record.GetString("email_provider"),
|
||||||
|
"ca_provider": record.GetString("ca_provider"),
|
||||||
|
"headers": record.Get("headers"),
|
||||||
|
"certificates": record.Get("certificates"),
|
||||||
|
"seo_meta": record.Get("seo_meta"),
|
||||||
|
"whois_raw": record.GetString("whois_raw"),
|
||||||
|
"privacy_enabled": record.GetBool("privacy_enabled"),
|
||||||
|
"transfer_lock": record.GetBool("transfer_lock"),
|
||||||
|
"tld": record.GetString("tld"),
|
||||||
|
"domain_statuses": record.Get("domain_statuses"),
|
||||||
|
"host_country_code": record.GetString("host_country_code"),
|
||||||
"tags": record.Get("tags"),
|
"tags": record.Get("tags"),
|
||||||
"notes": record.GetString("notes"),
|
"notes": record.GetString("notes"),
|
||||||
"favicon_url": record.GetString("favicon_url"),
|
"favicon_url": record.GetString("favicon_url"),
|
||||||
@@ -684,6 +730,27 @@ func (h *APIHandler) applyLookupData(record *core.Record, domainData *domain.Dom
|
|||||||
record.Set("registrant_postal", domainData.RegistrantPostal)
|
record.Set("registrant_postal", domainData.RegistrantPostal)
|
||||||
record.Set("abuse_email", domainData.AbuseEmail)
|
record.Set("abuse_email", domainData.AbuseEmail)
|
||||||
record.Set("abuse_phone", domainData.AbusePhone)
|
record.Set("abuse_phone", domainData.AbusePhone)
|
||||||
|
record.Set("dns_provider", domainData.DNSProvider)
|
||||||
|
record.Set("hosting_provider", domainData.HostingProvider)
|
||||||
|
record.Set("email_provider", domainData.EmailProvider)
|
||||||
|
record.Set("ca_provider", domainData.CAProvider)
|
||||||
|
if len(domainData.Headers) > 0 {
|
||||||
|
record.Set("headers", domainData.Headers)
|
||||||
|
}
|
||||||
|
if len(domainData.Certificates) > 0 {
|
||||||
|
record.Set("certificates", domainData.Certificates)
|
||||||
|
}
|
||||||
|
if domainData.SEOMeta != nil {
|
||||||
|
record.Set("seo_meta", domainData.SEOMeta)
|
||||||
|
}
|
||||||
|
record.Set("whois_raw", domainData.WHOISRaw)
|
||||||
|
record.Set("privacy_enabled", domainData.PrivacyEnabled)
|
||||||
|
record.Set("transfer_lock", domainData.TransferLock)
|
||||||
|
record.Set("tld", domainData.TLD)
|
||||||
|
if len(domainData.DomainStatuses) > 0 {
|
||||||
|
record.Set("domain_statuses", domainData.DomainStatuses)
|
||||||
|
}
|
||||||
|
record.Set("host_country_code", domainData.HostCountryCode)
|
||||||
record.Set("favicon_url", domainData.FaviconURL)
|
record.Set("favicon_url", domainData.FaviconURL)
|
||||||
record.Set("last_checked", time.Now())
|
record.Set("last_checked", time.Now())
|
||||||
}
|
}
|
||||||
@@ -705,3 +772,104 @@ func cleanDomain(domain string) string {
|
|||||||
}
|
}
|
||||||
return strings.ToLower(strings.TrimSpace(domain))
|
return strings.ToLower(strings.TrimSpace(domain))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getDomainSubdomains returns all subdomains for a domain
|
||||||
|
func (h *APIHandler) getDomainSubdomains(e *core.RequestEvent) error {
|
||||||
|
authRecord := e.Auth
|
||||||
|
if authRecord == nil {
|
||||||
|
return e.UnauthorizedError("unauthorized", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
domainID := e.Request.PathValue("id")
|
||||||
|
|
||||||
|
// Verify domain ownership
|
||||||
|
domain, err := h.app.FindRecordById("domains", domainID)
|
||||||
|
if err != nil {
|
||||||
|
return e.NotFoundError("domain not found", err)
|
||||||
|
}
|
||||||
|
if domain.GetString("user") != authRecord.Id {
|
||||||
|
return e.ForbiddenError("not authorized", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
records, err := h.app.FindAllRecords("subdomains",
|
||||||
|
dbx.NewExp("domain = {:domain}", dbx.Params{"domain": domainID}),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return e.InternalServerError("failed to fetch subdomains", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
subdomains := make([]map[string]interface{}, 0, len(records))
|
||||||
|
for _, record := range records {
|
||||||
|
subdomains = append(subdomains, map[string]interface{}{
|
||||||
|
"id": record.Id,
|
||||||
|
"domain": record.GetString("domain"),
|
||||||
|
"subdomain_name": record.GetString("subdomain_name"),
|
||||||
|
"full_domain": record.GetString("full_domain"),
|
||||||
|
"status": record.GetString("status"),
|
||||||
|
"ip_addresses": record.GetString("ip_addresses"),
|
||||||
|
"http_status": record.GetInt("http_status"),
|
||||||
|
"server_header": record.GetString("server_header"),
|
||||||
|
"discovery_source": record.GetString("discovery_source"),
|
||||||
|
"last_checked": record.GetDateTime("last_checked").Time(),
|
||||||
|
"created": record.GetDateTime("created").Time(),
|
||||||
|
"updated": record.GetDateTime("updated").Time(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return e.JSON(http.StatusOK, subdomains)
|
||||||
|
}
|
||||||
|
|
||||||
|
// discoverSubdomains triggers subdomain discovery for a domain
|
||||||
|
func (h *APIHandler) discoverSubdomains(e *core.RequestEvent) error {
|
||||||
|
authRecord := e.Auth
|
||||||
|
if authRecord == nil {
|
||||||
|
return e.UnauthorizedError("unauthorized", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
domainID := e.Request.PathValue("id")
|
||||||
|
|
||||||
|
// Verify domain ownership
|
||||||
|
domain, err := h.app.FindRecordById("domains", domainID)
|
||||||
|
if err != nil {
|
||||||
|
return e.NotFoundError("domain not found", err)
|
||||||
|
}
|
||||||
|
if domain.GetString("user") != authRecord.Id {
|
||||||
|
return e.ForbiddenError("not authorized", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trigger discovery asynchronously
|
||||||
|
go func() {
|
||||||
|
domainName := domain.GetString("domain_name")
|
||||||
|
userID := authRecord.Id
|
||||||
|
h.scheduler.discoverSubdomainsEnhanced(domain, domainName, userID)
|
||||||
|
}()
|
||||||
|
|
||||||
|
return e.JSON(http.StatusOK, map[string]string{"status": "discovery_started"})
|
||||||
|
}
|
||||||
|
|
||||||
|
// deleteSubdomain deletes a subdomain
|
||||||
|
func (h *APIHandler) deleteSubdomain(e *core.RequestEvent) error {
|
||||||
|
authRecord := e.Auth
|
||||||
|
if authRecord == nil {
|
||||||
|
return e.UnauthorizedError("unauthorized", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
subdomainID := e.Request.PathValue("subdomainId")
|
||||||
|
|
||||||
|
// Get subdomain
|
||||||
|
subdomain, err := h.app.FindRecordById("subdomains", subdomainID)
|
||||||
|
if err != nil {
|
||||||
|
return e.NotFoundError("subdomain not found", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify ownership
|
||||||
|
if subdomain.GetString("user") != authRecord.Id {
|
||||||
|
return e.ForbiddenError("not authorized", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := h.app.Delete(subdomain); err != nil {
|
||||||
|
return e.InternalServerError("failed to delete subdomain", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return e.NoContent(http.StatusNoContent)
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,275 @@
|
|||||||
|
package detect
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ProviderInfo holds detected provider name
|
||||||
|
type ProviderInfo struct {
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
// DetectDNSProvider detects the DNS provider from NS records
|
||||||
|
func DetectDNSProvider(nsRecords []string) string {
|
||||||
|
for _, ns := range nsRecords {
|
||||||
|
nsLower := strings.ToLower(ns)
|
||||||
|
switch {
|
||||||
|
case strings.Contains(nsLower, "cloudflare"):
|
||||||
|
return "Cloudflare"
|
||||||
|
case strings.Contains(nsLower, "awsdns"):
|
||||||
|
return "Amazon Route 53"
|
||||||
|
case strings.Contains(nsLower, "googledomains") || strings.Contains(nsLower, "google.com"):
|
||||||
|
return "Google Domains"
|
||||||
|
case strings.Contains(nsLower, "namecheap") || strings.Contains(nsLower, "namecheaphosting"):
|
||||||
|
return "Namecheap"
|
||||||
|
case strings.Contains(nsLower, "godaddy"):
|
||||||
|
return "GoDaddy"
|
||||||
|
case strings.Contains(nsLower, "domaincontrol"):
|
||||||
|
return "GoDaddy"
|
||||||
|
case strings.Contains(nsLower, "nsone.net"):
|
||||||
|
return "NS1"
|
||||||
|
case strings.Contains(nsLower, "digitalocean"):
|
||||||
|
return "DigitalOcean"
|
||||||
|
case strings.Contains(nsLower, "linode"):
|
||||||
|
return "Linode"
|
||||||
|
case strings.Contains(nsLower, "vultr"):
|
||||||
|
return "Vultr"
|
||||||
|
case strings.Contains(nsLower, "he.net"):
|
||||||
|
return "Hurricane Electric"
|
||||||
|
case strings.Contains(nsLower, "dyn.com") || strings.Contains(nsLower, "dynect"):
|
||||||
|
return "Dyn (Oracle)"
|
||||||
|
case strings.Contains(nsLower, "ultradns"):
|
||||||
|
return "UltraDNS"
|
||||||
|
case strings.Contains(nsLower, "dnsimple"):
|
||||||
|
return "DNSimple"
|
||||||
|
case strings.Contains(nsLower, "hover"):
|
||||||
|
return "Hover"
|
||||||
|
case strings.Contains(nsLower, "register.com"):
|
||||||
|
return "Register.com"
|
||||||
|
case strings.Contains(nsLower, "enom"):
|
||||||
|
return "eNom"
|
||||||
|
case strings.Contains(nsLower, "worldnic"):
|
||||||
|
return "Network Solutions"
|
||||||
|
case strings.Contains(nsLower, "zoneedit"):
|
||||||
|
return "ZoneEdit"
|
||||||
|
case strings.Contains(nsLower, "easydns"):
|
||||||
|
return "EasyDNS"
|
||||||
|
case strings.Contains(nsLower, "gandi"):
|
||||||
|
return "Gandi"
|
||||||
|
case strings.Contains(nsLower, "ovh"):
|
||||||
|
return "OVH"
|
||||||
|
case strings.Contains(nsLower, "hetzner"):
|
||||||
|
return "Hetzner"
|
||||||
|
case strings.Contains(nsLower, "azure-dns"):
|
||||||
|
return "Microsoft Azure"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// DetectEmailProvider detects the email provider from MX records
|
||||||
|
func DetectEmailProvider(mxRecords []string) string {
|
||||||
|
for _, mx := range mxRecords {
|
||||||
|
mxLower := strings.ToLower(mx)
|
||||||
|
// Extract just the hostname part if it has priority prefix
|
||||||
|
host := mxLower
|
||||||
|
if idx := strings.Index(mxLower, " "); idx > 0 {
|
||||||
|
host = strings.TrimSpace(mxLower[idx+1:])
|
||||||
|
}
|
||||||
|
host = strings.TrimSuffix(host, ".")
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case strings.Contains(host, "google") || strings.Contains(host, "gmail"):
|
||||||
|
return "Google Workspace"
|
||||||
|
case strings.Contains(host, "outlook") || strings.Contains(host, "microsoft") || strings.Contains(host, "protection.outlook"):
|
||||||
|
return "Microsoft 365"
|
||||||
|
case strings.Contains(host, "purelymail"):
|
||||||
|
return "Purelymail"
|
||||||
|
case strings.Contains(host, "zoho"):
|
||||||
|
return "Zoho Mail"
|
||||||
|
case strings.Contains(host, "protonmail") || strings.Contains(host, "pm.me"):
|
||||||
|
return "ProtonMail"
|
||||||
|
case strings.Contains(host, "fastmail"):
|
||||||
|
return "Fastmail"
|
||||||
|
case strings.Contains(host, "tutanota"):
|
||||||
|
return "Tutanota"
|
||||||
|
case strings.Contains(host, "mxroute"):
|
||||||
|
return "MXroute"
|
||||||
|
case strings.Contains(host, "namecheap"):
|
||||||
|
return "Namecheap"
|
||||||
|
case strings.Contains(host, "icloud") || strings.Contains(host, "me.com"):
|
||||||
|
return "iCloud Mail"
|
||||||
|
case strings.Contains(host, "yahoo"):
|
||||||
|
return "Yahoo"
|
||||||
|
case strings.Contains(host, "qq.com"):
|
||||||
|
return "QQ Mail"
|
||||||
|
case strings.Contains(host, "mail.ru"):
|
||||||
|
return "Mail.ru"
|
||||||
|
case strings.Contains(host, "yandex"):
|
||||||
|
return "Yandex"
|
||||||
|
case strings.Contains(host, "hover"):
|
||||||
|
return "Hover"
|
||||||
|
case strings.Contains(host, "godaddy") || strings.Contains(host, "domaincontrol"):
|
||||||
|
return "GoDaddy"
|
||||||
|
case strings.Contains(host, "pobox"):
|
||||||
|
return "Pobox"
|
||||||
|
case strings.Contains(host, "runbox"):
|
||||||
|
return "Runbox"
|
||||||
|
case strings.Contains(host, "posteo"):
|
||||||
|
return "Posteo"
|
||||||
|
case strings.Contains(host, "mailbox.org"):
|
||||||
|
return "Mailbox.org"
|
||||||
|
case strings.Contains(host, "forwardemail"):
|
||||||
|
return "Forward Email"
|
||||||
|
case strings.Contains(host, "improvmx"):
|
||||||
|
return "ImprovMX"
|
||||||
|
case strings.Contains(host, "cloudflare"):
|
||||||
|
return "Cloudflare Email Routing"
|
||||||
|
case strings.Contains(host, "amazonaws") || strings.Contains(host, "aws"):
|
||||||
|
return "Amazon SES"
|
||||||
|
case strings.Contains(host, "sendgrid") || strings.Contains(host, "twilio"):
|
||||||
|
return "SendGrid"
|
||||||
|
case strings.Contains(host, "mailgun"):
|
||||||
|
return "Mailgun"
|
||||||
|
case strings.Contains(host, "postmark"):
|
||||||
|
return "Postmark"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// DetectHostingProvider detects the hosting provider from HTTP headers
|
||||||
|
func DetectHostingProvider(headers http.Header) string {
|
||||||
|
server := strings.ToLower(headers.Get("Server"))
|
||||||
|
poweredBy := strings.ToLower(headers.Get("X-Powered-By"))
|
||||||
|
cfRay := headers.Get("CF-Ray")
|
||||||
|
vercelCache := headers.Get("X-Vercel-Cache")
|
||||||
|
vercelID := headers.Get("X-Vercel-Id")
|
||||||
|
netlifyID := headers.Get("X-NF-Request-Id")
|
||||||
|
githubRequest := headers.Get("X-GitHub-Request-Id")
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case vercelCache != "" || vercelID != "":
|
||||||
|
return "Vercel"
|
||||||
|
case netlifyID != "":
|
||||||
|
return "Netlify"
|
||||||
|
case githubRequest != "":
|
||||||
|
return "GitHub Pages"
|
||||||
|
case cfRay != "":
|
||||||
|
return "Cloudflare"
|
||||||
|
case strings.Contains(server, "cloudflare"):
|
||||||
|
return "Cloudflare"
|
||||||
|
case strings.Contains(server, "nginx") && vercelCache != "":
|
||||||
|
return "Vercel"
|
||||||
|
case strings.Contains(server, "awselb") || strings.Contains(server, "elb"):
|
||||||
|
return "AWS"
|
||||||
|
case strings.Contains(server, "amazon"):
|
||||||
|
return "AWS"
|
||||||
|
case strings.Contains(server, "microsoft-iis"):
|
||||||
|
return "Microsoft Azure"
|
||||||
|
case strings.Contains(server, "google") || strings.Contains(server, "gws"):
|
||||||
|
return "Google Cloud"
|
||||||
|
case strings.Contains(server, "heroku"):
|
||||||
|
return "Heroku"
|
||||||
|
case strings.Contains(server, "digitalocean"):
|
||||||
|
return "DigitalOcean"
|
||||||
|
case strings.Contains(server, "linode"):
|
||||||
|
return "Linode"
|
||||||
|
case strings.Contains(server, "ovh"):
|
||||||
|
return "OVH"
|
||||||
|
case strings.Contains(server, "hetzner"):
|
||||||
|
return "Hetzner"
|
||||||
|
case strings.Contains(server, "fastly"):
|
||||||
|
return "Fastly"
|
||||||
|
case strings.Contains(server, "bunnycdn"):
|
||||||
|
return "BunnyCDN"
|
||||||
|
case strings.Contains(server, "keycdn"):
|
||||||
|
return "KeyCDN"
|
||||||
|
case strings.Contains(server, "stackpath"):
|
||||||
|
return "StackPath"
|
||||||
|
case strings.Contains(server, "sucuri"):
|
||||||
|
return "Sucuri"
|
||||||
|
case strings.Contains(poweredBy, "next.js") || strings.Contains(poweredBy, "nextjs"):
|
||||||
|
return "Vercel"
|
||||||
|
case strings.Contains(poweredBy, "php"):
|
||||||
|
return "PHP"
|
||||||
|
case strings.Contains(server, "apache"):
|
||||||
|
return "Apache"
|
||||||
|
case strings.Contains(server, "nginx"):
|
||||||
|
return "nginx"
|
||||||
|
case strings.Contains(server, "caddy"):
|
||||||
|
return "Caddy"
|
||||||
|
case strings.Contains(server, "lighttpd"):
|
||||||
|
return "Lighttpd"
|
||||||
|
case strings.Contains(server, "litespeed"):
|
||||||
|
return "LiteSpeed"
|
||||||
|
case strings.Contains(server, "openresty"):
|
||||||
|
return "OpenResty"
|
||||||
|
case strings.Contains(server, "jetty"):
|
||||||
|
return "Jetty"
|
||||||
|
case strings.Contains(server, "tomcat"):
|
||||||
|
return "Tomcat"
|
||||||
|
case strings.Contains(server, "iis"):
|
||||||
|
return "IIS"
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// DetectCertificateAuthority detects the CA from an issuer string
|
||||||
|
func DetectCertificateAuthority(issuer string) string {
|
||||||
|
issuerLower := strings.ToLower(issuer)
|
||||||
|
switch {
|
||||||
|
case strings.Contains(issuerLower, "let's encrypt"):
|
||||||
|
return "Let's Encrypt"
|
||||||
|
case strings.Contains(issuerLower, "digicert"):
|
||||||
|
return "DigiCert"
|
||||||
|
case strings.Contains(issuerLower, "sectigo") || strings.Contains(issuerLower, "comodoca"):
|
||||||
|
return "Sectigo"
|
||||||
|
case strings.Contains(issuerLower, "globalsign"):
|
||||||
|
return "GlobalSign"
|
||||||
|
case strings.Contains(issuerLower, "geotrust"):
|
||||||
|
return "GeoTrust"
|
||||||
|
case strings.Contains(issuerLower, "thawte"):
|
||||||
|
return "Thawte"
|
||||||
|
case strings.Contains(issuerLower, "rapidssl"):
|
||||||
|
return "RapidSSL"
|
||||||
|
case strings.Contains(issuerLower, "symantec"):
|
||||||
|
return "Symantec"
|
||||||
|
case strings.Contains(issuerLower, "entrust"):
|
||||||
|
return "Entrust"
|
||||||
|
case strings.Contains(issuerLower, "certum"):
|
||||||
|
return "Certum"
|
||||||
|
case strings.Contains(issuerLower, "go daddy") || strings.Contains(issuerLower, "godaddy"):
|
||||||
|
return "GoDaddy"
|
||||||
|
case strings.Contains(issuerLower, "amazon"):
|
||||||
|
return "Amazon"
|
||||||
|
case strings.Contains(issuerLower, "google") && strings.Contains(issuerLower, "trust"):
|
||||||
|
return "Google Trust Services"
|
||||||
|
case strings.Contains(issuerLower, "cloudflare"):
|
||||||
|
return "Cloudflare"
|
||||||
|
case strings.Contains(issuerLower, "zero ssl") || strings.Contains(issuerLower, "zerossl"):
|
||||||
|
return "ZeroSSL"
|
||||||
|
case strings.Contains(issuerLower, "ssl.com"):
|
||||||
|
return "SSL.com"
|
||||||
|
case strings.Contains(issuerLower, "buypass"):
|
||||||
|
return "Buypass"
|
||||||
|
case strings.Contains(issuerLower, "harica"):
|
||||||
|
return "HARICA"
|
||||||
|
case strings.Contains(issuerLower, " Actalis "):
|
||||||
|
return "Actalis"
|
||||||
|
case strings.Contains(issuerLower, "swisssign"):
|
||||||
|
return "SwissSign"
|
||||||
|
case strings.Contains(issuerLower, "telekom"):
|
||||||
|
return "Telekom"
|
||||||
|
case strings.Contains(issuerLower, "trustwave"):
|
||||||
|
return "Trustwave"
|
||||||
|
case strings.Contains(issuerLower, "identrust"):
|
||||||
|
return "IdenTrust"
|
||||||
|
case strings.Contains(issuerLower, "usertrust"):
|
||||||
|
return "UserTrust"
|
||||||
|
case strings.Contains(issuerLower, "isrg") || strings.Contains(issuerLower, "internet security research"):
|
||||||
|
return "Let's Encrypt"
|
||||||
|
}
|
||||||
|
return issuer
|
||||||
|
}
|
||||||
@@ -151,13 +151,13 @@ func (s *Scheduler) checkDomain(record *core.Record) error {
|
|||||||
oldRecord := record.Fresh()
|
oldRecord := record.Fresh()
|
||||||
|
|
||||||
// Update record (only overwrite if new data is present to preserve valid data on partial lookups)
|
// Update record (only overwrite if new data is present to preserve valid data on partial lookups)
|
||||||
if newData.ExpiryDate != nil {
|
if newData.ExpiryDate != nil && newData.ExpiryDate.After(time.Time{}) {
|
||||||
record.Set("expiry_date", *newData.ExpiryDate)
|
record.Set("expiry_date", *newData.ExpiryDate)
|
||||||
}
|
}
|
||||||
if newData.CreationDate != nil {
|
if newData.CreationDate != nil && newData.CreationDate.After(time.Time{}) {
|
||||||
record.Set("creation_date", *newData.CreationDate)
|
record.Set("creation_date", *newData.CreationDate)
|
||||||
}
|
}
|
||||||
if newData.UpdatedDate != nil {
|
if newData.UpdatedDate != nil && newData.UpdatedDate.After(time.Time{}) {
|
||||||
record.Set("updated_date", *newData.UpdatedDate)
|
record.Set("updated_date", *newData.UpdatedDate)
|
||||||
}
|
}
|
||||||
if newData.RegistrarName != "" {
|
if newData.RegistrarName != "" {
|
||||||
@@ -262,9 +262,28 @@ func (s *Scheduler) checkDomain(record *core.Record) error {
|
|||||||
if newData.AbuseEmail != "" {
|
if newData.AbuseEmail != "" {
|
||||||
record.Set("abuse_email", newData.AbuseEmail)
|
record.Set("abuse_email", newData.AbuseEmail)
|
||||||
}
|
}
|
||||||
if newData.AbusePhone != "" {
|
|
||||||
record.Set("abuse_phone", newData.AbusePhone)
|
record.Set("abuse_phone", newData.AbusePhone)
|
||||||
|
record.Set("dns_provider", newData.DNSProvider)
|
||||||
|
record.Set("hosting_provider", newData.HostingProvider)
|
||||||
|
record.Set("email_provider", newData.EmailProvider)
|
||||||
|
record.Set("ca_provider", newData.CAProvider)
|
||||||
|
if len(newData.Headers) > 0 {
|
||||||
|
record.Set("headers", newData.Headers)
|
||||||
}
|
}
|
||||||
|
if len(newData.Certificates) > 0 {
|
||||||
|
record.Set("certificates", newData.Certificates)
|
||||||
|
}
|
||||||
|
if newData.SEOMeta != nil {
|
||||||
|
record.Set("seo_meta", newData.SEOMeta)
|
||||||
|
}
|
||||||
|
record.Set("whois_raw", newData.WHOISRaw)
|
||||||
|
record.Set("privacy_enabled", newData.PrivacyEnabled)
|
||||||
|
record.Set("transfer_lock", newData.TransferLock)
|
||||||
|
record.Set("tld", newData.TLD)
|
||||||
|
if len(newData.DomainStatuses) > 0 {
|
||||||
|
record.Set("domain_statuses", newData.DomainStatuses)
|
||||||
|
}
|
||||||
|
record.Set("host_country_code", newData.HostCountryCode)
|
||||||
record.Set("last_checked", time.Now())
|
record.Set("last_checked", time.Now())
|
||||||
|
|
||||||
// Update status - fallback to existing record expiry if new lookup didn't return one
|
// Update status - fallback to existing record expiry if new lookup didn't return one
|
||||||
@@ -334,66 +353,38 @@ func (s *Scheduler) checkDomain(record *core.Record) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Discover and save subdomains
|
// Discover and save subdomains using enhanced discovery
|
||||||
s.discoverSubdomains(record, domainName, userID)
|
s.discoverSubdomainsEnhanced(record, domainName, userID)
|
||||||
|
|
||||||
log.Printf("[domain-scheduler] Updated domain: %s (status: %s)", domainName, status)
|
log.Printf("[domain-scheduler] Updated domain: %s (status: %s)", domainName, status)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// discoverSubdomains discovers and saves subdomains for a domain
|
// discoverSubdomains discovers and saves subdomains for a domain (legacy method)
|
||||||
func (s *Scheduler) discoverSubdomains(record *core.Record, domainName, userID string) {
|
func (s *Scheduler) discoverSubdomains(record *core.Record, domainName, userID string) {
|
||||||
// Common subdomains to check
|
// Deprecated: Use discoverSubdomainsEnhanced instead
|
||||||
commonSubdomains := []string{
|
s.discoverSubdomainsEnhanced(record, domainName, userID)
|
||||||
"www", "mail", "ftp", "api", "blog", "shop", "admin", "app", "cdn",
|
}
|
||||||
"static", "dev", "staging", "test", "demo", "docs", "support", "help",
|
|
||||||
"status", "monitor", "grafana", "prometheus", "db", "cache", "redis",
|
|
||||||
"queue", "worker", "backup", "media", "assets", "download", "upload",
|
|
||||||
"git", "gitlab", "github", "jenkins", "ci", "cd", "vpn", "ssh",
|
|
||||||
"smtp", "imap", "mx", "webmail", "email", "analytics", "stats",
|
|
||||||
"search", "login", "auth", "sso", "oauth", "account", "user",
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get existing subdomains to avoid duplicates
|
// discoverSubdomainsEnhanced performs enhanced subdomain discovery
|
||||||
existing, _ := s.app.FindAllRecords("subdomains",
|
func (s *Scheduler) discoverSubdomainsEnhanced(record *core.Record, domainName, userID string) {
|
||||||
dbx.NewExp("domain = {:domain}", dbx.Params{"domain": record.Id}),
|
discovery := NewSubdomainDiscovery(s.app)
|
||||||
)
|
|
||||||
existingMap := make(map[string]bool)
|
|
||||||
for _, sub := range existing {
|
|
||||||
existingMap[sub.GetString("subdomain_name")] = true
|
|
||||||
}
|
|
||||||
|
|
||||||
collection, err := s.app.FindCollectionByNameOrId("subdomains")
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
results, err := discovery.Discover(ctx, domainName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Printf("[domain-scheduler] Subdomain discovery failed for %s: %v", domainName, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, sub := range commonSubdomains {
|
if err := discovery.SaveSubdomains(record, results, userID); err != nil {
|
||||||
if existingMap[sub] {
|
log.Printf("[domain-scheduler] Failed to save subdomains for %s: %v", domainName, err)
|
||||||
continue
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
fullDomain := sub + "." + domainName
|
log.Printf("[domain-scheduler] Discovered %d subdomains for %s", len(results), domainName)
|
||||||
ips, err := net.LookupHost(fullDomain)
|
|
||||||
if err != nil || len(ips) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Found a valid subdomain
|
|
||||||
subRecord := core.NewRecord(collection)
|
|
||||||
subRecord.Set("domain", record.Id)
|
|
||||||
subRecord.Set("subdomain_name", sub)
|
|
||||||
subRecord.Set("status", "active")
|
|
||||||
subRecord.Set("ip_addresses", strings.Join(ips, ","))
|
|
||||||
subRecord.Set("last_checked", time.Now())
|
|
||||||
subRecord.Set("user", userID)
|
|
||||||
|
|
||||||
if err := s.app.Save(subRecord); err != nil {
|
|
||||||
log.Printf("[domain-scheduler] Failed to save subdomain %s: %v", fullDomain, err)
|
|
||||||
} else {
|
|
||||||
log.Printf("[domain-scheduler] Discovered subdomain: %s", fullDomain)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// trackChanges compares old and new data and returns history entries
|
// trackChanges compares old and new data and returns history entries
|
||||||
@@ -450,6 +441,61 @@ func (s *Scheduler) trackChanges(oldRecord *core.Record, newData *domain.Domain,
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check provider changes
|
||||||
|
providers := []struct {
|
||||||
|
field string
|
||||||
|
value string
|
||||||
|
}{
|
||||||
|
{"dns_provider", newData.DNSProvider},
|
||||||
|
{"hosting_provider", newData.HostingProvider},
|
||||||
|
{"email_provider", newData.EmailProvider},
|
||||||
|
{"ca_provider", newData.CAProvider},
|
||||||
|
}
|
||||||
|
for _, p := range providers {
|
||||||
|
oldVal := oldRecord.GetString(p.field)
|
||||||
|
if p.value != "" && p.value != oldVal && oldVal != "" {
|
||||||
|
history = append(history, domain.DomainHistory{
|
||||||
|
ChangeType: domain.ChangeTypeProvider,
|
||||||
|
FieldName: p.field,
|
||||||
|
OldValue: oldVal,
|
||||||
|
NewValue: p.value,
|
||||||
|
CreatedAt: now,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check security changes
|
||||||
|
if newData.PrivacyEnabled != oldRecord.GetBool("privacy_enabled") {
|
||||||
|
history = append(history, domain.DomainHistory{
|
||||||
|
ChangeType: domain.ChangeTypeSecurity,
|
||||||
|
FieldName: "privacy_enabled",
|
||||||
|
OldValue: fmt.Sprintf("%t", oldRecord.GetBool("privacy_enabled")),
|
||||||
|
NewValue: fmt.Sprintf("%t", newData.PrivacyEnabled),
|
||||||
|
CreatedAt: now,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if newData.TransferLock != oldRecord.GetBool("transfer_lock") {
|
||||||
|
history = append(history, domain.DomainHistory{
|
||||||
|
ChangeType: domain.ChangeTypeSecurity,
|
||||||
|
FieldName: "transfer_lock",
|
||||||
|
OldValue: fmt.Sprintf("%t", oldRecord.GetBool("transfer_lock")),
|
||||||
|
NewValue: fmt.Sprintf("%t", newData.TransferLock),
|
||||||
|
CreatedAt: now,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check host country code change
|
||||||
|
oldCountryCode := oldRecord.GetString("host_country_code")
|
||||||
|
if newData.HostCountryCode != "" && newData.HostCountryCode != oldCountryCode && oldCountryCode != "" {
|
||||||
|
history = append(history, domain.DomainHistory{
|
||||||
|
ChangeType: domain.ChangeTypeHost,
|
||||||
|
FieldName: "host_country_code",
|
||||||
|
OldValue: oldCountryCode,
|
||||||
|
NewValue: newData.HostCountryCode,
|
||||||
|
CreatedAt: now,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return history
|
return history
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,477 @@
|
|||||||
|
package domains
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/pocketbase/dbx"
|
||||||
|
"github.com/pocketbase/pocketbase/core"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SubdomainDiscovery handles advanced subdomain discovery
|
||||||
|
type SubdomainDiscovery struct {
|
||||||
|
app core.App
|
||||||
|
client *http.Client
|
||||||
|
wordlist []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSubdomainDiscovery creates a new subdomain discovery service
|
||||||
|
func NewSubdomainDiscovery(app core.App) *SubdomainDiscovery {
|
||||||
|
return &SubdomainDiscovery{
|
||||||
|
app: app,
|
||||||
|
client: &http.Client{
|
||||||
|
Timeout: 10 * time.Second,
|
||||||
|
Transport: &http.Transport{
|
||||||
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wordlist: getEnhancedWordlist(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DiscoveryResult represents a discovered subdomain
|
||||||
|
type DiscoveryResult struct {
|
||||||
|
Subdomain string `json:"subdomain"`
|
||||||
|
FullDomain string `json:"full_domain"`
|
||||||
|
IPAddresses []string `json:"ip_addresses"`
|
||||||
|
StatusCode int `json:"status_code,omitempty"`
|
||||||
|
Server string `json:"server,omitempty"`
|
||||||
|
Source string `json:"source"` // dns, certificate, http, etc.
|
||||||
|
FoundAt time.Time `json:"found_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Discover performs comprehensive subdomain discovery
|
||||||
|
func (sd *SubdomainDiscovery) Discover(ctx context.Context, domainName string) ([]DiscoveryResult, error) {
|
||||||
|
var results []DiscoveryResult
|
||||||
|
var mu sync.Mutex
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
|
// Create a channel for results
|
||||||
|
resultChan := make(chan DiscoveryResult, 100)
|
||||||
|
|
||||||
|
// Start multiple discovery methods concurrently
|
||||||
|
wg.Add(4)
|
||||||
|
|
||||||
|
// 1. DNS brute force with enhanced wordlist
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
sd.dnsBruteForce(ctx, domainName, resultChan)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// 2. Certificate transparency log search
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
sd.ctLogSearch(ctx, domainName, resultChan)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// 3. DNS enumeration via common patterns
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
sd.patternEnumeration(ctx, domainName, resultChan)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// 4. HTTP probe for common subdomains
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
sd.httpProbe(ctx, domainName, resultChan)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Collect results in a separate goroutine
|
||||||
|
go func() {
|
||||||
|
wg.Wait()
|
||||||
|
close(resultChan)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Process results
|
||||||
|
seen := make(map[string]bool)
|
||||||
|
for result := range resultChan {
|
||||||
|
if seen[result.Subdomain] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
seen[result.Subdomain] = true
|
||||||
|
|
||||||
|
mu.Lock()
|
||||||
|
results = append(results, result)
|
||||||
|
mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
return results, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// dnsBruteForce performs DNS brute forcing with wordlist
|
||||||
|
func (sd *SubdomainDiscovery) dnsBruteForce(ctx context.Context, domainName string, results chan<- DiscoveryResult) {
|
||||||
|
semaphore := make(chan struct{}, 20) // Limit concurrency
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
|
for _, word := range sd.wordlist {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
|
semaphore <- struct{}{}
|
||||||
|
|
||||||
|
go func(word string) {
|
||||||
|
defer wg.Done()
|
||||||
|
defer func() { <-semaphore }()
|
||||||
|
|
||||||
|
subdomain := word + "." + domainName
|
||||||
|
ips, err := net.LookupHost(subdomain)
|
||||||
|
if err != nil || len(ips) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
results <- DiscoveryResult{
|
||||||
|
Subdomain: word,
|
||||||
|
FullDomain: subdomain,
|
||||||
|
IPAddresses: ips,
|
||||||
|
Source: "dns",
|
||||||
|
FoundAt: time.Now(),
|
||||||
|
}
|
||||||
|
}(word)
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ctLogSearch searches certificate transparency logs via crt.sh
|
||||||
|
func (sd *SubdomainDiscovery) ctLogSearch(ctx context.Context, domainName string, results chan<- DiscoveryResult) {
|
||||||
|
// Query crt.sh for certificates
|
||||||
|
url := fmt.Sprintf("https://crt.sh/?q=%%.%s&output=json", domainName)
|
||||||
|
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[subdomain-discovery] CT log search failed for %s: %v", domainName, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := sd.client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[subdomain-discovery] CT log search failed for %s: %v", domainName, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
log.Printf("[subdomain-discovery] CT log search returned status %d for %s", resp.StatusCode, domainName)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse crt.sh JSON response
|
||||||
|
var entries []struct {
|
||||||
|
NameValue string `json:"name_value"`
|
||||||
|
}
|
||||||
|
if err := json.NewDecoder(resp.Body).Decode(&entries); err != nil {
|
||||||
|
log.Printf("[subdomain-discovery] Failed to parse CT log response for %s: %v", domainName, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
seen := make(map[string]bool)
|
||||||
|
for _, entry := range entries {
|
||||||
|
// crt.sh returns one name_value per line, may contain wildcards or multiple names
|
||||||
|
names := strings.Split(entry.NameValue, "\n")
|
||||||
|
for _, name := range names {
|
||||||
|
name = strings.TrimSpace(name)
|
||||||
|
if name == "" || name == domainName {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Remove wildcard prefix
|
||||||
|
name = strings.TrimPrefix(name, "*.")
|
||||||
|
// Only include subdomains of the target domain
|
||||||
|
if !strings.HasSuffix(name, "."+domainName) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
subdomain := strings.TrimSuffix(name, "."+domainName)
|
||||||
|
if subdomain == "" || seen[subdomain] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
seen[subdomain] = true
|
||||||
|
|
||||||
|
// Try to resolve IPs
|
||||||
|
ips, _ := net.LookupHost(name)
|
||||||
|
|
||||||
|
results <- DiscoveryResult{
|
||||||
|
Subdomain: subdomain,
|
||||||
|
FullDomain: name,
|
||||||
|
IPAddresses: ips,
|
||||||
|
Source: "certificate",
|
||||||
|
FoundAt: time.Now(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[subdomain-discovery] CT log search found %d unique subdomains for %s", len(seen), domainName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// patternEnumeration enumerates common subdomain patterns
|
||||||
|
func (sd *SubdomainDiscovery) patternEnumeration(ctx context.Context, domainName string, results chan<- DiscoveryResult) {
|
||||||
|
patterns := []string{
|
||||||
|
"api-v1", "api-v2", "api-v3", "api-dev", "api-staging", "api-prod",
|
||||||
|
"app-dev", "app-staging", "app-prod", "web-dev", "web-staging",
|
||||||
|
"admin-dev", "admin-staging", "admin-prod",
|
||||||
|
"portal-dev", "portal-staging", "portal-prod",
|
||||||
|
"dashboard-dev", "dashboard-staging", "dashboard-prod",
|
||||||
|
"service-1", "service-2", "service-3",
|
||||||
|
"node-1", "node-2", "node-3",
|
||||||
|
"server-1", "server-2", "server-3",
|
||||||
|
"web-01", "web-02", "web-03",
|
||||||
|
"app-01", "app-02", "app-03",
|
||||||
|
"us-east", "us-west", "eu-west", "eu-central", "ap-south", "ap-northeast",
|
||||||
|
}
|
||||||
|
|
||||||
|
semaphore := make(chan struct{}, 10)
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
|
for _, pattern := range patterns {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
|
semaphore <- struct{}{}
|
||||||
|
|
||||||
|
go func(pattern string) {
|
||||||
|
defer wg.Done()
|
||||||
|
defer func() { <-semaphore }()
|
||||||
|
|
||||||
|
subdomain := pattern + "." + domainName
|
||||||
|
ips, err := net.LookupHost(subdomain)
|
||||||
|
if err != nil || len(ips) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
results <- DiscoveryResult{
|
||||||
|
Subdomain: pattern,
|
||||||
|
FullDomain: subdomain,
|
||||||
|
IPAddresses: ips,
|
||||||
|
Source: "pattern",
|
||||||
|
FoundAt: time.Now(),
|
||||||
|
}
|
||||||
|
}(pattern)
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
// httpProbe probes common subdomains via HTTP
|
||||||
|
func (sd *SubdomainDiscovery) httpProbe(ctx context.Context, domainName string, results chan<- DiscoveryResult) {
|
||||||
|
commonWebSubdomains := []string{"www", "app", "api", "admin", "dashboard", "portal", "web"}
|
||||||
|
|
||||||
|
semaphore := make(chan struct{}, 5)
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
|
for _, sub := range commonWebSubdomains {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
|
semaphore <- struct{}{}
|
||||||
|
|
||||||
|
go func(sub string) {
|
||||||
|
defer wg.Done()
|
||||||
|
defer func() { <-semaphore }()
|
||||||
|
|
||||||
|
// Try HTTPS first
|
||||||
|
url := fmt.Sprintf("https://%s.%s", sub, domainName)
|
||||||
|
resp, err := sd.client.Get(url)
|
||||||
|
if err != nil {
|
||||||
|
// Try HTTP
|
||||||
|
url = fmt.Sprintf("http://%s.%s", sub, domainName)
|
||||||
|
resp, err = sd.client.Get(url)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
// Get IP addresses
|
||||||
|
subdomain := sub + "." + domainName
|
||||||
|
ips, _ := net.LookupHost(subdomain)
|
||||||
|
|
||||||
|
results <- DiscoveryResult{
|
||||||
|
Subdomain: sub,
|
||||||
|
FullDomain: subdomain,
|
||||||
|
IPAddresses: ips,
|
||||||
|
StatusCode: resp.StatusCode,
|
||||||
|
Server: resp.Header.Get("Server"),
|
||||||
|
Source: "http",
|
||||||
|
FoundAt: time.Now(),
|
||||||
|
}
|
||||||
|
}(sub)
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SaveSubdomains saves discovered subdomains to the database
|
||||||
|
func (sd *SubdomainDiscovery) SaveSubdomains(domainRecord *core.Record, results []DiscoveryResult, userID string) error {
|
||||||
|
collection, err := sd.app.FindCollectionByNameOrId("subdomains")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get existing subdomains to avoid duplicates
|
||||||
|
existing, _ := sd.app.FindAllRecords("subdomains",
|
||||||
|
dbx.NewExp("domain = {:domain}", dbx.Params{"domain": domainRecord.Id}),
|
||||||
|
)
|
||||||
|
existingMap := make(map[string]bool)
|
||||||
|
for _, sub := range existing {
|
||||||
|
existingMap[sub.GetString("subdomain_name")] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, result := range results {
|
||||||
|
if existingMap[result.Subdomain] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
record := core.NewRecord(collection)
|
||||||
|
record.Set("domain", domainRecord.Id)
|
||||||
|
record.Set("subdomain_name", result.Subdomain)
|
||||||
|
record.Set("full_domain", result.FullDomain)
|
||||||
|
record.Set("status", "active")
|
||||||
|
record.Set("ip_addresses", strings.Join(result.IPAddresses, ","))
|
||||||
|
record.Set("discovery_source", result.Source)
|
||||||
|
record.Set("last_checked", time.Now())
|
||||||
|
record.Set("user", userID)
|
||||||
|
|
||||||
|
if result.StatusCode > 0 {
|
||||||
|
record.Set("http_status", result.StatusCode)
|
||||||
|
}
|
||||||
|
if result.Server != "" {
|
||||||
|
record.Set("server_header", result.Server)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := sd.app.Save(record); err != nil {
|
||||||
|
log.Printf("[subdomain-discovery] Failed to save subdomain %s: %v", result.FullDomain, err)
|
||||||
|
} else {
|
||||||
|
log.Printf("[subdomain-discovery] Saved subdomain: %s (source: %s)", result.FullDomain, result.Source)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getEnhancedWordlist returns an enhanced wordlist for subdomain discovery
|
||||||
|
func getEnhancedWordlist() []string {
|
||||||
|
// Comprehensive wordlist including common subdomains
|
||||||
|
wordlist := []string{
|
||||||
|
// Common web
|
||||||
|
"www", "mail", "ftp", "localhost", "admin", "dashboard", "portal",
|
||||||
|
"api", "app", "mobile", "dev", "test", "staging", "demo", "beta",
|
||||||
|
"prod", "production", "live", "www2", "www1", "blog", "shop", "store",
|
||||||
|
"support", "help", "docs", "documentation", "wiki", "forum", "community",
|
||||||
|
"news", "media", "cdn", "static", "assets", "files", "download", "upload",
|
||||||
|
"images", "img", "css", "js", "scripts", "resources", "public", "private",
|
||||||
|
|
||||||
|
// Services
|
||||||
|
"api-v1", "api-v2", "api-v3", "api-dev", "api-staging", "api-prod",
|
||||||
|
"graphql", "rest", "grpc", "websocket", "socket", "ws", "wss",
|
||||||
|
"oauth", "auth", "login", "signin", "signup", "register", "sso",
|
||||||
|
"saml", "oidc", "openid", "keycloak", "auth0", "cognito", "okta",
|
||||||
|
|
||||||
|
// Infrastructure
|
||||||
|
"server", "server1", "server2", "server3", "srv", "srv1", "srv2",
|
||||||
|
"node", "node1", "node2", "node3", "worker", "worker1", "worker2",
|
||||||
|
"web", "web1", "web2", "web3", "app1", "app2", "app3",
|
||||||
|
"db", "database", "mysql", "postgres", "mongodb", "redis", "elasticsearch",
|
||||||
|
"cache", "queue", "rabbitmq", "kafka", "zookeeper", "etcd",
|
||||||
|
|
||||||
|
// Cloud & DevOps
|
||||||
|
"k8s", "kubernetes", "docker", "swarm", "nomad", "consul", "vault",
|
||||||
|
"terraform", "ansible", "puppet", "chef", "salt", "jenkins", "gitlab",
|
||||||
|
"github", "bitbucket", "travis", "circleci", "drone", "argo", "spinnaker",
|
||||||
|
"prometheus", "grafana", "alertmanager", "thanos", "loki", "tempo",
|
||||||
|
"jaeger", "zipkin", "kibana", "elk", "efk", "splunk", "datadog",
|
||||||
|
|
||||||
|
// Security
|
||||||
|
"vpn", "ipsec", "openvpn", "wireguard", "fortinet", "paloalto",
|
||||||
|
"firewall", "waf", "ids", "ips", "siem", "soc", "nids",
|
||||||
|
"scan", "scanner", "security", "sec", "pentest", "vulnerability",
|
||||||
|
|
||||||
|
// Monitoring & Logging
|
||||||
|
"monitor", "monitoring", "status", "health", "healthcheck", "ping",
|
||||||
|
"uptime", "metrics", "logs", "logging", "audit", "trace", "apm",
|
||||||
|
"sentry", "bugsnag", "raygun", "rollbar", "airbrake", "honeybadger",
|
||||||
|
"newrelic", "appdynamics", "dynatrace", "instana", "scout", "skylight",
|
||||||
|
|
||||||
|
// Communication
|
||||||
|
"mail", "email", "smtp", "pop", "imap", "exchange", "webmail",
|
||||||
|
"mailgun", "sendgrid", "mailchimp", " SES", "postmark", "sparkpost",
|
||||||
|
"chat", "slack", "teams", "discord", "zoom", "meet", "webex",
|
||||||
|
"jitsi", "mattermost", "rocket", "element", "matrix",
|
||||||
|
|
||||||
|
// Regions
|
||||||
|
"us", "us-east", "us-west", "us-central", "us-south",
|
||||||
|
"eu", "eu-west", "eu-central", "eu-east", "eu-north", "eu-south",
|
||||||
|
"ap", "ap-south", "ap-northeast", "ap-southeast", "ap-east",
|
||||||
|
"sa", "sa-east", "af", "af-south", "me", "me-south",
|
||||||
|
|
||||||
|
// Environments
|
||||||
|
"sandbox", "playground", "lab", "labs", "experiment", "canary",
|
||||||
|
"blue", "green", "a", "b", "alpha", "beta", "gamma", "delta",
|
||||||
|
"rc", "release", "nightly", "stable", "latest", "edge", "lts",
|
||||||
|
|
||||||
|
// Corporate
|
||||||
|
"corp", "corporate", "enterprise", "business", "company", "org",
|
||||||
|
"intranet", "extranet", "partners", "vendors", "suppliers",
|
||||||
|
"hr", "finance", "accounting", "legal", "compliance", "it",
|
||||||
|
"sales", "marketing", "crm", "erp", "scm", "plm", "hrm",
|
||||||
|
|
||||||
|
// Development tools
|
||||||
|
"git", "svn", "cvs", "mercurial", "hg", "bitkeeper",
|
||||||
|
"repo", "repository", "repos", "source", "code", "src",
|
||||||
|
"build", "ci", "cd", "deploy", "deployment", "release",
|
||||||
|
"artifact", "artifacts", "package", "packages", "registry",
|
||||||
|
|
||||||
|
// Testing
|
||||||
|
"qa", "qc", "test", "testing", "tests", "spec", "specs",
|
||||||
|
"unit", "integration", "e2e", "acceptance", "regression",
|
||||||
|
"mock", "stub", "fake", "dummy", "sandbox", "fixture",
|
||||||
|
|
||||||
|
// Archive & Backup
|
||||||
|
"archive", "archives", "backup", "backups", "snapshot", "snapshots",
|
||||||
|
"old", "legacy", "deprecated", "retired", "historical",
|
||||||
|
|
||||||
|
// Miscellaneous
|
||||||
|
"search", "find", "lookup", "query", "browse", "explorer",
|
||||||
|
"api-docs", "swagger", "openapi", "redoc", "postman", "graphql-playground",
|
||||||
|
"status-page", "statuspage", "trust", "security", "compliance",
|
||||||
|
"terms", "privacy", "legal", "about", "contact", "feedback",
|
||||||
|
"careers", "jobs", "team", "people", "staff", "employees",
|
||||||
|
"press", "media-kit", "brand", "assets", "styleguide", "design",
|
||||||
|
"sitemap", "robots", "humans", "security", "pgp", "key",
|
||||||
|
"invite", "join", "apply", "subscribe", "newsletter", "rss", "atom",
|
||||||
|
"mobile", "m", "touch", "app", "ios", "android", "windows", "mac",
|
||||||
|
"download", "downloads", "dl", "installer", "setup", "update", "upgrade",
|
||||||
|
"payment", "pay", "billing", "invoice", "subscription", "checkout",
|
||||||
|
"cart", "basket", "shop", "store", "buy", "purchase", "order",
|
||||||
|
"account", "my", "profile", "user", "users", "member", "members",
|
||||||
|
"session", "token", "auth", "verify", "validation", "confirm",
|
||||||
|
"reset", "recovery", "forgot", "password", "pass", "pwd",
|
||||||
|
"2fa", "mfa", "totp", "otp", "authenticator", "security-key",
|
||||||
|
"webhook", "callback", "hook", "integration", "connector", "adapter",
|
||||||
|
"plugin", "extension", "addon", "module", "component", "widget",
|
||||||
|
"embed", "iframe", "frame", "proxy", "gateway", "ingress", "egress",
|
||||||
|
"loadbalancer", "lb", "vip", "floating", "virtual", "ha", "failover",
|
||||||
|
"primary", "secondary", "master", "slave", "leader", "follower",
|
||||||
|
"active", "standby", "hot", "warm", "cold", "mirror", "replica",
|
||||||
|
"shard", "shards", "partition", "partitions", "segment", "segments",
|
||||||
|
}
|
||||||
|
|
||||||
|
return wordlist
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
+116
-10
@@ -7,6 +7,8 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/henrygd/beszel/internal/entities/monitor"
|
"github.com/henrygd/beszel/internal/entities/monitor"
|
||||||
|
"github.com/henrygd/beszel/internal/hub/pagespeed"
|
||||||
|
"github.com/henrygd/beszel/internal/hub/utils"
|
||||||
"github.com/pocketbase/dbx"
|
"github.com/pocketbase/dbx"
|
||||||
"github.com/pocketbase/pocketbase/core"
|
"github.com/pocketbase/pocketbase/core"
|
||||||
)
|
)
|
||||||
@@ -40,16 +42,24 @@ func (h *APIHandler) RegisterRoutes(se *core.ServeEvent) {
|
|||||||
// CRUD endpoints
|
// CRUD endpoints
|
||||||
api.GET("", h.listMonitors)
|
api.GET("", h.listMonitors)
|
||||||
api.POST("", h.createMonitor)
|
api.POST("", h.createMonitor)
|
||||||
api.GET("/:id", h.getMonitor)
|
api.GET("/{id}", h.getMonitor)
|
||||||
api.PATCH("/:id", h.updateMonitor)
|
api.PATCH("/{id}", h.updateMonitor)
|
||||||
api.DELETE("/:id", h.deleteMonitor)
|
api.DELETE("/{id}", h.deleteMonitor)
|
||||||
|
|
||||||
// Action endpoints
|
// Action endpoints
|
||||||
api.POST("/:id/check", h.manualCheck)
|
api.POST("/{id}/check", h.manualCheck)
|
||||||
api.POST("/:id/pause", h.pauseMonitor)
|
api.POST("/{id}/pause", h.pauseMonitor)
|
||||||
api.POST("/:id/resume", h.resumeMonitor)
|
api.POST("/{id}/resume", h.resumeMonitor)
|
||||||
api.GET("/:id/stats", h.getStats)
|
api.GET("/{id}/stats", h.getStats)
|
||||||
api.GET("/:id/heartbeats", h.getHeartbeats)
|
api.GET("/{id}/heartbeats", h.getHeartbeats)
|
||||||
|
api.POST("/{id}/pagespeed", h.runPageSpeedCheck)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HeartbeatSummary represents a minimal heartbeat for the monitor list
|
||||||
|
type HeartbeatSummary struct {
|
||||||
|
Status string `json:"status"`
|
||||||
|
Ping int `json:"ping"`
|
||||||
|
Time time.Time `json:"time"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// MonitorResponse represents a monitor in API responses
|
// MonitorResponse represents a monitor in API responses
|
||||||
@@ -69,6 +79,7 @@ type MonitorResponse struct {
|
|||||||
Description string `json:"description,omitempty"`
|
Description string `json:"description,omitempty"`
|
||||||
LastCheck *time.Time `json:"last_check,omitempty"`
|
LastCheck *time.Time `json:"last_check,omitempty"`
|
||||||
UptimeStats map[string]float64 `json:"uptime_stats,omitempty"`
|
UptimeStats map[string]float64 `json:"uptime_stats,omitempty"`
|
||||||
|
RecentHeartbeats []HeartbeatSummary `json:"recent_heartbeats,omitempty"`
|
||||||
Tags []string `json:"tags,omitempty"`
|
Tags []string `json:"tags,omitempty"`
|
||||||
Keyword string `json:"keyword,omitempty"`
|
Keyword string `json:"keyword,omitempty"`
|
||||||
JSONQuery string `json:"json_query,omitempty"`
|
JSONQuery string `json:"json_query,omitempty"`
|
||||||
@@ -165,6 +176,35 @@ func (h *APIHandler) listMonitors(e *core.RequestEvent) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getRecentHeartbeats fetches the last N heartbeats for a monitor
|
||||||
|
func (h *APIHandler) getRecentHeartbeats(monitorID string, limit int) []HeartbeatSummary {
|
||||||
|
records, err := h.app.FindRecordsByFilter(
|
||||||
|
"monitor_heartbeats",
|
||||||
|
"monitor = {:monitorId}",
|
||||||
|
"-time",
|
||||||
|
limit,
|
||||||
|
0,
|
||||||
|
map[string]any{"monitorId": monitorID},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
heartbeats := make([]HeartbeatSummary, 0, len(records))
|
||||||
|
for _, hb := range records {
|
||||||
|
var t time.Time
|
||||||
|
if ts := hb.GetDateTime("time"); !ts.IsZero() {
|
||||||
|
t = ts.Time()
|
||||||
|
}
|
||||||
|
heartbeats = append(heartbeats, HeartbeatSummary{
|
||||||
|
Status: hb.GetString("status"),
|
||||||
|
Ping: hb.GetInt("ping"),
|
||||||
|
Time: t,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return heartbeats
|
||||||
|
}
|
||||||
|
|
||||||
// getMonitor returns a single monitor by ID
|
// getMonitor returns a single monitor by ID
|
||||||
func (h *APIHandler) getMonitor(e *core.RequestEvent) error {
|
func (h *APIHandler) getMonitor(e *core.RequestEvent) error {
|
||||||
id := e.Request.PathValue("id")
|
id := e.Request.PathValue("id")
|
||||||
@@ -182,7 +222,10 @@ func (h *APIHandler) getMonitor(e *core.RequestEvent) error {
|
|||||||
return e.ForbiddenError("Access denied", nil)
|
return e.ForbiddenError("Access denied", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
return e.JSON(http.StatusOK, recordToResponse(record))
|
resp := recordToResponse(record)
|
||||||
|
resp.RecentHeartbeats = h.getRecentHeartbeats(id, 12)
|
||||||
|
|
||||||
|
return e.JSON(http.StatusOK, resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
// createMonitor creates a new monitor
|
// createMonitor creates a new monitor
|
||||||
@@ -253,8 +296,12 @@ func (h *APIHandler) createMonitor(e *core.RequestEvent) error {
|
|||||||
h.scheduler.AddMonitor(record)
|
h.scheduler.AddMonitor(record)
|
||||||
|
|
||||||
// Run initial check synchronously so the monitor shows real status immediately
|
// Run initial check synchronously so the monitor shows real status immediately
|
||||||
if _, err := h.scheduler.RunManualCheck(record.Id); err != nil {
|
result, err := h.scheduler.RunManualCheck(record.Id)
|
||||||
|
if err != nil {
|
||||||
log.Printf("[monitor-api] Initial check failed for %s: %v", record.Id, err)
|
log.Printf("[monitor-api] Initial check failed for %s: %v", record.Id, err)
|
||||||
|
// Note: The monitor will remain in pending status and the scheduler will retry
|
||||||
|
} else {
|
||||||
|
log.Printf("[monitor-api] Initial check completed for %s: status=%s, ping=%v", record.Id, result.Status, result.Ping)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Re-fetch the updated record to get the new status
|
// Re-fetch the updated record to get the new status
|
||||||
@@ -565,6 +612,65 @@ func (h *APIHandler) getStats(e *core.RequestEvent) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// runPageSpeedCheck runs a PageSpeed Insights check for a monitor
|
||||||
|
func (h *APIHandler) runPageSpeedCheck(e *core.RequestEvent) error {
|
||||||
|
id := e.Request.PathValue("id")
|
||||||
|
if id == "" {
|
||||||
|
return e.BadRequestError("Monitor ID is required", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
record, err := h.app.FindRecordById("monitors", id)
|
||||||
|
if err != nil {
|
||||||
|
return e.NotFoundError("Monitor not found", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if record.GetString("user") != e.Auth.Id {
|
||||||
|
return e.ForbiddenError("Access denied", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
url := record.GetString("url")
|
||||||
|
if url == "" {
|
||||||
|
return e.BadRequestError("Monitor does not have a URL", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get strategy from query param, default to mobile
|
||||||
|
strategy := e.Request.URL.Query().Get("strategy")
|
||||||
|
if strategy == "" {
|
||||||
|
strategy = "mobile"
|
||||||
|
}
|
||||||
|
if strategy != "mobile" && strategy != "desktop" {
|
||||||
|
return e.BadRequestError("strategy must be 'mobile' or 'desktop'", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
apiKey, _ := utils.GetEnv("PAGESPEED_API_KEY")
|
||||||
|
checker := pagespeed.NewChecker(apiKey)
|
||||||
|
metrics, err := checker.CheckURL(url, strategy)
|
||||||
|
if err != nil {
|
||||||
|
return e.BadRequestError("PageSpeed check failed: "+err.Error(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
vitals := pagespeed.GetCoreWebVitalsStatus(metrics)
|
||||||
|
|
||||||
|
return e.JSON(http.StatusOK, map[string]interface{}{
|
||||||
|
"performance": metrics.Performance,
|
||||||
|
"accessibility": metrics.Accessibility,
|
||||||
|
"bestPractices": metrics.BestPractices,
|
||||||
|
"seo": metrics.SEO,
|
||||||
|
"pwa": metrics.PWA,
|
||||||
|
"fcp": metrics.FCP,
|
||||||
|
"lcp": metrics.LCP,
|
||||||
|
"ttfb": metrics.TTFB,
|
||||||
|
"cls": metrics.CLS,
|
||||||
|
"tbt": metrics.TBT,
|
||||||
|
"speedIndex": metrics.SpeedIndex,
|
||||||
|
"tti": metrics.TTI,
|
||||||
|
"strategy": metrics.Strategy,
|
||||||
|
"checkedAt": metrics.CheckedAt,
|
||||||
|
"url": metrics.URL,
|
||||||
|
"vitals": vitals,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// getHeartbeats returns recent heartbeats for a monitor
|
// getHeartbeats returns recent heartbeats for a monitor
|
||||||
func (h *APIHandler) getHeartbeats(e *core.RequestEvent) error {
|
func (h *APIHandler) getHeartbeats(e *core.RequestEvent) error {
|
||||||
id := e.Request.PathValue("id")
|
id := e.Request.PathValue("id")
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -99,7 +100,7 @@ func NewChecker(apiKey string) *Checker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CheckURL runs a PageSpeed check on a URL
|
// CheckURL runs a PageSpeed check on a URL
|
||||||
func (c *Checker) CheckURL(url string, strategy string) (*Metrics, error) {
|
func (c *Checker) CheckURL(pageURL string, strategy string) (*Metrics, error) {
|
||||||
if strategy == "" {
|
if strategy == "" {
|
||||||
strategy = "mobile"
|
strategy = "mobile"
|
||||||
}
|
}
|
||||||
@@ -107,7 +108,7 @@ func (c *Checker) CheckURL(url string, strategy string) (*Metrics, error) {
|
|||||||
// Build PageSpeed API URL
|
// Build PageSpeed API URL
|
||||||
apiURL := fmt.Sprintf(
|
apiURL := fmt.Sprintf(
|
||||||
"https://www.googleapis.com/pagespeedonline/v5/runPagespeed?url=%s&strategy=%s&category=PERFORMANCE&category=ACCESSIBILITY&category=BEST_PRACTICES&category=SEO&category=PWA",
|
"https://www.googleapis.com/pagespeedonline/v5/runPagespeed?url=%s&strategy=%s&category=PERFORMANCE&category=ACCESSIBILITY&category=BEST_PRACTICES&category=SEO&category=PWA",
|
||||||
url,
|
url.QueryEscape(pageURL),
|
||||||
strategy,
|
strategy,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -132,7 +133,7 @@ func (c *Checker) CheckURL(url string, strategy string) (*Metrics, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
metrics := &Metrics{
|
metrics := &Metrics{
|
||||||
URL: url,
|
URL: pageURL,
|
||||||
Strategy: strategy,
|
Strategy: strategy,
|
||||||
CheckedAt: time.Now(),
|
CheckedAt: time.Now(),
|
||||||
Performance: result.LighthouseResult.Categories.Performance.Score * 100,
|
Performance: result.LighthouseResult.Categories.Performance.Score * 100,
|
||||||
@@ -192,6 +193,7 @@ func GetCoreWebVitalsStatus(metrics *Metrics) map[string]string {
|
|||||||
"cls": getCLSStatus(metrics.CLS),
|
"cls": getCLSStatus(metrics.CLS),
|
||||||
"fcp": getFCPStatus(metrics.FCP),
|
"fcp": getFCPStatus(metrics.FCP),
|
||||||
"ttfb": getTTFBStatus(metrics.TTFB),
|
"ttfb": getTTFBStatus(metrics.TTFB),
|
||||||
|
"tti": getTTIStatus(metrics.TTI),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,6 +242,15 @@ func getTTFBStatus(value float64) string {
|
|||||||
return "poor"
|
return "poor"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getTTIStatus(value float64) string {
|
||||||
|
if value <= 3800 {
|
||||||
|
return "good"
|
||||||
|
} else if value <= 7300 {
|
||||||
|
return "needs-improvement"
|
||||||
|
}
|
||||||
|
return "poor"
|
||||||
|
}
|
||||||
|
|
||||||
// FormatDuration formats milliseconds to readable string
|
// FormatDuration formats milliseconds to readable string
|
||||||
func FormatDuration(ms float64) string {
|
func FormatDuration(ms float64) string {
|
||||||
if ms < 1000 {
|
if ms < 1000 {
|
||||||
|
|||||||
@@ -0,0 +1,65 @@
|
|||||||
|
package migrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/pocketbase/pocketbase/core"
|
||||||
|
m "github.com/pocketbase/pocketbase/migrations"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
m.Register(func(app core.App) error {
|
||||||
|
if err := enhanceDomainCollection(app); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}, func(app core.App) error {
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func enhanceDomainCollection(app core.App) error {
|
||||||
|
collection, err := app.FindCollectionByNameOrId("domains")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Provider detection fields
|
||||||
|
addTextField3(collection, "dns_provider")
|
||||||
|
addTextField3(collection, "hosting_provider")
|
||||||
|
addTextField3(collection, "email_provider")
|
||||||
|
addTextField3(collection, "ca_provider")
|
||||||
|
|
||||||
|
// JSON fields for complex data
|
||||||
|
addJSONField3(collection, "headers")
|
||||||
|
addJSONField3(collection, "certificates")
|
||||||
|
addJSONField3(collection, "seo_meta")
|
||||||
|
addJSONField3(collection, "domain_statuses")
|
||||||
|
|
||||||
|
// WHOIS and registration fields
|
||||||
|
addTextField3(collection, "whois_raw")
|
||||||
|
addBoolField3(collection, "privacy_enabled")
|
||||||
|
addBoolField3(collection, "transfer_lock")
|
||||||
|
addTextField3(collection, "tld")
|
||||||
|
|
||||||
|
// Enhanced geo
|
||||||
|
addTextField3(collection, "host_country_code")
|
||||||
|
|
||||||
|
return app.Save(collection)
|
||||||
|
}
|
||||||
|
|
||||||
|
func addTextField3(collection *core.Collection, name string) {
|
||||||
|
if collection.Fields.GetByName(name) == nil {
|
||||||
|
collection.Fields.Add(&core.TextField{Name: name})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func addBoolField3(collection *core.Collection, name string) {
|
||||||
|
if collection.Fields.GetByName(name) == nil {
|
||||||
|
collection.Fields.Add(&core.BoolField{Name: name})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func addJSONField3(collection *core.Collection, name string) {
|
||||||
|
if collection.Fields.GetByName(name) == nil {
|
||||||
|
collection.Fields.Add(&core.JSONField{Name: name})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,11 +5,25 @@ import { useQuery } from "@tanstack/react-query"
|
|||||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/components/ui/button"
|
||||||
import { Link } from "@/components/router"
|
import { Link } from "@/components/router"
|
||||||
import { ChevronLeft, ChevronRight, Calendar as CalendarIcon, AlertCircle, Globe, Shield } from "lucide-react"
|
import { ChevronLeft, ChevronRight, Calendar as CalendarIcon, AlertCircle, Globe, Shield, Filter, X } from "lucide-react"
|
||||||
import { getCalendarEvents, type CalendarEvent } from "@/lib/incidents"
|
import { getCalendarEvents, type CalendarEvent } from "@/lib/incidents"
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuCheckboxItem,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuLabel,
|
||||||
|
DropdownMenuSeparator,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from "@/components/ui/dropdown-menu"
|
||||||
|
|
||||||
export function CalendarView() {
|
export function CalendarView() {
|
||||||
const [currentDate, setCurrentDate] = useState(new Date())
|
const [currentDate, setCurrentDate] = useState(new Date())
|
||||||
|
const [eventFilters, setEventFilters] = useState({
|
||||||
|
domain_expiry: true,
|
||||||
|
ssl_expiry: true,
|
||||||
|
incident: true,
|
||||||
|
})
|
||||||
const year = currentDate.getFullYear()
|
const year = currentDate.getFullYear()
|
||||||
const month = currentDate.getMonth()
|
const month = currentDate.getMonth()
|
||||||
|
|
||||||
@@ -46,20 +60,22 @@ export function CalendarView() {
|
|||||||
// Days of month
|
// Days of month
|
||||||
for (let day = 1; day <= daysInMonth; day++) {
|
for (let day = 1; day <= daysInMonth; day++) {
|
||||||
const dateStr = `${year}-${String(month + 1).padStart(2, "0")}-${String(day).padStart(2, "0")}`
|
const dateStr = `${year}-${String(month + 1).padStart(2, "0")}-${String(day).padStart(2, "0")}`
|
||||||
const dayEvents = events?.filter((e) => e.date === dateStr) || []
|
const dayEvents = events?.filter((e) =>
|
||||||
|
e.date === dateStr && eventFilters[e.type as keyof typeof eventFilters]
|
||||||
|
) || []
|
||||||
d.push({ day, events: dayEvents })
|
d.push({ day, events: dayEvents })
|
||||||
}
|
}
|
||||||
|
|
||||||
return d
|
return d
|
||||||
}, [year, month, daysInMonth, firstDayOfMonth, events])
|
}, [year, month, daysInMonth, firstDayOfMonth, events, eventFilters])
|
||||||
|
|
||||||
const upcomingEvents = useMemo(() => {
|
const upcomingEvents = useMemo(() => {
|
||||||
const today = toDateString(new Date())
|
const today = toDateString(new Date())
|
||||||
return (events || [])
|
return (events || [])
|
||||||
.filter((event) => event.date >= today)
|
.filter((event) => event.date >= today && eventFilters[event.type as keyof typeof eventFilters])
|
||||||
.sort((a, b) => a.date.localeCompare(b.date))
|
.sort((a, b) => a.date.localeCompare(b.date))
|
||||||
.slice(0, 8)
|
.slice(0, 8)
|
||||||
}, [events])
|
}, [events, eventFilters])
|
||||||
|
|
||||||
const prevMonth = () => {
|
const prevMonth = () => {
|
||||||
setCurrentDate(new Date(year, month - 1, 1))
|
setCurrentDate(new Date(year, month - 1, 1))
|
||||||
@@ -123,6 +139,8 @@ export function CalendarView() {
|
|||||||
return (
|
return (
|
||||||
<Card className="w-full">
|
<Card className="w-full">
|
||||||
<CardHeader className="pb-4">
|
<CardHeader className="pb-4">
|
||||||
|
<div className="flex flex-col gap-4">
|
||||||
|
{/* Title Row */}
|
||||||
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
|
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
|
||||||
<CardTitle className="flex items-center gap-2 text-lg sm:text-xl">
|
<CardTitle className="flex items-center gap-2 text-lg sm:text-xl">
|
||||||
<div className="p-2 bg-primary/10 rounded-lg">
|
<div className="p-2 bg-primary/10 rounded-lg">
|
||||||
@@ -145,6 +163,94 @@ export function CalendarView() {
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Filter Controls Row */}
|
||||||
|
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-2">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span className="text-sm font-medium text-muted-foreground">Show:</span>
|
||||||
|
<div className="flex flex-wrap gap-1">
|
||||||
|
<Button
|
||||||
|
variant={eventFilters.domain_expiry ? "default" : "outline"}
|
||||||
|
size="sm"
|
||||||
|
onClick={() => setEventFilters(prev => ({ ...prev, domain_expiry: !prev.domain_expiry }))}
|
||||||
|
className="h-7 text-xs gap-1"
|
||||||
|
>
|
||||||
|
<Globe className="h-3 w-3" />
|
||||||
|
Domain
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant={eventFilters.ssl_expiry ? "default" : "outline"}
|
||||||
|
size="sm"
|
||||||
|
onClick={() => setEventFilters(prev => ({ ...prev, ssl_expiry: !prev.ssl_expiry }))}
|
||||||
|
className="h-7 text-xs gap-1"
|
||||||
|
>
|
||||||
|
<Shield className="h-3 w-3" />
|
||||||
|
SSL
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant={eventFilters.incident ? "default" : "outline"}
|
||||||
|
size="sm"
|
||||||
|
onClick={() => setEventFilters(prev => ({ ...prev, incident: !prev.incident }))}
|
||||||
|
className="h-7 text-xs gap-1"
|
||||||
|
>
|
||||||
|
<AlertCircle className="h-3 w-3" />
|
||||||
|
Incidents
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<Button variant="ghost" size="sm" className="h-7 text-xs">
|
||||||
|
<Filter className="h-3 w-3 mr-1" />
|
||||||
|
Quick Filters
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent align="end" className="w-48">
|
||||||
|
<DropdownMenuLabel>Event Types</DropdownMenuLabel>
|
||||||
|
<DropdownMenuSeparator />
|
||||||
|
<DropdownMenuCheckboxItem
|
||||||
|
checked={eventFilters.domain_expiry}
|
||||||
|
onCheckedChange={(checked) => setEventFilters(prev => ({ ...prev, domain_expiry: checked }))}
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Globe className="h-3 w-3" />
|
||||||
|
Domain Expiry
|
||||||
|
</div>
|
||||||
|
</DropdownMenuCheckboxItem>
|
||||||
|
<DropdownMenuCheckboxItem
|
||||||
|
checked={eventFilters.ssl_expiry}
|
||||||
|
onCheckedChange={(checked) => setEventFilters(prev => ({ ...prev, ssl_expiry: checked }))}
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Shield className="h-3 w-3" />
|
||||||
|
SSL Expiry
|
||||||
|
</div>
|
||||||
|
</DropdownMenuCheckboxItem>
|
||||||
|
<DropdownMenuCheckboxItem
|
||||||
|
checked={eventFilters.incident}
|
||||||
|
onCheckedChange={(checked) => setEventFilters(prev => ({ ...prev, incident: checked }))}
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<AlertCircle className="h-3 w-3" />
|
||||||
|
Incidents
|
||||||
|
</div>
|
||||||
|
</DropdownMenuCheckboxItem>
|
||||||
|
<DropdownMenuSeparator />
|
||||||
|
<DropdownMenuItem onClick={() => setEventFilters({ domain_expiry: true, ssl_expiry: true, incident: true })}>
|
||||||
|
Show All
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem onClick={() => setEventFilters({ domain_expiry: true, ssl_expiry: false, incident: false })}>
|
||||||
|
Domain Only
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem onClick={() => setEventFilters({ domain_expiry: false, ssl_expiry: true, incident: false })}>
|
||||||
|
SSL Only
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="space-y-4">
|
<CardContent className="space-y-4">
|
||||||
{/* Day headers */}
|
{/* Day headers */}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ import {
|
|||||||
type UpdateDomainRequest,
|
type UpdateDomainRequest,
|
||||||
type DomainLookupResult,
|
type DomainLookupResult,
|
||||||
} from "@/lib/domains"
|
} from "@/lib/domains"
|
||||||
import { Loader2, Search } from "lucide-react"
|
import { Loader2, Search, AlertTriangle, Calendar } from "lucide-react"
|
||||||
|
|
||||||
const formSchema = z.object({
|
const formSchema = z.object({
|
||||||
domain_name: z.string().min(1, "Domain name is required"),
|
domain_name: z.string().min(1, "Domain name is required"),
|
||||||
@@ -79,6 +79,13 @@ export function DomainDialog({ open, onOpenChange, domain, isEdit = false }: Dom
|
|||||||
const [activeTab, setActiveTab] = useState("basic")
|
const [activeTab, setActiveTab] = useState("basic")
|
||||||
const [lookupData, setLookupData] = useState<DomainLookupResult | null>(null)
|
const [lookupData, setLookupData] = useState<DomainLookupResult | null>(null)
|
||||||
const [isLookingUp, setIsLookingUp] = useState(false)
|
const [isLookingUp, setIsLookingUp] = useState(false)
|
||||||
|
// Manual expiry inputs when WHOIS fails
|
||||||
|
const [manualRegDate, setManualRegDate] = useState(() => {
|
||||||
|
const today = new Date()
|
||||||
|
return today.toISOString().split("T")[0]
|
||||||
|
})
|
||||||
|
const [manualRegPeriod, setManualRegPeriod] = useState<number>(1)
|
||||||
|
const [manualPurchasePrice, setManualPurchasePrice] = useState<number>(0)
|
||||||
|
|
||||||
const form = useForm<FormData>({
|
const form = useForm<FormData>({
|
||||||
resolver: zodResolver(formSchema),
|
resolver: zodResolver(formSchema),
|
||||||
@@ -163,6 +170,10 @@ export function DomainDialog({ open, onOpenChange, domain, isEdit = false }: Dom
|
|||||||
quiet_hours_end: "08:00",
|
quiet_hours_end: "08:00",
|
||||||
})
|
})
|
||||||
setLookupData(null)
|
setLookupData(null)
|
||||||
|
const today = new Date().toISOString().split("T")[0]
|
||||||
|
setManualRegDate(today)
|
||||||
|
setManualRegPeriod(1)
|
||||||
|
setManualPurchasePrice(0)
|
||||||
}
|
}
|
||||||
}, [open, isEdit, domain, form])
|
}, [open, isEdit, domain, form])
|
||||||
|
|
||||||
@@ -207,6 +218,11 @@ export function DomainDialog({ open, onOpenChange, domain, isEdit = false }: Dom
|
|||||||
try {
|
try {
|
||||||
const data = await lookupDomain(domainName)
|
const data = await lookupDomain(domainName)
|
||||||
setLookupData(data)
|
setLookupData(data)
|
||||||
|
// Reset manual inputs on new lookup
|
||||||
|
const today = new Date().toISOString().split("T")[0]
|
||||||
|
setManualRegDate(today)
|
||||||
|
setManualRegPeriod(1)
|
||||||
|
setManualPurchasePrice(0)
|
||||||
toast({ title: "Domain info retrieved successfully" })
|
toast({ title: "Domain info retrieved successfully" })
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast({
|
toast({
|
||||||
@@ -219,6 +235,17 @@ export function DomainDialog({ open, onOpenChange, domain, isEdit = false }: Dom
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Calculate expiry date from registration date + period in years
|
||||||
|
const calculateExpiryDate = (regDateStr: string, years: number): string | null => {
|
||||||
|
const regDate = new Date(regDateStr)
|
||||||
|
if (isNaN(regDate.getTime())) return null
|
||||||
|
const expiry = new Date(regDate)
|
||||||
|
expiry.setFullYear(expiry.getFullYear() + years)
|
||||||
|
// Subtract 1 day (expiry is typically the day before the anniversary)
|
||||||
|
expiry.setDate(expiry.getDate() - 1)
|
||||||
|
return expiry.toISOString().split("T")[0]
|
||||||
|
}
|
||||||
|
|
||||||
const onSubmit = (data: FormData) => {
|
const onSubmit = (data: FormData) => {
|
||||||
const payload: CreateDomainRequest = {
|
const payload: CreateDomainRequest = {
|
||||||
domain_name: cleanDomain(data.domain_name),
|
domain_name: cleanDomain(data.domain_name),
|
||||||
@@ -247,6 +274,19 @@ export function DomainDialog({ open, onOpenChange, domain, isEdit = false }: Dom
|
|||||||
quiet_hours_end: data.quiet_hours_enabled ? data.quiet_hours_end : undefined,
|
quiet_hours_end: data.quiet_hours_enabled ? data.quiet_hours_end : undefined,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If lookup returned no expiry, attach manual dates
|
||||||
|
if (!isEdit && lookupData && !lookupData.expiry_date) {
|
||||||
|
const calculatedExpiry = calculateExpiryDate(manualRegDate, manualRegPeriod)
|
||||||
|
if (calculatedExpiry) {
|
||||||
|
payload.expiry_date = calculatedExpiry
|
||||||
|
payload.creation_date = manualRegDate
|
||||||
|
}
|
||||||
|
// Use the manual purchase price if set (overrides form value when WHOIS fails)
|
||||||
|
if (manualPurchasePrice > 0) {
|
||||||
|
payload.purchase_price = manualPurchasePrice
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (isEdit && domain) {
|
if (isEdit && domain) {
|
||||||
updateMutation.mutate({
|
updateMutation.mutate({
|
||||||
id: domain.id,
|
id: domain.id,
|
||||||
@@ -384,6 +424,8 @@ export function DomainDialog({ open, onOpenChange, domain, isEdit = false }: Dom
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
{lookupData && !isEdit && (
|
{lookupData && !isEdit && (
|
||||||
|
<div className="space-y-3">
|
||||||
|
{/* Lookup Results */}
|
||||||
<div className="rounded-lg border p-4 space-y-2">
|
<div className="rounded-lg border p-4 space-y-2">
|
||||||
<h4 className="font-medium">Lookup Results</h4>
|
<h4 className="font-medium">Lookup Results</h4>
|
||||||
{lookupData.registrar_name && (
|
{lookupData.registrar_name && (
|
||||||
@@ -398,6 +440,88 @@ export function DomainDialog({ open, onOpenChange, domain, isEdit = false }: Dom
|
|||||||
{lookupData.host_country && (
|
{lookupData.host_country && (
|
||||||
<p className="text-sm">Location: {lookupData.host_country}</p>
|
<p className="text-sm">Location: {lookupData.host_country}</p>
|
||||||
)}
|
)}
|
||||||
|
{lookupData.dns_provider && (
|
||||||
|
<p className="text-sm">DNS: {lookupData.dns_provider}</p>
|
||||||
|
)}
|
||||||
|
{lookupData.hosting_provider && (
|
||||||
|
<p className="text-sm">Hosting: {lookupData.hosting_provider}</p>
|
||||||
|
)}
|
||||||
|
{lookupData.email_provider && (
|
||||||
|
<p className="text-sm">Email: {lookupData.email_provider}</p>
|
||||||
|
)}
|
||||||
|
{lookupData.ca_provider && (
|
||||||
|
<p className="text-sm">CA: {lookupData.ca_provider}</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Manual expiry fallback when WHOIS doesn't return expiry */}
|
||||||
|
{!lookupData.expiry_date && (
|
||||||
|
<div className="rounded-lg border border-yellow-500/30 bg-yellow-500/5 p-4 space-y-3">
|
||||||
|
<div className="flex items-center gap-2 text-yellow-700">
|
||||||
|
<AlertTriangle className="h-4 w-4" />
|
||||||
|
<h4 className="font-medium text-sm">Expiry date not found in WHOIS</h4>
|
||||||
|
</div>
|
||||||
|
<p className="text-xs text-muted-foreground">
|
||||||
|
Enter your registration details below and we'll calculate the expiry date.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-2 gap-3">
|
||||||
|
<div className="space-y-1">
|
||||||
|
<label className="text-xs font-medium">Registration Date</label>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Calendar className="h-3.5 w-3.5 text-muted-foreground" />
|
||||||
|
<Input
|
||||||
|
type="date"
|
||||||
|
value={manualRegDate}
|
||||||
|
onChange={(e) => setManualRegDate(e.target.value)}
|
||||||
|
className="h-8 text-sm"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-1">
|
||||||
|
<label className="text-xs font-medium">Registration Period</label>
|
||||||
|
<select
|
||||||
|
value={manualRegPeriod}
|
||||||
|
onChange={(e) => setManualRegPeriod(Number(e.target.value))}
|
||||||
|
className="w-full h-8 px-2 rounded-md border border-input bg-background text-sm"
|
||||||
|
>
|
||||||
|
<option value={1}>1 year</option>
|
||||||
|
<option value={2}>2 years</option>
|
||||||
|
<option value={3}>3 years</option>
|
||||||
|
<option value={5}>5 years</option>
|
||||||
|
<option value={10}>10 years</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-1">
|
||||||
|
<label className="text-xs font-medium">Purchase Price (total for selected period)</label>
|
||||||
|
<Input
|
||||||
|
type="number"
|
||||||
|
min={0}
|
||||||
|
step="0.01"
|
||||||
|
value={manualPurchasePrice || ""}
|
||||||
|
placeholder="e.g. 29.99"
|
||||||
|
onChange={(e) => setManualPurchasePrice(Number(e.target.value))}
|
||||||
|
className="h-8 text-sm"
|
||||||
|
/>
|
||||||
|
{manualPurchasePrice > 0 && manualRegPeriod > 1 && (
|
||||||
|
<p className="text-[10px] text-muted-foreground">
|
||||||
|
~{(manualPurchasePrice / manualRegPeriod).toFixed(2)} per year
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{manualRegDate && manualRegPeriod > 0 && (
|
||||||
|
<div className="rounded-md bg-muted p-2 text-xs">
|
||||||
|
<p className="font-medium">Calculated Expiry:</p>
|
||||||
|
<p className="text-muted-foreground">
|
||||||
|
{calculateExpiryDate(manualRegDate, manualRegPeriod)}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|||||||
@@ -39,17 +39,15 @@ import {
|
|||||||
DropdownMenuRadioItem,
|
DropdownMenuRadioItem,
|
||||||
DropdownMenuSeparator,
|
DropdownMenuSeparator,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
|
DropdownMenuCheckboxItem,
|
||||||
} from "@/components/ui/dropdown-menu"
|
} from "@/components/ui/dropdown-menu"
|
||||||
import { Badge } from "@/components/ui/badge"
|
|
||||||
import { Input } from "@/components/ui/input"
|
import { Input } from "@/components/ui/input"
|
||||||
import {
|
import {
|
||||||
getDomains,
|
getDomains,
|
||||||
deleteDomain,
|
deleteDomain,
|
||||||
refreshDomain,
|
refreshDomain,
|
||||||
getStatusBadgeColor,
|
getDomainSubdomains,
|
||||||
getStatusLabel,
|
|
||||||
formatDate,
|
formatDate,
|
||||||
formatDays,
|
|
||||||
type Domain,
|
type Domain,
|
||||||
} from "@/lib/domains"
|
} from "@/lib/domains"
|
||||||
import {
|
import {
|
||||||
@@ -67,11 +65,78 @@ import {
|
|||||||
} from "lucide-react"
|
} from "lucide-react"
|
||||||
import { DomainDialog } from "./domain-dialog"
|
import { DomainDialog } from "./domain-dialog"
|
||||||
import { Link } from "@/components/router"
|
import { Link } from "@/components/router"
|
||||||
import { useBrowserStorage } from "@/lib/utils"
|
import { cn, useBrowserStorage } from "@/lib/utils"
|
||||||
|
|
||||||
type ViewMode = "table" | "grid"
|
type ViewMode = "table" | "grid"
|
||||||
type StatusFilter = "all" | "active" | "expiring" | "expired" | "unknown" | "paused"
|
type StatusFilter = "all" | "active" | "expiring" | "expired" | "unknown" | "paused"
|
||||||
|
|
||||||
|
type DisplayOptions = {
|
||||||
|
showSSL: boolean
|
||||||
|
showRegistrar: boolean
|
||||||
|
showExpiryDate: boolean
|
||||||
|
showTags: boolean
|
||||||
|
showProviders: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
// Days left badge component - big and visible
|
||||||
|
function DaysLeftBadge({ days, label = "days" }: { days: number | undefined; label?: string }) {
|
||||||
|
if (days === undefined || days === null) return <span className="text-muted-foreground">-</span>
|
||||||
|
|
||||||
|
const isCritical = days >= 0 && days <= 7
|
||||||
|
const isWarning = days >= 0 && days <= 30
|
||||||
|
const isExpired = days < 0
|
||||||
|
|
||||||
|
const colorClass = isExpired
|
||||||
|
? "bg-red-500/15 text-red-600 border-red-500/30"
|
||||||
|
: isCritical
|
||||||
|
? "bg-red-500/15 text-red-600 border-red-500/30"
|
||||||
|
: isWarning
|
||||||
|
? "bg-yellow-500/15 text-yellow-600 border-yellow-500/30"
|
||||||
|
: "bg-green-500/15 text-green-600 border-green-500/30"
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`inline-flex flex-col items-center justify-center px-3 py-1.5 rounded-lg border-2 ${colorClass} min-w-[70px]`}>
|
||||||
|
<span className="text-lg font-bold leading-none">{isExpired ? Math.abs(days) : days}</span>
|
||||||
|
<span className="text-[10px] font-medium uppercase tracking-wide opacity-80">{isExpired ? "EXPIRED" : days === 1 ? "DAY" : label.toUpperCase()}</span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subdomain indicator component
|
||||||
|
function SubdomainIndicator({ domainId }: { domainId: string }) {
|
||||||
|
const { data: subdomains, isLoading } = useQuery({
|
||||||
|
queryKey: ["domain-subdomains", domainId],
|
||||||
|
queryFn: () => getDomainSubdomains(domainId),
|
||||||
|
enabled: !!domainId,
|
||||||
|
staleTime: 5 * 60 * 1000, // 5 minutes
|
||||||
|
})
|
||||||
|
|
||||||
|
if (isLoading || !subdomains || subdomains.length === 0) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const activeCount = subdomains.filter(s => s.status === "active").length
|
||||||
|
const totalCount = subdomains.length
|
||||||
|
const hasIssues = subdomains.some(s => s.status === "error")
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
<div className={cn(
|
||||||
|
"inline-flex items-center gap-1 px-2 py-1 rounded-full text-xs font-medium border",
|
||||||
|
hasIssues
|
||||||
|
? "bg-orange-500/15 text-orange-600 border-orange-500/30"
|
||||||
|
: "bg-blue-500/15 text-blue-600 border-blue-500/30"
|
||||||
|
)}>
|
||||||
|
<Globe className="h-3 w-3" />
|
||||||
|
<span>{activeCount}/{totalCount}</span>
|
||||||
|
</div>
|
||||||
|
{hasIssues && (
|
||||||
|
<AlertTriangle className="h-3 w-3 text-orange-500" />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export default function DomainsTable() {
|
export default function DomainsTable() {
|
||||||
const { t } = useLingui()
|
const { t } = useLingui()
|
||||||
const { toast } = useToast()
|
const { toast } = useToast()
|
||||||
@@ -88,6 +153,11 @@ export default function DomainsTable() {
|
|||||||
window.innerWidth < 1024 ? "grid" : "table"
|
window.innerWidth < 1024 ? "grid" : "table"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const [displayOptions, setDisplayOptions] = useBrowserStorage<DisplayOptions>(
|
||||||
|
"domainsDisplayOptions",
|
||||||
|
{ showSSL: true, showRegistrar: true, showExpiryDate: true, showTags: true, showProviders: false }
|
||||||
|
)
|
||||||
|
|
||||||
const { data: domains = [], isLoading } = useQuery({
|
const { data: domains = [], isLoading } = useQuery({
|
||||||
queryKey: ["domains"],
|
queryKey: ["domains"],
|
||||||
queryFn: getDomains,
|
queryFn: getDomains,
|
||||||
@@ -167,19 +237,35 @@ export default function DomainsTable() {
|
|||||||
refreshMutation.mutate(id)
|
refreshMutation.mutate(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
const getStatusIcon = (status: string) => {
|
// Status indicator component matching monitors table style
|
||||||
switch (status) {
|
function StatusIndicator({ status }: { status: string }) {
|
||||||
case "active":
|
const colors = {
|
||||||
return <CheckCircle2 className="h-4 w-4 text-green-500" />
|
active: "bg-green-500",
|
||||||
case "expiring":
|
expiring: "bg-yellow-500",
|
||||||
return <Clock className="h-4 w-4 text-yellow-500" />
|
expired: "bg-red-500",
|
||||||
case "expired":
|
unknown: "bg-gray-500",
|
||||||
return <AlertTriangle className="h-4 w-4 text-red-500" />
|
paused: "bg-blue-500",
|
||||||
default:
|
|
||||||
return <Globe className="h-4 w-4 text-gray-500" />
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const icons = {
|
||||||
|
active: CheckCircle2,
|
||||||
|
expiring: Clock,
|
||||||
|
expired: AlertTriangle,
|
||||||
|
unknown: AlertTriangle,
|
||||||
|
paused: Clock,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Icon = icons[status as keyof typeof icons] || AlertTriangle
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<div className={cn("h-2.5 w-2.5 rounded-full", colors[status as keyof typeof colors] || "bg-gray-500")} />
|
||||||
|
<Icon className="h-4 w-4 text-muted-foreground" />
|
||||||
|
<span className="capitalize text-sm">{status === "active" ? "Active" : status === "expiring" ? "Expiring Soon" : status === "expired" ? "Expired" : status}</span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return (
|
return (
|
||||||
<Card className="w-full px-3 py-5 sm:py-6 sm:px-6">
|
<Card className="w-full px-3 py-5 sm:py-6 sm:px-6">
|
||||||
@@ -346,6 +432,43 @@ export default function DomainsTable() {
|
|||||||
</DropdownMenuRadioItem>
|
</DropdownMenuRadioItem>
|
||||||
)}
|
)}
|
||||||
</DropdownMenuRadioGroup>
|
</DropdownMenuRadioGroup>
|
||||||
|
<DropdownMenuSeparator />
|
||||||
|
|
||||||
|
{/* Display Options */}
|
||||||
|
<DropdownMenuLabel className="flex items-center gap-2">
|
||||||
|
<FilterIcon className="size-4" />
|
||||||
|
<Trans>Display Columns</Trans>
|
||||||
|
</DropdownMenuLabel>
|
||||||
|
<DropdownMenuCheckboxItem
|
||||||
|
checked={displayOptions.showSSL}
|
||||||
|
onCheckedChange={(checked: boolean) => setDisplayOptions({ ...displayOptions, showSSL: checked })}
|
||||||
|
>
|
||||||
|
SSL Info
|
||||||
|
</DropdownMenuCheckboxItem>
|
||||||
|
<DropdownMenuCheckboxItem
|
||||||
|
checked={displayOptions.showRegistrar}
|
||||||
|
onCheckedChange={(checked: boolean) => setDisplayOptions({ ...displayOptions, showRegistrar: checked })}
|
||||||
|
>
|
||||||
|
Registrar
|
||||||
|
</DropdownMenuCheckboxItem>
|
||||||
|
<DropdownMenuCheckboxItem
|
||||||
|
checked={displayOptions.showExpiryDate}
|
||||||
|
onCheckedChange={(checked: boolean) => setDisplayOptions({ ...displayOptions, showExpiryDate: checked })}
|
||||||
|
>
|
||||||
|
Expiry Date
|
||||||
|
</DropdownMenuCheckboxItem>
|
||||||
|
<DropdownMenuCheckboxItem
|
||||||
|
checked={displayOptions.showTags}
|
||||||
|
onCheckedChange={(checked: boolean) => setDisplayOptions({ ...displayOptions, showTags: checked })}
|
||||||
|
>
|
||||||
|
Tags
|
||||||
|
</DropdownMenuCheckboxItem>
|
||||||
|
<DropdownMenuCheckboxItem
|
||||||
|
checked={displayOptions.showProviders}
|
||||||
|
onCheckedChange={(checked: boolean) => setDisplayOptions({ ...displayOptions, showProviders: checked })}
|
||||||
|
>
|
||||||
|
Providers
|
||||||
|
</DropdownMenuCheckboxItem>
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
</div>
|
</div>
|
||||||
@@ -374,11 +497,12 @@ export default function DomainsTable() {
|
|||||||
<TableRow>
|
<TableRow>
|
||||||
<TableHead>Domain</TableHead>
|
<TableHead>Domain</TableHead>
|
||||||
<TableHead>Status</TableHead>
|
<TableHead>Status</TableHead>
|
||||||
<TableHead>Expiry</TableHead>
|
{displayOptions.showExpiryDate && <TableHead>Expiry</TableHead>}
|
||||||
<TableHead>Days Left</TableHead>
|
<TableHead>Days Left</TableHead>
|
||||||
<TableHead>Registrar</TableHead>
|
{displayOptions.showRegistrar && <TableHead>Registrar</TableHead>}
|
||||||
<TableHead>SSL Expiry</TableHead>
|
{displayOptions.showSSL && <TableHead>SSL Expiry</TableHead>}
|
||||||
<TableHead>Tags</TableHead>
|
{displayOptions.showTags && <TableHead>Tags</TableHead>}
|
||||||
|
{displayOptions.showProviders && <TableHead>Providers</TableHead>}
|
||||||
<TableHead className="w-[100px]">Actions</TableHead>
|
<TableHead className="w-[100px]">Actions</TableHead>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHeader>
|
</TableHeader>
|
||||||
@@ -391,51 +515,38 @@ export default function DomainsTable() {
|
|||||||
<img
|
<img
|
||||||
src={domain.favicon_url}
|
src={domain.favicon_url}
|
||||||
alt=""
|
alt=""
|
||||||
className="h-4 w-4"
|
className="h-4 w-4 rounded-sm"
|
||||||
onError={(e) => (e.currentTarget.style.display = "none")}
|
onError={(e) => (e.currentTarget.style.display = "none")}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<span className="hover:underline">{domain.domain_name}</span>
|
<span className="hover:underline">{domain.domain_name}</span>
|
||||||
|
<SubdomainIndicator domainId={domain.id} />
|
||||||
</Link>
|
</Link>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<div className="flex items-center gap-2">
|
<StatusIndicator status={domain.status} />
|
||||||
{getStatusIcon(domain.status)}
|
|
||||||
<Badge className={getStatusBadgeColor(domain.status)}>
|
|
||||||
{getStatusLabel(domain.status)}
|
|
||||||
</Badge>
|
|
||||||
</div>
|
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
{displayOptions.showExpiryDate && (
|
||||||
<TableCell>
|
<TableCell>
|
||||||
{domain.expiry_date ? formatDate(domain.expiry_date) : "Unknown"}
|
{domain.expiry_date ? formatDate(domain.expiry_date) : "Unknown"}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
)}
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<span className={
|
<DaysLeftBadge days={domain.days_until_expiry} />
|
||||||
domain.days_until_expiry !== undefined && domain.days_until_expiry >= 0 && domain.days_until_expiry <= 30
|
|
||||||
? domain.days_until_expiry <= 7
|
|
||||||
? "text-red-600 font-semibold"
|
|
||||||
: "text-yellow-600"
|
|
||||||
: ""
|
|
||||||
}>
|
|
||||||
{formatDays(domain.days_until_expiry)}
|
|
||||||
</span>
|
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
{displayOptions.showRegistrar && (
|
||||||
<TableCell>{domain.registrar_name || "Unknown"}</TableCell>
|
<TableCell>{domain.registrar_name || "Unknown"}</TableCell>
|
||||||
|
)}
|
||||||
|
{displayOptions.showSSL && (
|
||||||
<TableCell>
|
<TableCell>
|
||||||
{domain.ssl_valid_to ? (
|
{domain.ssl_valid_to ? (
|
||||||
<span
|
<DaysLeftBadge days={domain.ssl_days_until} label="ssl" />
|
||||||
className={
|
|
||||||
domain.ssl_days_until !== undefined && domain.ssl_days_until >= 0 && domain.ssl_days_until <= 14
|
|
||||||
? "text-red-600"
|
|
||||||
: ""
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{formatDays(domain.ssl_days_until)}
|
|
||||||
</span>
|
|
||||||
) : (
|
) : (
|
||||||
"N/A"
|
<span className="text-muted-foreground">-</span>
|
||||||
)}
|
)}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
)}
|
||||||
|
{displayOptions.showTags && (
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<div className="flex flex-wrap gap-1">
|
<div className="flex flex-wrap gap-1">
|
||||||
{domain.tags?.map((tag: string) => (
|
{domain.tags?.map((tag: string) => (
|
||||||
@@ -449,6 +560,17 @@ export default function DomainsTable() {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
)}
|
||||||
|
{displayOptions.showProviders && (
|
||||||
|
<TableCell>
|
||||||
|
<div className="flex flex-col gap-0.5 text-xs">
|
||||||
|
{domain.dns_provider && <span className="text-muted-foreground">DNS: <span className="text-foreground">{domain.dns_provider}</span></span>}
|
||||||
|
{domain.hosting_provider && <span className="text-muted-foreground">Host: <span className="text-foreground">{domain.hosting_provider}</span></span>}
|
||||||
|
{domain.email_provider && <span className="text-muted-foreground">Email: <span className="text-foreground">{domain.email_provider}</span></span>}
|
||||||
|
{domain.ca_provider && <span className="text-muted-foreground">CA: <span className="text-foreground">{domain.ca_provider}</span></span>}
|
||||||
|
</div>
|
||||||
|
</TableCell>
|
||||||
|
)}
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
@@ -507,6 +629,7 @@ export default function DomainsTable() {
|
|||||||
)}
|
)}
|
||||||
<div className="min-w-0">
|
<div className="min-w-0">
|
||||||
<div className="font-medium truncate hover:underline">{domain.domain_name}</div>
|
<div className="font-medium truncate hover:underline">{domain.domain_name}</div>
|
||||||
|
<SubdomainIndicator domainId={domain.id} />
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
@@ -528,14 +651,9 @@ export default function DomainsTable() {
|
|||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center gap-2">
|
<StatusIndicator status={domain.status} />
|
||||||
{getStatusIcon(domain.status)}
|
|
||||||
<Badge className={getStatusBadgeColor(domain.status)}>
|
|
||||||
{getStatusLabel(domain.status)}
|
|
||||||
</Badge>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{domain.tags && domain.tags.length > 0 && (
|
{displayOptions.showTags && domain.tags && domain.tags.length > 0 && (
|
||||||
<div className="flex flex-wrap gap-1">
|
<div className="flex flex-wrap gap-1">
|
||||||
{domain.tags.map((tag: string) => (
|
{domain.tags.map((tag: string) => (
|
||||||
<span
|
<span
|
||||||
@@ -549,30 +667,28 @@ export default function DomainsTable() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="grid grid-cols-2 gap-2 text-sm">
|
<div className="grid gap-2 text-sm">
|
||||||
<div>
|
<div className="flex items-center justify-between">
|
||||||
<div className="text-xs text-muted-foreground">Days Left</div>
|
{displayOptions.showExpiryDate && (
|
||||||
<span className={
|
<span className="text-xs text-muted-foreground">{domain.expiry_date ? formatDate(domain.expiry_date) : "Unknown"}</span>
|
||||||
domain.days_until_expiry !== undefined && domain.days_until_expiry >= 0 && domain.days_until_expiry <= 30
|
)}
|
||||||
? domain.days_until_expiry <= 7
|
{displayOptions.showRegistrar && (
|
||||||
? "text-red-600 font-semibold"
|
<span className="text-xs text-muted-foreground truncate max-w-[120px]">{domain.registrar_name || "Unknown"}</span>
|
||||||
: "text-yellow-600"
|
)}
|
||||||
: ""
|
|
||||||
}>
|
|
||||||
{formatDays(domain.days_until_expiry)}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
{displayOptions.showProviders && (
|
||||||
<div className="text-xs text-muted-foreground">SSL</div>
|
<div className="flex flex-wrap gap-1 text-[10px]">
|
||||||
<span
|
{domain.dns_provider && <span className="text-muted-foreground">DNS: <span className="text-foreground">{domain.dns_provider}</span></span>}
|
||||||
className={
|
{domain.hosting_provider && <span className="text-muted-foreground">Host: <span className="text-foreground">{domain.hosting_provider}</span></span>}
|
||||||
domain.ssl_days_until !== undefined && domain.ssl_days_until >= 0 && domain.ssl_days_until <= 14
|
{domain.email_provider && <span className="text-muted-foreground">Email: <span className="text-foreground">{domain.email_provider}</span></span>}
|
||||||
? "text-red-600"
|
{domain.ca_provider && <span className="text-muted-foreground">CA: <span className="text-foreground">{domain.ca_provider}</span></span>}
|
||||||
: ""
|
</div>
|
||||||
}
|
)}
|
||||||
>
|
<div className="flex gap-2">
|
||||||
{formatDays(domain.ssl_days_until)}
|
<DaysLeftBadge days={domain.days_until_expiry} />
|
||||||
</span>
|
{displayOptions.showSSL && domain.ssl_valid_to && (
|
||||||
|
<DaysLeftBadge days={domain.ssl_days_until} label="ssl" />
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,297 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"
|
||||||
|
import { useToast } from "@/components/ui/use-toast"
|
||||||
|
import { Button } from "@/components/ui/button"
|
||||||
|
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card"
|
||||||
|
import { Badge } from "@/components/ui/badge"
|
||||||
|
import {
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableHead,
|
||||||
|
TableHeader,
|
||||||
|
TableRow,
|
||||||
|
} from "@/components/ui/table"
|
||||||
|
import {
|
||||||
|
Tooltip,
|
||||||
|
TooltipContent,
|
||||||
|
TooltipProvider,
|
||||||
|
TooltipTrigger,
|
||||||
|
} from "@/components/ui/tooltip"
|
||||||
|
import { Skeleton } from "@/components/ui/skeleton"
|
||||||
|
import {
|
||||||
|
Globe,
|
||||||
|
RefreshCw,
|
||||||
|
Trash2,
|
||||||
|
Search,
|
||||||
|
Server,
|
||||||
|
ExternalLink,
|
||||||
|
CheckCircle2,
|
||||||
|
XCircle,
|
||||||
|
Shield,
|
||||||
|
} from "lucide-react"
|
||||||
|
import {
|
||||||
|
getDomainSubdomains,
|
||||||
|
refreshSubdomainDiscovery,
|
||||||
|
deleteSubdomain,
|
||||||
|
type Subdomain,
|
||||||
|
} from "@/lib/domains"
|
||||||
|
import { useState } from "react"
|
||||||
|
|
||||||
|
interface SubdomainListProps {
|
||||||
|
domainId: string
|
||||||
|
}
|
||||||
|
|
||||||
|
function SubdomainStatusBadge({ status }: { status: string }) {
|
||||||
|
const configs = {
|
||||||
|
active: { color: "bg-green-500", icon: CheckCircle2, text: "Active" },
|
||||||
|
inactive: { color: "bg-gray-500", icon: XCircle, text: "Inactive" },
|
||||||
|
error: { color: "bg-red-500", icon: XCircle, text: "Error" },
|
||||||
|
}
|
||||||
|
|
||||||
|
const config = configs[status as keyof typeof configs] || configs.inactive
|
||||||
|
const Icon = config.icon
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex items-center gap-1.5">
|
||||||
|
<div className={`h-2 w-2 rounded-full ${config.color}`} />
|
||||||
|
<Icon className="h-3.5 w-3.5 text-muted-foreground" />
|
||||||
|
<span className="text-xs capitalize">{config.text}</span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function SourceBadge({ source }: { source: string }) {
|
||||||
|
const sourceConfig: Record<string, { label: string; variant: "default" | "secondary" | "outline" }> = {
|
||||||
|
dns: { label: "DNS", variant: "default" },
|
||||||
|
http: { label: "HTTP", variant: "secondary" },
|
||||||
|
pattern: { label: "Pattern", variant: "outline" },
|
||||||
|
certificate: { label: "Cert", variant: "secondary" },
|
||||||
|
}
|
||||||
|
|
||||||
|
const config = sourceConfig[source] || { label: source, variant: "outline" }
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Badge variant={config.variant} className="text-xs">
|
||||||
|
{config.label}
|
||||||
|
</Badge>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SubdomainList({ domainId }: SubdomainListProps) {
|
||||||
|
const { toast } = useToast()
|
||||||
|
const queryClient = useQueryClient()
|
||||||
|
const [isDiscovering, setIsDiscovering] = useState(false)
|
||||||
|
|
||||||
|
const { data: subdomains, isLoading } = useQuery({
|
||||||
|
queryKey: ["domain-subdomains", domainId],
|
||||||
|
queryFn: () => getDomainSubdomains(domainId),
|
||||||
|
})
|
||||||
|
|
||||||
|
const refreshMutation = useMutation({
|
||||||
|
mutationFn: async () => {
|
||||||
|
setIsDiscovering(true)
|
||||||
|
await refreshSubdomainDiscovery(domainId)
|
||||||
|
// Wait a bit for discovery to start
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 2000))
|
||||||
|
setIsDiscovering(false)
|
||||||
|
},
|
||||||
|
onSuccess: () => {
|
||||||
|
toast({ title: "Subdomain discovery started" })
|
||||||
|
queryClient.invalidateQueries({ queryKey: ["domain-subdomains", domainId] })
|
||||||
|
},
|
||||||
|
onError: (error: Error) => {
|
||||||
|
setIsDiscovering(false)
|
||||||
|
toast({
|
||||||
|
title: "Discovery failed",
|
||||||
|
description: error.message,
|
||||||
|
variant: "destructive",
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const deleteMutation = useMutation({
|
||||||
|
mutationFn: deleteSubdomain,
|
||||||
|
onSuccess: () => {
|
||||||
|
toast({ title: "Subdomain deleted" })
|
||||||
|
queryClient.invalidateQueries({ queryKey: ["domain-subdomains", domainId] })
|
||||||
|
},
|
||||||
|
onError: (error: Error) => {
|
||||||
|
toast({
|
||||||
|
title: "Failed to delete",
|
||||||
|
description: error.message,
|
||||||
|
variant: "destructive",
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return (
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<Skeleton className="h-6 w-32" />
|
||||||
|
<Skeleton className="h-4 w-48 mt-2" />
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Skeleton className="h-10 w-full" />
|
||||||
|
<Skeleton className="h-10 w-full" />
|
||||||
|
<Skeleton className="h-10 w-full" />
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const activeCount = subdomains?.filter((s) => s.status === "active").length || 0
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<CardTitle className="text-lg flex items-center gap-2">
|
||||||
|
<Search className="h-5 w-5" />
|
||||||
|
Discovered Subdomains
|
||||||
|
{subdomains && subdomains.length > 0 && (
|
||||||
|
<Badge variant="secondary" className="ml-2">
|
||||||
|
{activeCount}/{subdomains.length} active
|
||||||
|
</Badge>
|
||||||
|
)}
|
||||||
|
</CardTitle>
|
||||||
|
<CardDescription>
|
||||||
|
Subdomains discovered through DNS, HTTP, and pattern enumeration
|
||||||
|
</CardDescription>
|
||||||
|
</div>
|
||||||
|
<TooltipProvider>
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger asChild>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => refreshMutation.mutate()}
|
||||||
|
disabled={isDiscovering || refreshMutation.isPending}
|
||||||
|
>
|
||||||
|
<RefreshCw className={`mr-2 h-4 w-4 ${isDiscovering ? "animate-spin" : ""}`} />
|
||||||
|
{isDiscovering ? "Discovering..." : "Discover"}
|
||||||
|
</Button>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>
|
||||||
|
<p>Run enhanced subdomain discovery</p>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</TooltipProvider>
|
||||||
|
</div>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
{!subdomains || subdomains.length === 0 ? (
|
||||||
|
<div className="text-center py-8">
|
||||||
|
<Search className="h-12 w-12 text-muted-foreground mx-auto mb-4" />
|
||||||
|
<p className="text-lg font-medium">No subdomains discovered yet</p>
|
||||||
|
<p className="text-muted-foreground mb-4">
|
||||||
|
Run discovery to find subdomains via DNS, HTTP, and certificate transparency logs
|
||||||
|
</p>
|
||||||
|
<Button
|
||||||
|
onClick={() => refreshMutation.mutate()}
|
||||||
|
disabled={isDiscovering || refreshMutation.isPending}
|
||||||
|
>
|
||||||
|
<RefreshCw className={`mr-2 h-4 w-4 ${isDiscovering ? "animate-spin" : ""}`} />
|
||||||
|
{isDiscovering ? "Discovering..." : "Start Discovery"}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<Table>
|
||||||
|
<TableHeader>
|
||||||
|
<TableRow>
|
||||||
|
<TableHead>Subdomain</TableHead>
|
||||||
|
<TableHead>Status</TableHead>
|
||||||
|
<TableHead>Source</TableHead>
|
||||||
|
<TableHead>IP Addresses</TableHead>
|
||||||
|
<TableHead>HTTP</TableHead>
|
||||||
|
<TableHead className="w-[100px]">Actions</TableHead>
|
||||||
|
</TableRow>
|
||||||
|
</TableHeader>
|
||||||
|
<TableBody>
|
||||||
|
{subdomains.map((subdomain) => (
|
||||||
|
<TableRow key={subdomain.id}>
|
||||||
|
<TableCell className="font-medium">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Globe className="h-4 w-4 text-muted-foreground" />
|
||||||
|
<span className="font-mono text-sm">
|
||||||
|
{subdomain.subdomain_name}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<SubdomainStatusBadge status={subdomain.status} />
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<SourceBadge source={subdomain.discovery_source} />
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
{subdomain.ip_addresses ? (
|
||||||
|
<div className="flex items-center gap-1.5">
|
||||||
|
<Server className="h-3.5 w-3.5 text-muted-foreground" />
|
||||||
|
<span className="text-xs text-muted-foreground truncate max-w-[150px]">
|
||||||
|
{subdomain.ip_addresses.split(",").length} IP(s)
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<span className="text-xs text-muted-foreground">-</span>
|
||||||
|
)}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
{subdomain.http_status ? (
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Badge
|
||||||
|
variant={subdomain.http_status < 400 ? "default" : "destructive"}
|
||||||
|
className="text-xs"
|
||||||
|
>
|
||||||
|
{subdomain.http_status}
|
||||||
|
</Badge>
|
||||||
|
{subdomain.server_header && (
|
||||||
|
<TooltipProvider>
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger>
|
||||||
|
<Shield className="h-3.5 w-3.5 text-muted-foreground" />
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>
|
||||||
|
<p className="text-xs">{subdomain.server_header}</p>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</TooltipProvider>
|
||||||
|
)}
|
||||||
|
<a
|
||||||
|
href={`https://${subdomain.full_domain}`}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="text-muted-foreground hover:text-foreground"
|
||||||
|
>
|
||||||
|
<ExternalLink className="h-3.5 w-3.5" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<span className="text-xs text-muted-foreground">-</span>
|
||||||
|
)}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
className="h-8 w-8 text-destructive"
|
||||||
|
onClick={() => deleteMutation.mutate(subdomain.id)}
|
||||||
|
disabled={deleteMutation.isPending}
|
||||||
|
>
|
||||||
|
<Trash2 className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
)}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,255 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import { useMemo } from "react"
|
||||||
|
import { useQuery } from "@tanstack/react-query"
|
||||||
|
import { Card, CardContent, CardHeader } from "@/components/ui/card"
|
||||||
|
import { Badge } from "@/components/ui/badge"
|
||||||
|
import { Collapsible } from "@/components/ui/collapsible"
|
||||||
|
import {
|
||||||
|
Globe,
|
||||||
|
Server,
|
||||||
|
Activity,
|
||||||
|
ArrowUpRight,
|
||||||
|
ArrowDownRight,
|
||||||
|
Minus,
|
||||||
|
ExternalLink,
|
||||||
|
} from "lucide-react"
|
||||||
|
import {
|
||||||
|
listMonitors,
|
||||||
|
groupMonitorsByDomain,
|
||||||
|
getMonitorStatusColor,
|
||||||
|
formatUptime,
|
||||||
|
type Monitor,
|
||||||
|
type GroupedMonitors,
|
||||||
|
} from "@/lib/monitors"
|
||||||
|
import { Link } from "@/components/router"
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
interface GroupedMonitorsTableProps {
|
||||||
|
view?: "grid" | "list"
|
||||||
|
}
|
||||||
|
|
||||||
|
function DomainGroupHeader({
|
||||||
|
domain,
|
||||||
|
group,
|
||||||
|
monitorCount,
|
||||||
|
}: {
|
||||||
|
domain: string
|
||||||
|
group: GroupedMonitors
|
||||||
|
monitorCount: number
|
||||||
|
}) {
|
||||||
|
// Calculate aggregate status for the domain
|
||||||
|
const allMonitors = [...group.monitors, ...Array.from(group.subdomains.values()).flat()]
|
||||||
|
const upCount = allMonitors.filter((m) => m.status === "up").length
|
||||||
|
const downCount = allMonitors.filter((m) => m.status === "down").length
|
||||||
|
const pausedCount = allMonitors.filter((m) => m.status === "paused").length
|
||||||
|
|
||||||
|
const statusColor =
|
||||||
|
downCount > 0 ? "bg-red-500" : upCount > 0 ? "bg-green-500" : pausedCount > 0 ? "bg-yellow-500" : "bg-gray-400"
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex items-center gap-3 py-2">
|
||||||
|
<div className={cn("h-3 w-3 rounded-full", statusColor)} />
|
||||||
|
<div className="flex-1">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Globe className="h-4 w-4 text-muted-foreground" />
|
||||||
|
<span className="font-semibold">{domain}</span>
|
||||||
|
<Badge variant="secondary" className="text-xs">
|
||||||
|
{monitorCount} monitor{monitorCount !== 1 ? "s" : ""}
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2 text-sm text-muted-foreground">
|
||||||
|
{upCount > 0 && (
|
||||||
|
<span className="flex items-center gap-1 text-green-600">
|
||||||
|
<ArrowUpRight className="h-3.5 w-3.5" />
|
||||||
|
{upCount}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{downCount > 0 && (
|
||||||
|
<span className="flex items-center gap-1 text-red-600">
|
||||||
|
<ArrowDownRight className="h-3.5 w-3.5" />
|
||||||
|
{downCount}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{pausedCount > 0 && (
|
||||||
|
<span className="flex items-center gap-1 text-yellow-600">
|
||||||
|
<Minus className="h-3.5 w-3.5" />
|
||||||
|
{pausedCount}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function MonitorCard({ monitor }: { monitor: Monitor }) {
|
||||||
|
const uptime24h = monitor.uptime_stats?.["24h"] ?? 100
|
||||||
|
const statusColor = getMonitorStatusColor(monitor.status)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Link href={`/monitor/${monitor.id}`}>
|
||||||
|
<div className="group relative rounded-lg border p-3 hover:bg-muted/50 transition-colors cursor-pointer">
|
||||||
|
<div className="flex items-start gap-3">
|
||||||
|
<div className={cn("mt-1 h-2.5 w-2.5 rounded-full", statusColor)} />
|
||||||
|
<div className="flex-1 min-w-0">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span className="font-medium text-sm truncate">{monitor.name}</span>
|
||||||
|
{monitor.active === false && (
|
||||||
|
<Badge variant="secondary" className="text-[10px]">
|
||||||
|
Paused
|
||||||
|
</Badge>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2 mt-1 text-xs text-muted-foreground">
|
||||||
|
<Activity className="h-3 w-3" />
|
||||||
|
<span>24h: {formatUptime(uptime24h)}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ExternalLink className="h-3.5 w-3.5 text-muted-foreground opacity-0 group-hover:opacity-100 transition-opacity" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function SubdomainSection({
|
||||||
|
subdomain,
|
||||||
|
monitors,
|
||||||
|
}: {
|
||||||
|
subdomain: string
|
||||||
|
monitors: Monitor[]
|
||||||
|
}) {
|
||||||
|
const downCount = monitors.filter((m) => m.status === "down").length
|
||||||
|
|
||||||
|
const header = (
|
||||||
|
<div className="flex items-center gap-2 py-1.5">
|
||||||
|
<Server className="h-3.5 w-3.5 text-muted-foreground" />
|
||||||
|
<span className="text-sm font-medium">{subdomain}</span>
|
||||||
|
<Badge variant="outline" className="text-[10px] h-5 px-1">
|
||||||
|
{monitors.length}
|
||||||
|
</Badge>
|
||||||
|
{downCount > 0 && (
|
||||||
|
<Badge variant="destructive" className="text-[10px] h-5 px-1">
|
||||||
|
{downCount} down
|
||||||
|
</Badge>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Collapsible
|
||||||
|
title={`${subdomain} (${monitors.length})`}
|
||||||
|
icon={<Server className="h-4 w-4" />}
|
||||||
|
defaultOpen={true}
|
||||||
|
className="ml-4 border-l-2 border-muted rounded-none border-t-0 border-r-0 border-b-0"
|
||||||
|
>
|
||||||
|
<div className="pl-6 py-1 space-y-1">
|
||||||
|
{monitors.map((monitor) => (
|
||||||
|
<MonitorCard key={monitor.id} monitor={monitor} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</Collapsible>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function DomainGroup({ domain, group }: { domain: string; group: GroupedMonitors }) {
|
||||||
|
const monitorCount = group.monitors.length + Array.from(group.subdomains.values()).flat().length
|
||||||
|
|
||||||
|
const content = (
|
||||||
|
<CardContent className="pt-0 pb-4 px-4">
|
||||||
|
{/* Root domain monitors */}
|
||||||
|
{group.monitors.length > 0 && (
|
||||||
|
<div className="mb-3">
|
||||||
|
<div className="flex items-center gap-2 py-1.5 text-sm text-muted-foreground">
|
||||||
|
<Globe className="h-3.5 w-3.5" />
|
||||||
|
<span>Root Domain</span>
|
||||||
|
</div>
|
||||||
|
<div className="pl-6 space-y-1">
|
||||||
|
{group.monitors.map((monitor) => (
|
||||||
|
<MonitorCard key={monitor.id} monitor={monitor} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Subdomain sections */}
|
||||||
|
{Array.from(group.subdomains.entries()).map(([subdomain, monitors]) => (
|
||||||
|
<SubdomainSection key={subdomain} subdomain={subdomain} monitors={monitors} />
|
||||||
|
))}
|
||||||
|
</CardContent>
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Collapsible
|
||||||
|
title={domain}
|
||||||
|
icon={<Globe className="h-4 w-4" />}
|
||||||
|
defaultOpen={true}
|
||||||
|
description={<DomainGroupHeader domain={domain} group={group} monitorCount={monitorCount} />}
|
||||||
|
>
|
||||||
|
{content}
|
||||||
|
</Collapsible>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function GroupedMonitorsTable() {
|
||||||
|
const { data: monitors, isLoading } = useQuery({
|
||||||
|
queryKey: ["monitors"],
|
||||||
|
queryFn: listMonitors,
|
||||||
|
})
|
||||||
|
|
||||||
|
const groupedMonitors = useMemo(() => {
|
||||||
|
if (!monitors) return new Map()
|
||||||
|
return groupMonitorsByDomain(monitors)
|
||||||
|
}, [monitors])
|
||||||
|
|
||||||
|
const ungroupedMonitors = useMemo(() => {
|
||||||
|
if (!monitors) return []
|
||||||
|
return monitors.filter((m) => !m.url && !m.hostname)
|
||||||
|
}, [monitors])
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return (
|
||||||
|
<div className="space-y-4">
|
||||||
|
{[1, 2, 3].map((i) => (
|
||||||
|
<div key={i} className="h-32 bg-muted rounded-lg animate-pulse" />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const domainGroups = Array.from(groupedMonitors.entries()).sort((a, b) => a[0].localeCompare(b[0]))
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="space-y-4">
|
||||||
|
{/* Domain groups */}
|
||||||
|
{domainGroups.map(([domain, group]) => (
|
||||||
|
<DomainGroup key={domain} domain={domain} group={group} />
|
||||||
|
))}
|
||||||
|
|
||||||
|
{/* Ungrouped monitors (no URL/hostname) */}
|
||||||
|
{ungroupedMonitors.length > 0 && (
|
||||||
|
<Collapsible
|
||||||
|
title="Other Monitors"
|
||||||
|
icon={<Server className="h-4 w-4" />}
|
||||||
|
defaultOpen={true}
|
||||||
|
>
|
||||||
|
<div className="space-y-1">
|
||||||
|
{ungroupedMonitors.map((monitor) => (
|
||||||
|
<MonitorCard key={monitor.id} monitor={monitor} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</Collapsible>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Empty state */}
|
||||||
|
{domainGroups.length === 0 && ungroupedMonitors.length === 0 && (
|
||||||
|
<div className="text-center py-12">
|
||||||
|
<Activity className="h-12 w-12 text-muted-foreground mx-auto mb-4" />
|
||||||
|
<p className="text-lg font-medium">No monitors yet</p>
|
||||||
|
<p className="text-muted-foreground">Create your first monitor to get started</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import { Trans, useLingui } from "@lingui/react/macro"
|
import { Trans, useLingui } from "@lingui/react/macro"
|
||||||
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"
|
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"
|
||||||
import {
|
import {
|
||||||
|
AlertTriangle,
|
||||||
ArrowDownIcon,
|
ArrowDownIcon,
|
||||||
ArrowUpIcon,
|
ArrowUpIcon,
|
||||||
CheckCircleIcon,
|
CheckCircleIcon,
|
||||||
@@ -16,17 +17,12 @@ import {
|
|||||||
Settings2Icon,
|
Settings2Icon,
|
||||||
TagIcon,
|
TagIcon,
|
||||||
Trash2Icon,
|
Trash2Icon,
|
||||||
|
XCircle,
|
||||||
XCircleIcon,
|
XCircleIcon,
|
||||||
} from "lucide-react"
|
} from "lucide-react"
|
||||||
import { memo, useMemo, useState } from "react"
|
import { memo, useMemo, useState } from "react"
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/components/ui/button"
|
||||||
import {
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
|
||||||
Card,
|
|
||||||
CardContent,
|
|
||||||
CardDescription,
|
|
||||||
CardHeader,
|
|
||||||
CardTitle,
|
|
||||||
} from "@/components/ui/card"
|
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
@@ -38,24 +34,13 @@ import {
|
|||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from "@/components/ui/dropdown-menu"
|
} from "@/components/ui/dropdown-menu"
|
||||||
import { Input } from "@/components/ui/input"
|
import { Input } from "@/components/ui/input"
|
||||||
import {
|
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
|
||||||
Table,
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"
|
||||||
TableBody,
|
|
||||||
TableCell,
|
|
||||||
TableHead,
|
|
||||||
TableHeader,
|
|
||||||
TableRow,
|
|
||||||
} from "@/components/ui/table"
|
|
||||||
import {
|
|
||||||
Tooltip,
|
|
||||||
TooltipContent,
|
|
||||||
TooltipProvider,
|
|
||||||
TooltipTrigger,
|
|
||||||
} from "@/components/ui/tooltip"
|
|
||||||
import { useToast } from "@/components/ui/use-toast"
|
import { useToast } from "@/components/ui/use-toast"
|
||||||
import {
|
import {
|
||||||
deleteMonitor,
|
deleteMonitor,
|
||||||
getMonitorTypeLabel,
|
getMonitorTypeLabel,
|
||||||
|
getMonitorFaviconUrl,
|
||||||
listMonitors,
|
listMonitors,
|
||||||
manualCheck,
|
manualCheck,
|
||||||
pauseMonitor,
|
pauseMonitor,
|
||||||
@@ -99,13 +84,35 @@ function StatusIndicator({ status }: { status: MonitorStatus }) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Favicon component
|
||||||
|
function MonitorFavicon({ monitor, className }: { monitor: Monitor; className?: string }) {
|
||||||
|
const [error, setError] = useState(false)
|
||||||
|
const faviconUrl = getMonitorFaviconUrl(monitor)
|
||||||
|
|
||||||
|
if (!faviconUrl || error) {
|
||||||
|
return <GlobeIcon className={cn("h-4 w-4 text-muted-foreground", className)} />
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<img
|
||||||
|
src={faviconUrl}
|
||||||
|
alt=""
|
||||||
|
className={cn("h-4 w-4 object-contain", className)}
|
||||||
|
onError={() => setError(true)}
|
||||||
|
loading="lazy"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// Monitor Card component for grid view
|
// Monitor Card component for grid view
|
||||||
function MonitorCard({
|
function MonitorCard({
|
||||||
monitor,
|
monitor,
|
||||||
onEdit,
|
onEdit,
|
||||||
|
displayOptions,
|
||||||
}: {
|
}: {
|
||||||
monitor: Monitor
|
monitor: Monitor
|
||||||
onEdit: (m: Monitor) => void
|
onEdit: (m: Monitor) => void
|
||||||
|
displayOptions: DisplayOptions
|
||||||
}) {
|
}) {
|
||||||
const { toast } = useToast()
|
const { toast } = useToast()
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
@@ -144,8 +151,11 @@ function MonitorCard({
|
|||||||
<div className="flex items-start justify-between">
|
<div className="flex items-start justify-between">
|
||||||
<Link href={`/monitor/${monitor.id}`} className="flex items-center gap-3 cursor-pointer min-w-0">
|
<Link href={`/monitor/${monitor.id}`} className="flex items-center gap-3 cursor-pointer min-w-0">
|
||||||
<div className="shrink-0">
|
<div className="shrink-0">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<MonitorFavicon monitor={monitor} className="h-5 w-5" />
|
||||||
<StatusIndicator status={monitor.status} />
|
<StatusIndicator status={monitor.status} />
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<div className="min-w-0">
|
<div className="min-w-0">
|
||||||
<div className="font-medium truncate hover:underline">{monitor.name}</div>
|
<div className="font-medium truncate hover:underline">{monitor.name}</div>
|
||||||
<div className="text-xs text-muted-foreground truncate">
|
<div className="text-xs text-muted-foreground truncate">
|
||||||
@@ -165,10 +175,7 @@ function MonitorCard({
|
|||||||
<Edit3Icon className="mr-2 h-4 w-4" />
|
<Edit3Icon className="mr-2 h-4 w-4" />
|
||||||
Edit
|
Edit
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem onClick={() => deleteMutation.mutate(monitor.id)} className="text-destructive">
|
||||||
onClick={() => deleteMutation.mutate(monitor.id)}
|
|
||||||
className="text-destructive"
|
|
||||||
>
|
|
||||||
<Trash2Icon className="mr-2 h-4 w-4" />
|
<Trash2Icon className="mr-2 h-4 w-4" />
|
||||||
Delete
|
Delete
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
@@ -176,14 +183,32 @@ function MonitorCard({
|
|||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-2 gap-3 text-sm">
|
<div className="space-y-3">
|
||||||
<div className="space-y-1">
|
<div className="flex items-center justify-between">
|
||||||
<div className="text-xs text-muted-foreground">Type</div>
|
<div className="text-xs text-muted-foreground">Type</div>
|
||||||
<div className="inline-flex items-center rounded-md bg-muted px-2 py-1 text-xs font-medium">
|
<div className="inline-flex items-center rounded-md bg-muted px-2 py-1 text-xs font-medium">
|
||||||
{getMonitorTypeLabel(monitor.type)}
|
{getMonitorTypeLabel(monitor.type)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-1">
|
|
||||||
|
{/* Uptime - Prominent pill display */}
|
||||||
|
{displayOptions.showUptimePills && (
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<div className="flex items-center gap-2 flex-wrap">
|
||||||
|
{displayOptions.showUptimePercentage && (
|
||||||
|
<UptimePill uptime={monitor.uptime_stats?.uptime_24h ?? 100} label="24h" />
|
||||||
|
)}
|
||||||
|
{displayOptions.showUptimePercentage &&
|
||||||
|
monitor.uptime_stats?.uptime_7d !== undefined &&
|
||||||
|
monitor.uptime_stats.uptime_7d !== monitor.uptime_stats?.uptime_24h && (
|
||||||
|
<UptimePill uptime={monitor.uptime_stats.uptime_7d} label="7d" />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{displayOptions.showHeartbeatDots && <UptimeDots heartbeats={monitor.recent_heartbeats} />}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="flex items-center justify-between text-sm">
|
||||||
<div className="text-xs text-muted-foreground">Response</div>
|
<div className="text-xs text-muted-foreground">Response</div>
|
||||||
<div>
|
<div>
|
||||||
{monitor.last_check ? (
|
{monitor.last_check ? (
|
||||||
@@ -193,10 +218,6 @@ function MonitorCard({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-span-2 space-y-1">
|
|
||||||
<div className="text-xs text-muted-foreground">Uptime (24h)</div>
|
|
||||||
<UptimeBar stats={monitor.uptime_stats} />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{monitor.tags && monitor.tags.length > 0 && (
|
{monitor.tags && monitor.tags.length > 0 && (
|
||||||
@@ -224,12 +245,7 @@ function MonitorCard({
|
|||||||
onClick={() => checkMutation.mutate(monitor.id)}
|
onClick={() => checkMutation.mutate(monitor.id)}
|
||||||
disabled={checkMutation.isPending}
|
disabled={checkMutation.isPending}
|
||||||
>
|
>
|
||||||
<RefreshCwIcon
|
<RefreshCwIcon className={cn("h-4 w-4 mr-1", checkMutation.isPending && "animate-spin")} />
|
||||||
className={cn(
|
|
||||||
"h-4 w-4 mr-1",
|
|
||||||
checkMutation.isPending && "animate-spin"
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
Check
|
Check
|
||||||
</Button>
|
</Button>
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
@@ -249,9 +265,13 @@ function MonitorCard({
|
|||||||
disabled={pauseMutation.isPending}
|
disabled={pauseMutation.isPending}
|
||||||
>
|
>
|
||||||
{monitor.status === "paused" ? (
|
{monitor.status === "paused" ? (
|
||||||
<><PlayIcon className="h-4 w-4 mr-1" /> Resume</>
|
<>
|
||||||
|
<PlayIcon className="h-4 w-4 mr-1" /> Resume
|
||||||
|
</>
|
||||||
) : (
|
) : (
|
||||||
<><PauseIcon className="h-4 w-4 mr-1" /> Pause</>
|
<>
|
||||||
|
<PauseIcon className="h-4 w-4 mr-1" /> Pause
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
@@ -265,36 +285,123 @@ function MonitorCard({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Uptime bar component
|
// Uptime pill badge component - styled like domainstack.io status badges
|
||||||
function UptimeBar({ stats }: { stats?: Record<string, number> }) {
|
function UptimePill({ uptime, label = "24h" }: { uptime: number; label?: string }) {
|
||||||
const uptime24h = stats?.uptime_24h ?? 100
|
let colorClass = "bg-green-500/10 text-green-700 border-green-500/20 dark:text-green-400"
|
||||||
|
let icon = <CheckCircleIcon className="h-3.5 w-3.5 text-green-600 dark:text-green-400" />
|
||||||
|
let ringClass = "ring-green-500/20"
|
||||||
|
|
||||||
let color = "bg-green-500"
|
if (uptime < 99.9) {
|
||||||
if (uptime24h < 95) color = "bg-yellow-500"
|
colorClass = "bg-green-500/10 text-green-700 border-green-500/20 dark:text-green-400"
|
||||||
if (uptime24h < 90) color = "bg-red-500"
|
ringClass = "ring-green-500/20"
|
||||||
|
}
|
||||||
|
if (uptime < 95) {
|
||||||
|
colorClass = "bg-yellow-500/10 text-yellow-700 border-yellow-500/20 dark:text-yellow-400"
|
||||||
|
icon = <AlertTriangle className="h-3.5 w-3.5 text-yellow-600 dark:text-yellow-400" />
|
||||||
|
ringClass = "ring-yellow-500/20"
|
||||||
|
}
|
||||||
|
if (uptime < 90) {
|
||||||
|
colorClass = "bg-red-500/10 text-red-700 border-red-500/20 dark:text-red-400"
|
||||||
|
icon = <XCircle className="h-3.5 w-3.5 text-red-600 dark:text-red-400" />
|
||||||
|
ringClass = "ring-red-500/20"
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<div className="h-2 w-16 rounded-full bg-muted overflow-hidden">
|
|
||||||
<div
|
<div
|
||||||
className={cn("h-full transition-all", color)}
|
className={cn(
|
||||||
style={{ width: `${uptime24h}%` }}
|
"inline-flex items-center gap-1.5 px-3 py-1.5 rounded-full border text-xs font-semibold shadow-sm",
|
||||||
/>
|
"transition-all hover:scale-105",
|
||||||
</div>
|
colorClass,
|
||||||
<span className="text-xs text-muted-foreground w-14">
|
ringClass,
|
||||||
{formatUptime(uptime24h)}
|
"ring-1"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{icon}
|
||||||
|
<span>{formatUptime(uptime)}</span>
|
||||||
|
<span className="text-[10px] font-medium uppercase opacity-60 border-l border-current/20 pl-1.5 ml-0.5">
|
||||||
|
{label}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Uptime bar component with pill style
|
||||||
|
function UptimeBar({ stats }: { stats?: Record<string, number> }) {
|
||||||
|
const uptime24h = stats?.uptime_24h ?? 100
|
||||||
|
const uptime7d = stats?.uptime_7d ?? 100
|
||||||
|
const uptime30d = stats?.uptime_30d ?? 100
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col gap-1.5">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<UptimePill uptime={uptime24h} label="24h" />
|
||||||
|
{uptime7d !== 100 && uptime7d !== uptime24h && <UptimePill uptime={uptime7d} label="7d" />}
|
||||||
|
{uptime30d !== 100 && uptime30d !== uptime24h && uptime30d !== uptime7d && (
|
||||||
|
<UptimePill uptime={uptime30d} label="30d" />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mini uptime dots visualization - styled as a clean status bar
|
||||||
|
function UptimeDots({ heartbeats }: { heartbeats?: Array<{ status: string; time: string }> }) {
|
||||||
|
const totalSlots = 16
|
||||||
|
const recent = heartbeats?.slice(-totalSlots) || []
|
||||||
|
const emptySlots = totalSlots - recent.length
|
||||||
|
|
||||||
|
const getStatusColor = (status: string) => {
|
||||||
|
switch (status) {
|
||||||
|
case "up":
|
||||||
|
return "bg-green-500 shadow-[0_0_6px_rgba(34,197,94,0.4)]"
|
||||||
|
case "down":
|
||||||
|
return "bg-red-500 shadow-[0_0_6px_rgba(239,68,68,0.4)]"
|
||||||
|
case "paused":
|
||||||
|
return "bg-gray-400"
|
||||||
|
default:
|
||||||
|
return "bg-yellow-500"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TooltipProvider>
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
<div className="flex gap-[3px] p-1 rounded-md bg-muted/50">
|
||||||
|
{recent.map((hb, i) => (
|
||||||
|
<Tooltip key={i}>
|
||||||
|
<TooltipTrigger asChild>
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"h-4 w-[6px] rounded-full transition-all cursor-pointer hover:scale-125",
|
||||||
|
getStatusColor(hb.status)
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent side="bottom" className="text-xs">
|
||||||
|
<p className="capitalize font-medium">{hb.status}</p>
|
||||||
|
<p className="text-muted-foreground">{new Date(hb.time).toLocaleString()}</p>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
))}
|
||||||
|
{emptySlots > 0 &&
|
||||||
|
Array.from({ length: emptySlots }).map((_, i) => (
|
||||||
|
<div key={`empty-${i}`} className="h-4 w-[6px] rounded-full bg-muted" />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</TooltipProvider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// Monitor row component
|
// Monitor row component
|
||||||
function MonitorRow({
|
function MonitorRow({
|
||||||
monitor,
|
monitor,
|
||||||
onEdit,
|
onEdit,
|
||||||
|
displayOptions,
|
||||||
}: {
|
}: {
|
||||||
monitor: Monitor
|
monitor: Monitor
|
||||||
onEdit: (m: Monitor) => void
|
onEdit: (m: Monitor) => void
|
||||||
|
displayOptions: DisplayOptions
|
||||||
}) {
|
}) {
|
||||||
const { toast } = useToast()
|
const { toast } = useToast()
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
@@ -338,7 +445,7 @@ function MonitorRow({
|
|||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<Link href={`/monitor/${monitor.id}`} className="flex items-center gap-3 cursor-pointer">
|
<Link href={`/monitor/${monitor.id}`} className="flex items-center gap-3 cursor-pointer">
|
||||||
<GlobeIcon className="h-4 w-4 text-muted-foreground" />
|
<MonitorFavicon monitor={monitor} className="h-5 w-5" />
|
||||||
<div>
|
<div>
|
||||||
<div className="font-medium hover:underline">{monitor.name}</div>
|
<div className="font-medium hover:underline">{monitor.name}</div>
|
||||||
<div className="text-xs text-muted-foreground">
|
<div className="text-xs text-muted-foreground">
|
||||||
@@ -358,15 +465,17 @@ function MonitorRow({
|
|||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
{monitor.last_check ? (
|
{monitor.last_check ? (
|
||||||
<div className="text-sm">
|
<div className="text-sm">{formatPing(monitor.uptime_stats?.last_ping || 0)}</div>
|
||||||
{formatPing(monitor.uptime_stats?.last_ping || 0)}
|
|
||||||
</div>
|
|
||||||
) : (
|
) : (
|
||||||
<span className="text-sm text-muted-foreground">-</span>
|
<span className="text-sm text-muted-foreground">-</span>
|
||||||
)}
|
)}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
|
{displayOptions.showUptimePills ? (
|
||||||
<UptimeBar stats={monitor.uptime_stats} />
|
<UptimeBar stats={monitor.uptime_stats} />
|
||||||
|
) : (
|
||||||
|
<span className="text-sm text-muted-foreground">-</span>
|
||||||
|
)}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<div className="flex flex-wrap gap-1">
|
<div className="flex flex-wrap gap-1">
|
||||||
@@ -393,12 +502,7 @@ function MonitorRow({
|
|||||||
onClick={() => checkMutation.mutate(monitor.id)}
|
onClick={() => checkMutation.mutate(monitor.id)}
|
||||||
disabled={checkMutation.isPending}
|
disabled={checkMutation.isPending}
|
||||||
>
|
>
|
||||||
<RefreshCwIcon
|
<RefreshCwIcon className={cn("h-4 w-4", checkMutation.isPending && "animate-spin")} />
|
||||||
className={cn(
|
|
||||||
"h-4 w-4",
|
|
||||||
checkMutation.isPending && "animate-spin"
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</Button>
|
</Button>
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
<TooltipContent>
|
<TooltipContent>
|
||||||
@@ -417,11 +521,7 @@ function MonitorRow({
|
|||||||
onClick={() => pauseMutation.mutate(monitor.id)}
|
onClick={() => pauseMutation.mutate(monitor.id)}
|
||||||
disabled={pauseMutation.isPending}
|
disabled={pauseMutation.isPending}
|
||||||
>
|
>
|
||||||
{monitor.status === "paused" ? (
|
{monitor.status === "paused" ? <PlayIcon className="h-4 w-4" /> : <PauseIcon className="h-4 w-4" />}
|
||||||
<PlayIcon className="h-4 w-4" />
|
|
||||||
) : (
|
|
||||||
<PauseIcon className="h-4 w-4" />
|
|
||||||
)}
|
|
||||||
</Button>
|
</Button>
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
<TooltipContent>
|
<TooltipContent>
|
||||||
@@ -441,10 +541,7 @@ function MonitorRow({
|
|||||||
<Edit3Icon className="mr-2 h-4 w-4" />
|
<Edit3Icon className="mr-2 h-4 w-4" />
|
||||||
Edit
|
Edit
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem className="text-destructive" onClick={() => deleteMutation.mutate(monitor.id)}>
|
||||||
className="text-destructive"
|
|
||||||
onClick={() => deleteMutation.mutate(monitor.id)}
|
|
||||||
>
|
|
||||||
<Trash2Icon className="mr-2 h-4 w-4" />
|
<Trash2Icon className="mr-2 h-4 w-4" />
|
||||||
Delete
|
Delete
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
@@ -460,6 +557,12 @@ type ViewMode = "table" | "grid"
|
|||||||
type StatusFilter = "all" | MonitorStatus
|
type StatusFilter = "all" | MonitorStatus
|
||||||
type TypeFilter = "all" | MonitorType
|
type TypeFilter = "all" | MonitorType
|
||||||
|
|
||||||
|
interface DisplayOptions {
|
||||||
|
showUptimePills: boolean
|
||||||
|
showUptimePercentage: boolean
|
||||||
|
showHeartbeatDots: boolean
|
||||||
|
}
|
||||||
|
|
||||||
// Main component
|
// Main component
|
||||||
export default memo(function MonitorsTable() {
|
export default memo(function MonitorsTable() {
|
||||||
const { t } = useLingui()
|
const { t } = useLingui()
|
||||||
@@ -475,6 +578,12 @@ export default memo(function MonitorsTable() {
|
|||||||
window.innerWidth < 1024 ? "grid" : "table"
|
window.innerWidth < 1024 ? "grid" : "table"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const [displayOptions, setDisplayOptions] = useBrowserStorage<DisplayOptions>("monitorsDisplayOptions", {
|
||||||
|
showUptimePills: true,
|
||||||
|
showUptimePercentage: true,
|
||||||
|
showHeartbeatDots: true,
|
||||||
|
})
|
||||||
|
|
||||||
const { data: monitors = [], isLoading } = useQuery({
|
const { data: monitors = [], isLoading } = useQuery({
|
||||||
queryKey: ["monitors"],
|
queryKey: ["monitors"],
|
||||||
queryFn: listMonitors,
|
queryFn: listMonitors,
|
||||||
@@ -550,8 +659,7 @@ export default memo(function MonitorsTable() {
|
|||||||
{stats.down > 0 && (
|
{stats.down > 0 && (
|
||||||
<>
|
<>
|
||||||
{" "}
|
{" "}
|
||||||
{stats.down}{" "}
|
{stats.down} <ArrowDownIcon className="inline h-3 w-3 text-red-500" />
|
||||||
<ArrowDownIcon className="inline h-3 w-3 text-red-500" />
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{stats.paused > 0 && (
|
{stats.paused > 0 && (
|
||||||
@@ -672,6 +780,42 @@ export default memo(function MonitorsTable() {
|
|||||||
</DropdownMenuRadioGroup>
|
</DropdownMenuRadioGroup>
|
||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
|
|
||||||
|
{/* Display Options */}
|
||||||
|
<DropdownMenuLabel className="flex items-center gap-2">
|
||||||
|
<Settings2Icon className="size-4" />
|
||||||
|
<Trans>Display</Trans>
|
||||||
|
</DropdownMenuLabel>
|
||||||
|
<div className="px-2 py-1 space-y-1">
|
||||||
|
<label className="flex items-center gap-2 text-sm cursor-pointer hover:bg-muted/50 rounded px-1 py-0.5">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={displayOptions.showUptimePills}
|
||||||
|
onChange={(e) => setDisplayOptions({ ...displayOptions, showUptimePills: e.target.checked })}
|
||||||
|
className="rounded border-gray-300"
|
||||||
|
/>
|
||||||
|
<span>Show uptime pills</span>
|
||||||
|
</label>
|
||||||
|
<label className="flex items-center gap-2 text-sm cursor-pointer hover:bg-muted/50 rounded px-1 py-0.5">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={displayOptions.showUptimePercentage}
|
||||||
|
onChange={(e) => setDisplayOptions({ ...displayOptions, showUptimePercentage: e.target.checked })}
|
||||||
|
className="rounded border-gray-300"
|
||||||
|
/>
|
||||||
|
<span>Show percentage</span>
|
||||||
|
</label>
|
||||||
|
<label className="flex items-center gap-2 text-sm cursor-pointer hover:bg-muted/50 rounded px-1 py-0.5">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={displayOptions.showHeartbeatDots}
|
||||||
|
onChange={(e) => setDisplayOptions({ ...displayOptions, showHeartbeatDots: e.target.checked })}
|
||||||
|
className="rounded border-gray-300"
|
||||||
|
/>
|
||||||
|
<span>Show heartbeat dots</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<DropdownMenuSeparator />
|
||||||
|
|
||||||
{/* Status Filter */}
|
{/* Status Filter */}
|
||||||
<DropdownMenuLabel className="flex items-center gap-2">
|
<DropdownMenuLabel className="flex items-center gap-2">
|
||||||
<FilterIcon className="size-4" />
|
<FilterIcon className="size-4" />
|
||||||
@@ -760,6 +904,7 @@ export default memo(function MonitorsTable() {
|
|||||||
key={monitor.id}
|
key={monitor.id}
|
||||||
monitor={monitor}
|
monitor={monitor}
|
||||||
onEdit={setEditingMonitor}
|
onEdit={setEditingMonitor}
|
||||||
|
displayOptions={displayOptions}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</TableBody>
|
</TableBody>
|
||||||
@@ -771,6 +916,7 @@ export default memo(function MonitorsTable() {
|
|||||||
key={monitor.id}
|
key={monitor.id}
|
||||||
monitor={monitor}
|
monitor={monitor}
|
||||||
onEdit={setEditingMonitor}
|
onEdit={setEditingMonitor}
|
||||||
|
displayOptions={displayOptions}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@@ -778,10 +924,7 @@ export default memo(function MonitorsTable() {
|
|||||||
</CardContent>
|
</CardContent>
|
||||||
|
|
||||||
{/* Add Monitor Dialog */}
|
{/* Add Monitor Dialog */}
|
||||||
<AddMonitorDialog
|
<AddMonitorDialog open={isAddDialogOpen} onOpenChange={setIsAddDialogOpen} />
|
||||||
open={isAddDialogOpen}
|
|
||||||
onOpenChange={setIsAddDialogOpen}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* Edit Monitor Dialog */}
|
{/* Edit Monitor Dialog */}
|
||||||
{editingMonitor && (
|
{editingMonitor && (
|
||||||
|
|||||||
@@ -0,0 +1,875 @@
|
|||||||
|
import type { Domain } from "@/lib/domains"
|
||||||
|
import { formatDate, formatDays } from "@/lib/domains"
|
||||||
|
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card"
|
||||||
|
import { Badge } from "@/components/ui/badge"
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
import {
|
||||||
|
Globe,
|
||||||
|
Shield,
|
||||||
|
Server,
|
||||||
|
MapPin,
|
||||||
|
FileText,
|
||||||
|
Building2,
|
||||||
|
User,
|
||||||
|
Eye,
|
||||||
|
EyeOff,
|
||||||
|
Mail,
|
||||||
|
Network,
|
||||||
|
Search,
|
||||||
|
Code2,
|
||||||
|
AlertTriangle,
|
||||||
|
CheckCircle2,
|
||||||
|
Clock,
|
||||||
|
ExternalLink,
|
||||||
|
Info,
|
||||||
|
} from "lucide-react"
|
||||||
|
|
||||||
|
// --- Reusable section wrapper inspired by domainstack.io ---
|
||||||
|
|
||||||
|
function SectionCard({
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
icon: Icon,
|
||||||
|
children,
|
||||||
|
accent = "slate",
|
||||||
|
}: {
|
||||||
|
title: string
|
||||||
|
description?: string
|
||||||
|
icon: React.ElementType
|
||||||
|
children: React.ReactNode
|
||||||
|
accent?: "blue" | "green" | "orange" | "purple" | "slate" | "red" | "yellow"
|
||||||
|
}) {
|
||||||
|
const accentBorder = {
|
||||||
|
blue: "border-t-blue-500/40",
|
||||||
|
green: "border-t-green-500/40",
|
||||||
|
orange: "border-t-orange-500/40",
|
||||||
|
purple: "border-t-purple-500/40",
|
||||||
|
slate: "border-t-slate-500/30",
|
||||||
|
red: "border-t-red-500/40",
|
||||||
|
yellow: "border-t-yellow-500/40",
|
||||||
|
}
|
||||||
|
const accentIcon = {
|
||||||
|
blue: "text-blue-500",
|
||||||
|
green: "text-green-500",
|
||||||
|
orange: "text-orange-500",
|
||||||
|
purple: "text-purple-500",
|
||||||
|
slate: "text-slate-500",
|
||||||
|
red: "text-red-500",
|
||||||
|
yellow: "text-yellow-500",
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card className={cn("overflow-hidden rounded-xl border-t-2", accentBorder[accent])}>
|
||||||
|
<CardHeader className="pb-3">
|
||||||
|
<div className="flex items-center gap-2.5">
|
||||||
|
<Icon className={cn("h-5 w-5", accentIcon[accent])} />
|
||||||
|
<div>
|
||||||
|
<CardTitle className="text-base">{title}</CardTitle>
|
||||||
|
{description && <CardDescription className="text-xs">{description}</CardDescription>}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>{children}</CardContent>
|
||||||
|
</Card>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function KV({
|
||||||
|
label,
|
||||||
|
value,
|
||||||
|
suffix,
|
||||||
|
leading,
|
||||||
|
}: {
|
||||||
|
label: string
|
||||||
|
value?: string | null
|
||||||
|
suffix?: React.ReactNode
|
||||||
|
leading?: React.ReactNode
|
||||||
|
}) {
|
||||||
|
if (!value && value !== "0") return null
|
||||||
|
return (
|
||||||
|
<div className="flex h-14 min-w-0 items-center justify-between gap-3 rounded-lg border bg-background/60 px-3 py-2">
|
||||||
|
<div className="flex min-w-0 flex-col">
|
||||||
|
<div className="text-[10px] leading-none tracking-wider uppercase text-foreground/60">{label}</div>
|
||||||
|
<div className="inline-flex min-w-0 items-center gap-1.5 text-[13px] text-foreground/90 mt-1">
|
||||||
|
{leading ? <span className="shrink-0">{leading}</span> : null}
|
||||||
|
<span className="truncate">{value}</span>
|
||||||
|
{suffix ? <span className="shrink-0">{suffix}</span> : null}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function KVGrid({ children, cols = 2 }: { children: React.ReactNode; cols?: 1 | 2 | 3 | 4 }) {
|
||||||
|
const colClass =
|
||||||
|
cols === 4
|
||||||
|
? "lg:grid-cols-4 md:grid-cols-2"
|
||||||
|
: cols === 3
|
||||||
|
? "sm:grid-cols-3"
|
||||||
|
: cols === 1
|
||||||
|
? "grid-cols-1"
|
||||||
|
: "sm:grid-cols-2"
|
||||||
|
return <div className={cn("grid grid-cols-1 gap-2", colClass)}>{children}</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
function DnsGroup({
|
||||||
|
type,
|
||||||
|
records,
|
||||||
|
ttl,
|
||||||
|
}: {
|
||||||
|
type: string
|
||||||
|
records: Array<{ value: string; ttl?: string | number }>
|
||||||
|
ttl?: string | number
|
||||||
|
}) {
|
||||||
|
if (!records?.length) return null
|
||||||
|
return (
|
||||||
|
<div className="space-y-1.5">
|
||||||
|
<div className="flex items-center gap-2 mb-2">
|
||||||
|
<Badge variant="outline" className="font-mono text-[10px] px-1.5 py-0.5">
|
||||||
|
{type}
|
||||||
|
</Badge>
|
||||||
|
<span className="text-xs text-muted-foreground">
|
||||||
|
{records.length} record{records.length !== 1 ? "s" : ""}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-1">
|
||||||
|
{records.map((rec, i) => (
|
||||||
|
<div key={i} className="flex items-center gap-2 rounded-md border bg-background/40 px-2.5 py-1.5 text-sm">
|
||||||
|
<code className="text-xs font-mono text-foreground/80 truncate flex-1">{rec.value}</code>
|
||||||
|
{(rec.ttl || ttl) && (
|
||||||
|
<span className="text-[10px] text-muted-foreground shrink-0">TTL {rec.ttl || ttl}</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function MapEmbed({ lat, lon, title }: { lat: number; lon: number; title?: string }) {
|
||||||
|
const mapUrl = `https://www.openstreetmap.org/export/embed.html?bbox=${lon - 0.8}%2C${lat - 0.8}%2C${lon + 0.8}%2C${lat + 0.8}&layer=mapnik&marker=${lat}%2C${lon}`
|
||||||
|
return (
|
||||||
|
<div className="relative h-[200px] w-full overflow-hidden rounded-lg border mt-2">
|
||||||
|
<iframe title={title || "Location map"} src={mapUrl} className="h-full w-full border-0" loading="lazy" />
|
||||||
|
<a
|
||||||
|
href={`https://www.openstreetmap.org/?mlat=${lat}&mlon=${lon}#map=12/${lat}/${lon}`}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="absolute bottom-2 right-2 rounded-md bg-white/90 px-2 py-1 text-[10px] font-medium text-foreground shadow-sm hover:bg-white border"
|
||||||
|
>
|
||||||
|
Open Map
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function StatusDot({ status }: { status: string }) {
|
||||||
|
const colors: Record<string, string> = {
|
||||||
|
up: "bg-green-500",
|
||||||
|
down: "bg-red-500",
|
||||||
|
paused: "bg-gray-400",
|
||||||
|
active: "bg-green-500",
|
||||||
|
expiring: "bg-yellow-500",
|
||||||
|
expired: "bg-red-500",
|
||||||
|
unknown: "bg-gray-400",
|
||||||
|
}
|
||||||
|
return <div className={cn("h-2.5 w-2.5 rounded-full", colors[status] || "bg-yellow-500")} />
|
||||||
|
}
|
||||||
|
|
||||||
|
function CertificateCard({
|
||||||
|
cert,
|
||||||
|
index,
|
||||||
|
total,
|
||||||
|
}: {
|
||||||
|
cert: NonNullable<Domain["certificates"]> extends Array<infer T> ? T : never
|
||||||
|
index: number
|
||||||
|
total: number
|
||||||
|
}) {
|
||||||
|
const label = index === 0 ? "Leaf" : index === total - 1 ? "Root" : "Intermediate"
|
||||||
|
return (
|
||||||
|
<div className="rounded-lg border p-3 space-y-2">
|
||||||
|
<div className="flex items-center gap-2 flex-wrap">
|
||||||
|
<Badge variant={index === 0 ? "default" : "secondary"} className="text-[10px]">
|
||||||
|
{label}
|
||||||
|
</Badge>
|
||||||
|
{cert.ca_provider && (
|
||||||
|
<Badge variant="outline" className="text-[10px]">
|
||||||
|
{cert.ca_provider}
|
||||||
|
</Badge>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="text-sm space-y-1">
|
||||||
|
<p>
|
||||||
|
<span className="text-muted-foreground text-xs">Subject:</span>{" "}
|
||||||
|
<span className="font-medium">{cert.subject}</span>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<span className="text-muted-foreground text-xs">Issuer:</span> {cert.issuer}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
{cert.alt_names && cert.alt_names.length > 0 && (
|
||||||
|
<div>
|
||||||
|
<p className="text-[10px] text-muted-foreground mb-1">SANs ({cert.alt_names.length})</p>
|
||||||
|
<div className="flex flex-wrap gap-1">
|
||||||
|
{cert.alt_names.slice(0, 6).map((name, j) => (
|
||||||
|
<code key={j} className="text-[10px] bg-muted px-1.5 py-0.5 rounded">
|
||||||
|
{name}
|
||||||
|
</code>
|
||||||
|
))}
|
||||||
|
{cert.alt_names.length > 6 && (
|
||||||
|
<span className="text-[10px] text-muted-foreground">+{cert.alt_names.length - 6}</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="text-[10px] text-muted-foreground pt-1 border-t">
|
||||||
|
{cert.valid_from} → {cert.valid_to}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Main exported sections ---
|
||||||
|
|
||||||
|
export function RegistrationSection({ domain }: { domain: Domain }) {
|
||||||
|
return (
|
||||||
|
<SectionCard title="Registration" description="Registrar and registrant details" icon={Building2} accent="blue">
|
||||||
|
<KVGrid>
|
||||||
|
<KV
|
||||||
|
label="Registrar"
|
||||||
|
value={domain.registrar_name || "Unknown"}
|
||||||
|
leading={<Building2 className="h-3.5 w-3.5 text-muted-foreground" />}
|
||||||
|
/>
|
||||||
|
<KV
|
||||||
|
label="Registrant"
|
||||||
|
value={
|
||||||
|
domain.privacy_enabled
|
||||||
|
? "Hidden (Privacy Protected)"
|
||||||
|
: domain.registrant_name || domain.registrant_org || "Unknown"
|
||||||
|
}
|
||||||
|
leading={
|
||||||
|
domain.privacy_enabled ? (
|
||||||
|
<EyeOff className="h-3.5 w-3.5 text-muted-foreground" />
|
||||||
|
) : (
|
||||||
|
<User className="h-3.5 w-3.5 text-muted-foreground" />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
suffix={
|
||||||
|
domain.privacy_enabled !== undefined && (
|
||||||
|
<Badge variant={domain.privacy_enabled ? "default" : "outline"} className="text-[10px]">
|
||||||
|
{domain.privacy_enabled ? "Privacy On" : "Privacy Off"}
|
||||||
|
</Badge>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<KV label="Created" value={formatDate(domain.creation_date) || "Unknown"} />
|
||||||
|
<KV
|
||||||
|
label="Expires"
|
||||||
|
value={formatDate(domain.expiry_date) || "Unknown"}
|
||||||
|
suffix={
|
||||||
|
domain.days_until_expiry !== undefined && domain.days_until_expiry >= 0 ? (
|
||||||
|
<Badge
|
||||||
|
variant={
|
||||||
|
domain.days_until_expiry <= 7
|
||||||
|
? "destructive"
|
||||||
|
: domain.days_until_expiry <= 30
|
||||||
|
? "outline"
|
||||||
|
: "secondary"
|
||||||
|
}
|
||||||
|
className="text-[10px]"
|
||||||
|
>
|
||||||
|
{formatDays(domain.days_until_expiry)}
|
||||||
|
</Badge>
|
||||||
|
) : null
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
{domain.registrar_id && <KV label="Registrar IANA ID" value={domain.registrar_id} />}
|
||||||
|
{domain.whois_server && <KV label="WHOIS Server" value={domain.whois_server} />}
|
||||||
|
</KVGrid>
|
||||||
|
{domain.whois_status && (
|
||||||
|
<div className="mt-3 pt-3 border-t">
|
||||||
|
<p className="text-[10px] uppercase tracking-wider text-foreground/60 mb-2">EPP Status Codes</p>
|
||||||
|
<div className="flex flex-wrap gap-1.5">
|
||||||
|
{domain.whois_status.split(", ").map((s, i) => (
|
||||||
|
<Badge key={i} variant="secondary" className="text-[10px]">
|
||||||
|
{s}
|
||||||
|
</Badge>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</SectionCard>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function HostingSection({ domain }: { domain: Domain }) {
|
||||||
|
const location = [domain.host_city, domain.host_region, domain.host_country].filter(Boolean).join(", ") || null
|
||||||
|
const hasCoords = domain.host_lat !== undefined && domain.host_lon !== undefined
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SectionCard title="Hosting & Email" description="Providers and IP geolocation" icon={Server} accent="green">
|
||||||
|
<KVGrid>
|
||||||
|
{domain.dns_provider && (
|
||||||
|
<KV
|
||||||
|
label="DNS"
|
||||||
|
value={domain.dns_provider}
|
||||||
|
leading={<Network className="h-3.5 w-3.5 text-muted-foreground" />}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{domain.hosting_provider && (
|
||||||
|
<KV
|
||||||
|
label="Hosting"
|
||||||
|
value={domain.hosting_provider}
|
||||||
|
leading={<Server className="h-3.5 w-3.5 text-muted-foreground" />}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{domain.email_provider && (
|
||||||
|
<KV
|
||||||
|
label="Email"
|
||||||
|
value={domain.email_provider}
|
||||||
|
leading={<Mail className="h-3.5 w-3.5 text-muted-foreground" />}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{domain.ca_provider && (
|
||||||
|
<KV
|
||||||
|
label="Certificate Authority"
|
||||||
|
value={domain.ca_provider}
|
||||||
|
leading={<Shield className="h-3.5 w-3.5 text-muted-foreground" />}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{location && (
|
||||||
|
<KV
|
||||||
|
label="Location"
|
||||||
|
value={location}
|
||||||
|
leading={
|
||||||
|
<span className="text-sm">
|
||||||
|
{domain.host_country_code ? (
|
||||||
|
<span title={domain.host_country_code}>
|
||||||
|
{String.fromCodePoint(
|
||||||
|
...domain.host_country_code
|
||||||
|
.toUpperCase()
|
||||||
|
.split("")
|
||||||
|
.map((c) => 127397 + c.charCodeAt(0))
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
<MapPin className="h-3.5 w-3.5 text-muted-foreground" />
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</KVGrid>
|
||||||
|
{hasCoords && domain.host_lat && domain.host_lon && (
|
||||||
|
<MapEmbed lat={domain.host_lat} lon={domain.host_lon} title={`Map for ${domain.domain_name}`} />
|
||||||
|
)}
|
||||||
|
{/* IP Addresses */}
|
||||||
|
<div className="mt-3 pt-3 border-t">
|
||||||
|
<p className="text-[10px] uppercase tracking-wider text-foreground/60 mb-2">IP Addresses</p>
|
||||||
|
<div className="space-y-1">
|
||||||
|
{domain.ipv4_addresses?.map((ip) => (
|
||||||
|
<div key={ip} className="flex items-center gap-2">
|
||||||
|
<Badge variant="outline" className="text-[10px] font-mono">
|
||||||
|
IPv4
|
||||||
|
</Badge>
|
||||||
|
<code className="text-sm font-mono">{ip}</code>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
{domain.ipv6_addresses?.map((ip) => (
|
||||||
|
<div key={ip} className="flex items-center gap-2">
|
||||||
|
<Badge variant="outline" className="text-[10px] font-mono">
|
||||||
|
IPv6
|
||||||
|
</Badge>
|
||||||
|
<code className="text-sm font-mono break-all">{ip}</code>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
{!domain.ipv4_addresses?.length && !domain.ipv6_addresses?.length && (
|
||||||
|
<p className="text-sm text-muted-foreground">No IP addresses found</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</SectionCard>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function DnsSection({ domain }: { domain: Domain }) {
|
||||||
|
const aRecords =
|
||||||
|
domain.dns_a_records?.map((v) => ({ value: v })) || domain.ipv4_addresses?.map((v) => ({ value: v })) || []
|
||||||
|
const aaaaRecords =
|
||||||
|
domain.dns_aaaa_records?.map((v) => ({ value: v })) || domain.ipv6_addresses?.map((v) => ({ value: v })) || []
|
||||||
|
const mxRecords =
|
||||||
|
domain.dns_mx_records?.map((v) => ({ value: v })) || domain.mx_records?.map((v) => ({ value: v })) || []
|
||||||
|
const nsRecords =
|
||||||
|
domain.dns_ns_records?.map((v) => ({ value: v })) || domain.name_servers?.map((v) => ({ value: v })) || []
|
||||||
|
const txtRecords =
|
||||||
|
domain.dns_txt_records?.map((v) => ({ value: v })) || domain.txt_records?.map((v) => ({ value: v })) || []
|
||||||
|
|
||||||
|
if (
|
||||||
|
!aRecords.length &&
|
||||||
|
!aaaaRecords.length &&
|
||||||
|
!mxRecords.length &&
|
||||||
|
!nsRecords.length &&
|
||||||
|
!txtRecords.length &&
|
||||||
|
!domain.cname_record &&
|
||||||
|
!domain.srv_records?.length
|
||||||
|
) {
|
||||||
|
return (
|
||||||
|
<SectionCard title="DNS Records" description="A, AAAA, MX, CNAME, TXT, NS" icon={Network} accent="orange">
|
||||||
|
<div className="flex items-start gap-3 rounded-lg border border-dashed p-4">
|
||||||
|
<Info className="mt-0.5 h-4 w-4 shrink-0 text-muted-foreground" />
|
||||||
|
<p className="text-sm text-muted-foreground">No DNS records available</p>
|
||||||
|
</div>
|
||||||
|
</SectionCard>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SectionCard title="DNS Records" description="A, AAAA, MX, CNAME, TXT, NS" icon={Network} accent="orange">
|
||||||
|
<div className="space-y-4">
|
||||||
|
<DnsGroup type="A" records={aRecords} />
|
||||||
|
<DnsGroup type="AAAA" records={aaaaRecords} />
|
||||||
|
{domain.cname_record && <DnsGroup type="CNAME" records={[{ value: domain.cname_record }]} />}
|
||||||
|
<DnsGroup type="MX" records={mxRecords} />
|
||||||
|
<DnsGroup type="TXT" records={txtRecords} />
|
||||||
|
<DnsGroup type="NS" records={nsRecords} />
|
||||||
|
{domain.srv_records && domain.srv_records.length > 0 && (
|
||||||
|
<DnsGroup type="SRV" records={domain.srv_records.map((v) => ({ value: v }))} />
|
||||||
|
)}
|
||||||
|
{domain.dnssec && (
|
||||||
|
<div className="flex items-center gap-2 pt-2 border-t">
|
||||||
|
<span className="text-xs text-muted-foreground">DNSSEC</span>
|
||||||
|
<Badge variant={domain.dnssec === "signed" ? "default" : "secondary"} className="text-[10px]">
|
||||||
|
{domain.dnssec}
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</SectionCard>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SslSection({ domain }: { domain: Domain }) {
|
||||||
|
if (!domain.ssl_valid_to) {
|
||||||
|
return (
|
||||||
|
<SectionCard title="SSL Certificates" description="Issuer and validity" icon={Shield} accent="purple">
|
||||||
|
<div className="flex items-start gap-3 rounded-lg border border-dashed p-4">
|
||||||
|
<Info className="mt-0.5 h-4 w-4 shrink-0 text-muted-foreground" />
|
||||||
|
<p className="text-sm text-muted-foreground">No SSL certificate information available</p>
|
||||||
|
</div>
|
||||||
|
</SectionCard>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SectionCard title="SSL Certificates" description="Issuer and validity" icon={Shield} accent="purple">
|
||||||
|
<div className="space-y-3">
|
||||||
|
<KVGrid>
|
||||||
|
<KV
|
||||||
|
label="Status"
|
||||||
|
value={domain.ssl_days_until && domain.ssl_days_until > 0 ? "Valid" : "Expired"}
|
||||||
|
leading={<StatusDot status={domain.ssl_days_until && domain.ssl_days_until > 0 ? "up" : "down"} />}
|
||||||
|
suffix={
|
||||||
|
domain.ssl_days_until !== undefined && (
|
||||||
|
<Badge
|
||||||
|
variant={
|
||||||
|
domain.ssl_days_until <= 7 ? "destructive" : domain.ssl_days_until <= 30 ? "outline" : "secondary"
|
||||||
|
}
|
||||||
|
className="text-[10px]"
|
||||||
|
>
|
||||||
|
{formatDays(domain.ssl_days_until)}
|
||||||
|
</Badge>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<KV label="Subject" value={domain.ssl_subject || domain.domain_name} />
|
||||||
|
<KV label="Issuer" value={domain.ssl_issuer || "Unknown"} />
|
||||||
|
<KV label="Valid From" value={formatDate(domain.ssl_valid_from) || "Unknown"} />
|
||||||
|
<KV label="Valid To" value={formatDate(domain.ssl_valid_to) || "Unknown"} />
|
||||||
|
{domain.ssl_key_size && <KV label="Key Size" value={`${domain.ssl_key_size} bits`} />}
|
||||||
|
{domain.ssl_signature_algo && <KV label="Algorithm" value={domain.ssl_signature_algo} />}
|
||||||
|
</KVGrid>
|
||||||
|
{domain.certificates && domain.certificates.length > 0 && (
|
||||||
|
<div className="space-y-2 pt-2 border-t">
|
||||||
|
<p className="text-[10px] uppercase tracking-wider text-foreground/60">
|
||||||
|
Certificate Chain ({domain.certificates.length})
|
||||||
|
</p>
|
||||||
|
{domain.certificates.map((cert, i) => (
|
||||||
|
<CertificateCard key={i} cert={cert} index={i} total={domain.certificates?.length ?? 0} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</SectionCard>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SeoSection({ domain }: { domain: Domain }) {
|
||||||
|
const seo = domain.seo_meta
|
||||||
|
if (!seo) {
|
||||||
|
return (
|
||||||
|
<SectionCard title="SEO & Social" description="Meta tags, previews, robots.txt" icon={Search} accent="slate">
|
||||||
|
<div className="flex items-start gap-3 rounded-lg border border-dashed p-4">
|
||||||
|
<Info className="mt-0.5 h-4 w-4 shrink-0 text-muted-foreground" />
|
||||||
|
<p className="text-sm text-muted-foreground">No SEO data available</p>
|
||||||
|
</div>
|
||||||
|
</SectionCard>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const metaTags = seo.general
|
||||||
|
const og = seo.openGraph
|
||||||
|
const twitter = seo.twitter
|
||||||
|
const robots = seo.robots
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SectionCard title="SEO & Social" description="Meta tags, previews, robots.txt" icon={Search} accent="slate">
|
||||||
|
<div className="space-y-4">
|
||||||
|
{/* Meta Tags */}
|
||||||
|
{metaTags && (
|
||||||
|
<div>
|
||||||
|
<div className="flex items-center gap-2 mb-2">
|
||||||
|
<FileText className="h-4 w-4 text-muted-foreground" />
|
||||||
|
<span className="text-xs font-medium">Meta Tags</span>
|
||||||
|
{Object.values(metaTags).filter(Boolean).length > 0 && (
|
||||||
|
<Badge variant="secondary" className="text-[10px]">
|
||||||
|
{Object.values(metaTags).filter(Boolean).length}
|
||||||
|
</Badge>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-2">
|
||||||
|
{metaTags.title && <KV label="Title" value={metaTags.title} />}
|
||||||
|
{metaTags.description && <KV label="Description" value={metaTags.description} />}
|
||||||
|
{metaTags.canonical && <KV label="Canonical" value={metaTags.canonical} />}
|
||||||
|
{metaTags.robots && <KV label="Robots" value={metaTags.robots} />}
|
||||||
|
{metaTags.author && <KV label="Author" value={metaTags.author} />}
|
||||||
|
{metaTags.keywords && (
|
||||||
|
<div className="sm:col-span-2">
|
||||||
|
<KV
|
||||||
|
label="Keywords"
|
||||||
|
value={metaTags.keywords.substring(0, 120) + (metaTags.keywords.length > 120 ? "..." : "")}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Open Graph Preview */}
|
||||||
|
{og && (og.title || og.description) && (
|
||||||
|
<div className="pt-3 border-t">
|
||||||
|
<div className="flex items-center gap-2 mb-2">
|
||||||
|
<Globe className="h-4 w-4 text-muted-foreground" />
|
||||||
|
<span className="text-xs font-medium">Open Graph</span>
|
||||||
|
</div>
|
||||||
|
<div className="rounded-lg border bg-background/40 p-3 space-y-1">
|
||||||
|
{og.images && og.images.length > 0 && (
|
||||||
|
<div className="mb-2">
|
||||||
|
<img
|
||||||
|
src={og.images[0]}
|
||||||
|
alt="OG preview"
|
||||||
|
className="max-h-32 rounded-md object-cover w-full"
|
||||||
|
loading="lazy"
|
||||||
|
onError={(e) => ((e.target as HTMLImageElement).style.display = "none")}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<p className="text-sm font-medium text-foreground/90">{og.title}</p>
|
||||||
|
<p className="text-xs text-muted-foreground line-clamp-2">{og.description}</p>
|
||||||
|
{og.url && (
|
||||||
|
<a
|
||||||
|
href={og.url}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="text-[10px] text-primary hover:underline truncate block"
|
||||||
|
>
|
||||||
|
{og.url}
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Twitter Card Preview */}
|
||||||
|
{twitter && (twitter.title || twitter.description) && (
|
||||||
|
<div className="pt-3 border-t">
|
||||||
|
<div className="flex items-center gap-2 mb-2">
|
||||||
|
<ExternalLink className="h-4 w-4 text-muted-foreground" />
|
||||||
|
<span className="text-xs font-medium">Twitter/X Card</span>
|
||||||
|
{twitter.card && (
|
||||||
|
<Badge variant="outline" className="text-[10px]">
|
||||||
|
{twitter.card}
|
||||||
|
</Badge>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="rounded-lg border bg-background/40 p-3 space-y-1">
|
||||||
|
{twitter.image && (
|
||||||
|
<img
|
||||||
|
src={twitter.image}
|
||||||
|
alt="Twitter preview"
|
||||||
|
className="max-h-32 rounded-md object-cover w-full"
|
||||||
|
loading="lazy"
|
||||||
|
onError={(e) => ((e.target as HTMLImageElement).style.display = "none")}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<p className="text-sm font-medium text-foreground/90">{twitter.title}</p>
|
||||||
|
<p className="text-xs text-muted-foreground line-clamp-2">{twitter.description}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* robots.txt */}
|
||||||
|
{robots?.fetched && (
|
||||||
|
<div className="pt-3 border-t">
|
||||||
|
<div className="flex items-center gap-2 mb-2">
|
||||||
|
<Code2 className="h-4 w-4 text-muted-foreground" />
|
||||||
|
<span className="text-xs font-medium">robots.txt</span>
|
||||||
|
</div>
|
||||||
|
{robots.sitemaps && robots.sitemaps.length > 0 && (
|
||||||
|
<div className="mb-2">
|
||||||
|
<p className="text-[10px] text-muted-foreground mb-1">Sitemaps</p>
|
||||||
|
<div className="flex flex-wrap gap-1">
|
||||||
|
{robots.sitemaps.map((s, i) => (
|
||||||
|
<a
|
||||||
|
key={i}
|
||||||
|
href={s}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="text-[10px] text-primary hover:underline truncate max-w-[300px]"
|
||||||
|
>
|
||||||
|
{s}
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{robots.groups && robots.groups.length > 0 && (
|
||||||
|
<div className="space-y-2">
|
||||||
|
{robots.groups.map((group, i) => (
|
||||||
|
<div key={i} className="rounded-md bg-muted/50 p-2 text-xs space-y-1">
|
||||||
|
<p className="text-muted-foreground font-medium">User-agent: {group.userAgents.join(", ")}</p>
|
||||||
|
{group.rules.map((rule, j) => (
|
||||||
|
<div key={j} className="flex items-center gap-1.5">
|
||||||
|
{rule.type === "Allow" ? (
|
||||||
|
<CheckCircle2 className="h-3 w-3 text-green-500" />
|
||||||
|
) : (
|
||||||
|
<AlertTriangle className="h-3 w-3 text-yellow-500" />
|
||||||
|
)}
|
||||||
|
<span className={rule.type === "Allow" ? "text-green-600" : "text-yellow-600"}>
|
||||||
|
{rule.type}: {rule.value}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</SectionCard>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function DomainTypeBadge({ type }: { type?: string }) {
|
||||||
|
if (!type) return null
|
||||||
|
const configs: Record<string, { color: string; icon: React.ElementType; label: string }> = {
|
||||||
|
expiry: { color: "bg-blue-500/10 text-blue-600 border-blue-500/20", icon: Clock, label: "Expiry Monitor" },
|
||||||
|
watchlist: { color: "bg-purple-500/10 text-purple-600 border-purple-500/20", icon: Eye, label: "Watchlist" },
|
||||||
|
portfolio: { color: "bg-green-500/10 text-green-600 border-green-500/20", icon: Globe, label: "Portfolio" },
|
||||||
|
}
|
||||||
|
const config = configs[type] || configs.expiry
|
||||||
|
const Icon = config.icon
|
||||||
|
return (
|
||||||
|
<Badge variant="outline" className={cn("gap-1 text-[10px]", config.color)}>
|
||||||
|
<Icon className="h-3 w-3" />
|
||||||
|
{config.label}
|
||||||
|
</Badge>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ValuationSection({ domain }: { domain: Domain }) {
|
||||||
|
const hasData = (domain.purchase_price ?? 0) > 0 || (domain.current_value ?? 0) > 0 || (domain.renewal_cost ?? 0) > 0
|
||||||
|
if (!hasData) return null
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SectionCard
|
||||||
|
title="Valuation & Costs"
|
||||||
|
description="Financial information and renewal settings"
|
||||||
|
icon={FileText}
|
||||||
|
accent="yellow"
|
||||||
|
>
|
||||||
|
<KVGrid>
|
||||||
|
{(domain.purchase_price ?? 0) > 0 && <KV label="Purchase Price" value={`$${domain.purchase_price}`} />}
|
||||||
|
{(domain.current_value ?? 0) > 0 && <KV label="Current Value" value={`$${domain.current_value}`} />}
|
||||||
|
{(domain.renewal_cost ?? 0) > 0 && <KV label="Renewal Cost" value={`$${domain.renewal_cost}`} />}
|
||||||
|
<KV
|
||||||
|
label="Auto-renew"
|
||||||
|
value={domain.auto_renew ? "Enabled" : "Disabled"}
|
||||||
|
leading={
|
||||||
|
domain.auto_renew ? (
|
||||||
|
<CheckCircle2 className="h-3.5 w-3.5 text-green-500" />
|
||||||
|
) : (
|
||||||
|
<AlertTriangle className="h-3.5 w-3.5 text-yellow-500" />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</KVGrid>
|
||||||
|
</SectionCard>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function DomainExpiryOverview({ domain }: { domain: Domain }) {
|
||||||
|
return (
|
||||||
|
<div className="grid sm:grid-cols-2 gap-4">
|
||||||
|
{/* Domain Expiry */}
|
||||||
|
<Card
|
||||||
|
className={cn(
|
||||||
|
"overflow-hidden",
|
||||||
|
domain.days_until_expiry !== undefined && domain.days_until_expiry >= 0 && domain.days_until_expiry <= 7
|
||||||
|
? "border-red-500/30"
|
||||||
|
: domain.days_until_expiry !== undefined && domain.days_until_expiry >= 0 && domain.days_until_expiry <= 30
|
||||||
|
? "border-yellow-500/30"
|
||||||
|
: ""
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"h-1",
|
||||||
|
domain.days_until_expiry !== undefined && domain.days_until_expiry >= 0 && domain.days_until_expiry <= 7
|
||||||
|
? "bg-red-500"
|
||||||
|
: domain.days_until_expiry !== undefined &&
|
||||||
|
domain.days_until_expiry >= 0 &&
|
||||||
|
domain.days_until_expiry <= 30
|
||||||
|
? "bg-yellow-500"
|
||||||
|
: "bg-green-500"
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<CardContent className="p-5">
|
||||||
|
<div className="flex items-center justify-between mb-3">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"p-2.5 rounded-xl",
|
||||||
|
domain.days_until_expiry !== undefined &&
|
||||||
|
domain.days_until_expiry >= 0 &&
|
||||||
|
domain.days_until_expiry <= 7
|
||||||
|
? "bg-red-500/10"
|
||||||
|
: domain.days_until_expiry !== undefined &&
|
||||||
|
domain.days_until_expiry >= 0 &&
|
||||||
|
domain.days_until_expiry <= 30
|
||||||
|
? "bg-yellow-500/10"
|
||||||
|
: "bg-green-500/10"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<Globe
|
||||||
|
className={cn(
|
||||||
|
"h-5 w-5",
|
||||||
|
domain.days_until_expiry !== undefined &&
|
||||||
|
domain.days_until_expiry >= 0 &&
|
||||||
|
domain.days_until_expiry <= 7
|
||||||
|
? "text-red-500"
|
||||||
|
: domain.days_until_expiry !== undefined &&
|
||||||
|
domain.days_until_expiry >= 0 &&
|
||||||
|
domain.days_until_expiry <= 30
|
||||||
|
? "text-yellow-500"
|
||||||
|
: "text-green-500"
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="text-sm text-muted-foreground">Domain Expires</p>
|
||||||
|
<p className="font-semibold">{formatDate(domain.expiry_date) || "N/A"}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"text-xl font-bold",
|
||||||
|
domain.days_until_expiry !== undefined && domain.days_until_expiry >= 0 && domain.days_until_expiry <= 7
|
||||||
|
? "text-red-500"
|
||||||
|
: domain.days_until_expiry !== undefined &&
|
||||||
|
domain.days_until_expiry >= 0 &&
|
||||||
|
domain.days_until_expiry <= 30
|
||||||
|
? "text-yellow-500"
|
||||||
|
: "text-green-500"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{typeof domain.days_until_expiry === "number" && domain.days_until_expiry >= 0
|
||||||
|
? formatDays(domain.days_until_expiry)
|
||||||
|
: domain.days_until_expiry === -1
|
||||||
|
? "No data"
|
||||||
|
: "N/A"}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{/* SSL Expiry */}
|
||||||
|
<Card
|
||||||
|
className={cn(
|
||||||
|
"overflow-hidden",
|
||||||
|
domain.ssl_days_until !== undefined && domain.ssl_days_until >= 0 && domain.ssl_days_until <= 7
|
||||||
|
? "border-red-500/30"
|
||||||
|
: domain.ssl_days_until !== undefined && domain.ssl_days_until >= 0 && domain.ssl_days_until <= 30
|
||||||
|
? "border-yellow-500/30"
|
||||||
|
: ""
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"h-1",
|
||||||
|
domain.ssl_days_until !== undefined && domain.ssl_days_until >= 0 && domain.ssl_days_until <= 7
|
||||||
|
? "bg-red-500"
|
||||||
|
: domain.ssl_days_until !== undefined && domain.ssl_days_until >= 0 && domain.ssl_days_until <= 30
|
||||||
|
? "bg-yellow-500"
|
||||||
|
: "bg-green-500"
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<CardContent className="p-5">
|
||||||
|
<div className="flex items-center justify-between mb-3">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"p-2.5 rounded-xl",
|
||||||
|
domain.ssl_days_until !== undefined && domain.ssl_days_until >= 0 && domain.ssl_days_until <= 7
|
||||||
|
? "bg-red-500/10"
|
||||||
|
: domain.ssl_days_until !== undefined && domain.ssl_days_until >= 0 && domain.ssl_days_until <= 30
|
||||||
|
? "bg-yellow-500/10"
|
||||||
|
: "bg-green-500/10"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<Shield
|
||||||
|
className={cn(
|
||||||
|
"h-5 w-5",
|
||||||
|
domain.ssl_days_until !== undefined && domain.ssl_days_until >= 0 && domain.ssl_days_until <= 7
|
||||||
|
? "text-red-500"
|
||||||
|
: domain.ssl_days_until !== undefined && domain.ssl_days_until >= 0 && domain.ssl_days_until <= 30
|
||||||
|
? "text-yellow-500"
|
||||||
|
: "text-green-500"
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="text-sm text-muted-foreground">SSL Expires</p>
|
||||||
|
<p className="font-semibold">{formatDate(domain.ssl_valid_to) || "No SSL"}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"text-xl font-bold",
|
||||||
|
domain.ssl_days_until !== undefined && domain.ssl_days_until >= 0 && domain.ssl_days_until <= 7
|
||||||
|
? "text-red-500"
|
||||||
|
: domain.ssl_days_until !== undefined && domain.ssl_days_until >= 0 && domain.ssl_days_until <= 30
|
||||||
|
? "text-yellow-500"
|
||||||
|
: "text-green-500"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{typeof domain.ssl_days_until === "number" && domain.ssl_days_until >= 0
|
||||||
|
? formatDays(domain.ssl_days_until)
|
||||||
|
: "N/A"}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,403 @@
|
|||||||
|
import { useEffect, useMemo, useState } from "react"
|
||||||
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
|
||||||
|
import { Badge } from "@/components/ui/badge"
|
||||||
|
import { Globe, Shield, Server, MapPin, FileText, Info, AlertTriangle } from "lucide-react"
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
import { formatDate } from "@/lib/domains"
|
||||||
|
import type { Monitor, Heartbeat } from "@/lib/monitors"
|
||||||
|
|
||||||
|
// --- Styled components inspired by domainstack.io ---
|
||||||
|
|
||||||
|
function InfoSection({
|
||||||
|
title,
|
||||||
|
icon: Icon,
|
||||||
|
children,
|
||||||
|
accent = "slate",
|
||||||
|
}: {
|
||||||
|
title: string
|
||||||
|
icon: React.ElementType
|
||||||
|
children: React.ReactNode
|
||||||
|
accent?: "blue" | "green" | "orange" | "purple" | "slate"
|
||||||
|
}) {
|
||||||
|
const accentColors = {
|
||||||
|
blue: "border-blue-500/10 bg-blue-500/5",
|
||||||
|
green: "border-green-500/10 bg-green-500/5",
|
||||||
|
orange: "border-orange-500/10 bg-orange-500/5",
|
||||||
|
purple: "border-purple-500/10 bg-purple-500/5",
|
||||||
|
slate: "border-border bg-background/60",
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card className={cn("relative overflow-hidden rounded-xl border", accentColors[accent])}>
|
||||||
|
<CardHeader className="pb-3">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Icon className="h-4 w-4 text-muted-foreground" />
|
||||||
|
<CardTitle className="text-base">{title}</CardTitle>
|
||||||
|
</div>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>{children}</CardContent>
|
||||||
|
</Card>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function KV({
|
||||||
|
label,
|
||||||
|
value,
|
||||||
|
suffix,
|
||||||
|
leading,
|
||||||
|
}: {
|
||||||
|
label: string
|
||||||
|
value: string
|
||||||
|
suffix?: React.ReactNode
|
||||||
|
leading?: React.ReactNode
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<div className="flex h-14 min-w-0 items-center justify-between gap-3 rounded-lg border bg-background/60 px-3 py-2">
|
||||||
|
<div className="flex min-w-0 flex-col">
|
||||||
|
<div className="text-[10px] leading-none tracking-wider uppercase text-foreground/70">{label}</div>
|
||||||
|
<div className="inline-flex min-w-0 items-center gap-1.5 text-[13px] text-foreground/95 mt-1">
|
||||||
|
{leading ? <span className="shrink-0">{leading}</span> : null}
|
||||||
|
<span className="truncate">{value}</span>
|
||||||
|
{suffix ? <span className="shrink-0">{suffix}</span> : null}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function KVGrid({ children, cols = 2 }: { children: React.ReactNode; cols?: 1 | 2 | 3 }) {
|
||||||
|
const colClass = cols === 3 ? "sm:grid-cols-3" : cols === 1 ? "grid-cols-1" : "sm:grid-cols-2"
|
||||||
|
return <div className={cn("grid grid-cols-1 gap-2", colClass)}>{children}</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Data fetching hooks ---
|
||||||
|
|
||||||
|
interface DnsRecord {
|
||||||
|
type: string
|
||||||
|
value: string
|
||||||
|
ttl?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DomainInfo {
|
||||||
|
hostname: string | null
|
||||||
|
rootDomain: string | null
|
||||||
|
dnsRecords: DnsRecord[]
|
||||||
|
geo: { city?: string; region?: string; country?: string; lat?: number; lon?: number } | null
|
||||||
|
ssl: { valid: boolean; expiry?: string; daysLeft?: number } | null
|
||||||
|
seo: {
|
||||||
|
title?: string
|
||||||
|
description?: string
|
||||||
|
canonical?: string
|
||||||
|
robots?: string
|
||||||
|
generator?: string
|
||||||
|
} | null
|
||||||
|
loading: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
function useDomainInfo(monitor: Monitor | undefined, heartbeats: Heartbeat[] | undefined) {
|
||||||
|
const [info, setInfo] = useState<DomainInfo>({
|
||||||
|
hostname: null,
|
||||||
|
rootDomain: null,
|
||||||
|
dnsRecords: [],
|
||||||
|
geo: null,
|
||||||
|
ssl: null,
|
||||||
|
seo: null,
|
||||||
|
loading: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
const hostname = useMemo(() => {
|
||||||
|
if (!monitor) return null
|
||||||
|
if (monitor.hostname) return monitor.hostname.toLowerCase()
|
||||||
|
if (monitor.url) {
|
||||||
|
try {
|
||||||
|
const url = new URL(monitor.url.startsWith("http") ? monitor.url : `https://${monitor.url}`)
|
||||||
|
return url.hostname.toLowerCase()
|
||||||
|
} catch {
|
||||||
|
return monitor.url.toLowerCase()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}, [monitor])
|
||||||
|
|
||||||
|
const rootDomain = useMemo(() => {
|
||||||
|
if (!hostname) return null
|
||||||
|
const clean = hostname.replace(/^www\./, "")
|
||||||
|
const parts = clean.split(".")
|
||||||
|
if (parts.length <= 2) return clean
|
||||||
|
const specialTLDs = ["co.uk", "com.au", "co.jp", "com.br", "co.nz", "co.za", "co.in", "com.cn"]
|
||||||
|
const lastTwo = parts.slice(-2).join(".")
|
||||||
|
const lastThree = parts.slice(-3).join(".")
|
||||||
|
if (specialTLDs.includes(lastThree)) return lastThree
|
||||||
|
return lastTwo
|
||||||
|
}, [hostname])
|
||||||
|
|
||||||
|
// SSL from latest heartbeat
|
||||||
|
useEffect(() => {
|
||||||
|
if (!heartbeats?.length) {
|
||||||
|
setInfo((prev) => ({ ...prev, ssl: null }))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const latest = heartbeats[0]
|
||||||
|
if (latest.cert_expiry) {
|
||||||
|
const expiry = new Date(latest.cert_expiry * 1000)
|
||||||
|
const daysLeft = Math.ceil((expiry.getTime() - Date.now()) / (1000 * 60 * 60 * 24))
|
||||||
|
setInfo((prev) => ({
|
||||||
|
...prev,
|
||||||
|
ssl: { valid: latest.cert_valid ?? true, expiry: expiry.toISOString(), daysLeft },
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}, [heartbeats])
|
||||||
|
|
||||||
|
// Fetch DNS, geo, SEO
|
||||||
|
useEffect(() => {
|
||||||
|
if (!hostname) {
|
||||||
|
setInfo((prev) => ({ ...prev, loading: false }))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let cancelled = false
|
||||||
|
|
||||||
|
async function fetchData() {
|
||||||
|
// DNS via Cloudflare DoH
|
||||||
|
const dnsPromise = fetch(`https://cloudflare-dns.com/dns-query?name=${encodeURIComponent(hostname)}&type=A`, {
|
||||||
|
headers: { Accept: "application/dns-json" },
|
||||||
|
})
|
||||||
|
.then((r) => r.json())
|
||||||
|
.then((data) => {
|
||||||
|
const records: DnsRecord[] = []
|
||||||
|
if (data.Answer) {
|
||||||
|
for (const ans of data.Answer) {
|
||||||
|
records.push({ type: "A", value: ans.data, ttl: ans.TTL })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return records
|
||||||
|
})
|
||||||
|
.catch(() => [] as DnsRecord[])
|
||||||
|
|
||||||
|
// Geolocation via ipapi.co (free, no key needed for basic)
|
||||||
|
const geoPromise = fetch(`https://ipapi.co/${encodeURIComponent(hostname)}/json/`, {
|
||||||
|
headers: { Accept: "application/json" },
|
||||||
|
})
|
||||||
|
.then((r) => {
|
||||||
|
if (!r.ok) throw new Error("geo failed")
|
||||||
|
return r.json()
|
||||||
|
})
|
||||||
|
.then((data) => ({
|
||||||
|
city: data.city,
|
||||||
|
region: data.region,
|
||||||
|
country: data.country_name,
|
||||||
|
lat: data.latitude,
|
||||||
|
lon: data.longitude,
|
||||||
|
}))
|
||||||
|
.catch(() => null)
|
||||||
|
|
||||||
|
// SEO meta tags
|
||||||
|
const seoPromise = monitor?.url
|
||||||
|
? fetch(monitor.url, { method: "GET", mode: "cors" })
|
||||||
|
.then((r) => r.text())
|
||||||
|
.then((html) => {
|
||||||
|
const parser = new DOMParser()
|
||||||
|
const doc = parser.parseFromString(html, "text/html")
|
||||||
|
const getMeta = (name: string) =>
|
||||||
|
doc.querySelector(`meta[name="${name}"]`)?.getAttribute("content") ||
|
||||||
|
doc.querySelector(`meta[property="og:${name}"]`)?.getAttribute("content") ||
|
||||||
|
undefined
|
||||||
|
return {
|
||||||
|
title: doc.querySelector("title")?.textContent || undefined,
|
||||||
|
description: getMeta("description"),
|
||||||
|
canonical: doc.querySelector('link[rel="canonical"]')?.getAttribute("href") || undefined,
|
||||||
|
robots: getMeta("robots"),
|
||||||
|
generator: getMeta("generator"),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => null)
|
||||||
|
: Promise.resolve(null)
|
||||||
|
|
||||||
|
const [dnsRecords, geo, seo] = await Promise.all([dnsPromise, geoPromise, seoPromise])
|
||||||
|
|
||||||
|
if (!cancelled) {
|
||||||
|
setInfo((prev) => ({
|
||||||
|
...prev,
|
||||||
|
hostname,
|
||||||
|
rootDomain,
|
||||||
|
dnsRecords,
|
||||||
|
geo,
|
||||||
|
seo,
|
||||||
|
loading: false,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchData()
|
||||||
|
return () => {
|
||||||
|
cancelled = true
|
||||||
|
}
|
||||||
|
}, [hostname, monitor?.url, rootDomain])
|
||||||
|
|
||||||
|
return { ...info, hostname, rootDomain }
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Map component ---
|
||||||
|
|
||||||
|
function MapEmbed({ lat, lon, hostname }: { lat: number; lon: number; hostname?: string | null }) {
|
||||||
|
const mapUrl = `https://www.openstreetmap.org/export/embed.html?bbox=${lon - 0.5}%2C${lat - 0.5}%2C${lon + 0.5}%2C${lat + 0.5}&layer=mapnik&marker=${lat}%2C${lon}`
|
||||||
|
return (
|
||||||
|
<div className="relative h-[220px] w-full overflow-hidden rounded-lg border">
|
||||||
|
<iframe
|
||||||
|
title={`Map for ${hostname || "location"}`}
|
||||||
|
src={mapUrl}
|
||||||
|
className="h-full w-full border-0"
|
||||||
|
loading="lazy"
|
||||||
|
/>
|
||||||
|
<a
|
||||||
|
href={`https://www.openstreetmap.org/?mlat=${lat}&mlon=${lon}#map=12/${lat}/${lon}`}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="absolute bottom-2 right-2 rounded bg-white/90 px-2 py-1 text-[10px] font-medium text-foreground shadow hover:bg-white"
|
||||||
|
>
|
||||||
|
View Larger Map
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Main composite component ---
|
||||||
|
|
||||||
|
export function MonitorInfoSections({
|
||||||
|
monitor,
|
||||||
|
heartbeats,
|
||||||
|
}: {
|
||||||
|
monitor: Monitor | undefined
|
||||||
|
heartbeats: Heartbeat[] | undefined
|
||||||
|
}) {
|
||||||
|
const { hostname, rootDomain, dnsRecords, geo, ssl, seo, loading } = useDomainInfo(monitor, heartbeats)
|
||||||
|
|
||||||
|
if (!monitor) return null
|
||||||
|
|
||||||
|
const showSeo = seo && (seo.title || seo.description)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="grid gap-4">
|
||||||
|
{/* Registration / Domain Overview */}
|
||||||
|
<InfoSection title="Domain Overview" icon={Globe} accent="blue">
|
||||||
|
<KVGrid>
|
||||||
|
<KV
|
||||||
|
label="Hostname"
|
||||||
|
value={hostname || "N/A"}
|
||||||
|
leading={<Globe className="h-3.5 w-3.5 text-muted-foreground" />}
|
||||||
|
/>
|
||||||
|
<KV
|
||||||
|
label="Root Domain"
|
||||||
|
value={rootDomain || "N/A"}
|
||||||
|
leading={<Server className="h-3.5 w-3.5 text-muted-foreground" />}
|
||||||
|
/>
|
||||||
|
<KV label="Type" value={monitor.type.toUpperCase()} />
|
||||||
|
<KV label="Created" value={formatDate(monitor.created)} />
|
||||||
|
</KVGrid>
|
||||||
|
</InfoSection>
|
||||||
|
|
||||||
|
<div className="grid sm:grid-cols-2 gap-4">
|
||||||
|
{/* Hosting & Geolocation */}
|
||||||
|
<InfoSection title="Hosting & Location" icon={MapPin} accent="green">
|
||||||
|
{geo ? (
|
||||||
|
<div className="space-y-3">
|
||||||
|
<KVGrid cols={1}>
|
||||||
|
<KV
|
||||||
|
label="Location"
|
||||||
|
value={[geo.city, geo.region, geo.country].filter(Boolean).join(", ") || "Unknown"}
|
||||||
|
leading={<MapPin className="h-3.5 w-3.5 text-muted-foreground" />}
|
||||||
|
/>
|
||||||
|
</KVGrid>
|
||||||
|
{geo.lat && geo.lon ? <MapEmbed lat={geo.lat} lon={geo.lon} hostname={hostname} /> : null}
|
||||||
|
</div>
|
||||||
|
) : loading ? (
|
||||||
|
<div className="flex items-center gap-2 py-4 text-sm text-muted-foreground">
|
||||||
|
<span className="h-4 w-4 animate-spin rounded-full border-2 border-muted-foreground/30 border-t-foreground" />
|
||||||
|
Looking up location...
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="flex items-start gap-3 rounded-lg border border-dashed p-4">
|
||||||
|
<Info className="mt-0.5 h-4 w-4 shrink-0 text-muted-foreground" />
|
||||||
|
<div className="text-sm text-muted-foreground">No geolocation data available.</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</InfoSection>
|
||||||
|
|
||||||
|
{/* SSL Certificate */}
|
||||||
|
<InfoSection title="SSL Certificate" icon={Shield} accent="purple">
|
||||||
|
{ssl ? (
|
||||||
|
<div className="space-y-2">
|
||||||
|
<KVGrid cols={1}>
|
||||||
|
<KV
|
||||||
|
label="Status"
|
||||||
|
value={ssl.valid ? "Valid" : "Invalid"}
|
||||||
|
leading={
|
||||||
|
<div className={cn("h-2.5 w-2.5 rounded-full", ssl.valid ? "bg-green-500" : "bg-red-500")} />
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<KV label="Expires" value={ssl.expiry ? formatDate(ssl.expiry) : "Unknown"} />
|
||||||
|
{ssl.daysLeft !== undefined && (
|
||||||
|
<KV
|
||||||
|
label="Days Left"
|
||||||
|
value={`${ssl.daysLeft} days`}
|
||||||
|
suffix={
|
||||||
|
ssl.daysLeft <= 7 ? (
|
||||||
|
<Badge variant="destructive" className="text-[10px]">
|
||||||
|
Expiring Soon
|
||||||
|
</Badge>
|
||||||
|
) : ssl.daysLeft <= 30 ? (
|
||||||
|
<Badge variant="outline" className="text-[10px] border-yellow-500/50 text-yellow-600">
|
||||||
|
Warning
|
||||||
|
</Badge>
|
||||||
|
) : null
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</KVGrid>
|
||||||
|
</div>
|
||||||
|
) : monitor.type === "https" || monitor.url?.startsWith("https") ? (
|
||||||
|
<div className="flex items-start gap-3 rounded-lg border border-dashed p-4">
|
||||||
|
<AlertTriangle className="mt-0.5 h-4 w-4 shrink-0 text-muted-foreground" />
|
||||||
|
<div className="text-sm text-muted-foreground">No SSL data yet. It will appear after the next check.</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="flex items-start gap-3 rounded-lg border border-dashed p-4">
|
||||||
|
<Info className="mt-0.5 h-4 w-4 shrink-0 text-muted-foreground" />
|
||||||
|
<div className="text-sm text-muted-foreground">SSL not applicable for this monitor type.</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</InfoSection>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* DNS Records */}
|
||||||
|
{dnsRecords.length > 0 && (
|
||||||
|
<InfoSection title="DNS Records" icon={Server} accent="orange">
|
||||||
|
<div className="space-y-2">
|
||||||
|
{dnsRecords.map((rec, i) => (
|
||||||
|
<div key={i} className="flex items-center gap-3 rounded-lg border bg-background/60 px-3 py-2">
|
||||||
|
<Badge variant="outline" className="shrink-0 font-mono text-[10px]">
|
||||||
|
{rec.type}
|
||||||
|
</Badge>
|
||||||
|
<span className="text-[13px] text-foreground/90 truncate">{rec.value}</span>
|
||||||
|
{rec.ttl && <span className="ml-auto text-[10px] text-muted-foreground">TTL {rec.ttl}</span>}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</InfoSection>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* SEO & Meta */}
|
||||||
|
{showSeo && (
|
||||||
|
<InfoSection title="SEO & Social" icon={FileText} accent="slate">
|
||||||
|
<KVGrid>
|
||||||
|
{seo?.title && <KV label="Title" value={seo.title} />}
|
||||||
|
{seo?.description && <KV label="Description" value={seo.description} />}
|
||||||
|
{seo?.canonical && <KV label="Canonical" value={seo.canonical} />}
|
||||||
|
{seo?.robots && <KV label="Robots" value={seo.robots} />}
|
||||||
|
{seo?.generator && <KV label="Generator" value={seo.generator} />}
|
||||||
|
</KVGrid>
|
||||||
|
</InfoSection>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ import { Button } from "@/components/ui/button"
|
|||||||
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card"
|
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card"
|
||||||
import { Input } from "@/components/ui/input"
|
import { Input } from "@/components/ui/input"
|
||||||
import { Label } from "@/components/ui/label"
|
import { Label } from "@/components/ui/label"
|
||||||
|
import { Switch } from "@/components/ui/switch"
|
||||||
import {
|
import {
|
||||||
AlertDialog,
|
AlertDialog,
|
||||||
AlertDialogAction,
|
AlertDialogAction,
|
||||||
@@ -17,7 +18,6 @@ import {
|
|||||||
AlertDialogTitle,
|
AlertDialogTitle,
|
||||||
} from "@/components/ui/alert-dialog"
|
} from "@/components/ui/alert-dialog"
|
||||||
import { Badge } from "@/components/ui/badge"
|
import { Badge } from "@/components/ui/badge"
|
||||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
|
|
||||||
import {
|
import {
|
||||||
Globe,
|
Globe,
|
||||||
Clock,
|
Clock,
|
||||||
@@ -33,10 +33,17 @@ import {
|
|||||||
TrendingUp,
|
TrendingUp,
|
||||||
TrendingDown,
|
TrendingDown,
|
||||||
Plus,
|
Plus,
|
||||||
|
Zap,
|
||||||
|
Gauge,
|
||||||
|
Smartphone,
|
||||||
|
Lock,
|
||||||
|
Eye,
|
||||||
|
LayoutDashboard,
|
||||||
type LucideIcon,
|
type LucideIcon,
|
||||||
} from "lucide-react"
|
} from "lucide-react"
|
||||||
import {
|
import {
|
||||||
type Heartbeat,
|
type Heartbeat,
|
||||||
|
type Monitor,
|
||||||
getMonitor,
|
getMonitor,
|
||||||
getMonitorStats,
|
getMonitorStats,
|
||||||
getMonitorHeartbeats,
|
getMonitorHeartbeats,
|
||||||
@@ -45,8 +52,10 @@ import {
|
|||||||
resumeMonitor,
|
resumeMonitor,
|
||||||
deleteMonitor,
|
deleteMonitor,
|
||||||
getMonitorTypeLabel,
|
getMonitorTypeLabel,
|
||||||
|
getMonitorFaviconUrl,
|
||||||
formatUptime,
|
formatUptime,
|
||||||
formatPing,
|
formatPing,
|
||||||
|
runPageSpeedCheck,
|
||||||
} from "@/lib/monitors"
|
} from "@/lib/monitors"
|
||||||
import { formatDate } from "@/lib/domains"
|
import { formatDate } from "@/lib/domains"
|
||||||
import {
|
import {
|
||||||
@@ -57,23 +66,324 @@ import {
|
|||||||
getStatusPageUrl,
|
getStatusPageUrl,
|
||||||
removeMonitorFromStatusPage,
|
removeMonitorFromStatusPage,
|
||||||
} from "@/lib/statuspages"
|
} from "@/lib/statuspages"
|
||||||
import {
|
import { XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, Line, LineChart } from "recharts"
|
||||||
Bar,
|
|
||||||
XAxis,
|
|
||||||
YAxis,
|
|
||||||
CartesianGrid,
|
|
||||||
Tooltip,
|
|
||||||
ResponsiveContainer,
|
|
||||||
Area,
|
|
||||||
Cell,
|
|
||||||
ComposedChart,
|
|
||||||
Legend,
|
|
||||||
} from "recharts"
|
|
||||||
import { Link, navigate } from "@/components/router"
|
import { Link, navigate } from "@/components/router"
|
||||||
import { AddMonitorDialog } from "@/components/monitors-table/add-monitor-dialog"
|
import { AddMonitorDialog } from "@/components/monitors-table/add-monitor-dialog"
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
|
import { MonitorInfoSections } from "./monitor-info-sections"
|
||||||
|
|
||||||
type HeartbeatRow = Heartbeat & { timestamp?: string }
|
type HeartbeatRow = Heartbeat
|
||||||
|
|
||||||
|
// Uptime Bar Component - Visual timeline of recent checks
|
||||||
|
function UptimeBarVisualization({ heartbeats }: { heartbeats?: HeartbeatRow[] }) {
|
||||||
|
const recent = useMemo(() => {
|
||||||
|
if (!heartbeats?.length) return []
|
||||||
|
return heartbeats.slice(0, 30).reverse()
|
||||||
|
}, [heartbeats])
|
||||||
|
|
||||||
|
if (!recent.length) {
|
||||||
|
return (
|
||||||
|
<div className="flex gap-0.5 h-8 items-center">
|
||||||
|
{Array.from({ length: 12 }).map((_, i) => (
|
||||||
|
<div key={i} className="flex-1 h-6 rounded-sm bg-muted/50" />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div className="flex gap-0.5 h-8">
|
||||||
|
{recent.map((hb, i) => (
|
||||||
|
<div
|
||||||
|
key={i}
|
||||||
|
className={cn(
|
||||||
|
"flex-1 rounded-sm transition-all hover:opacity-80 cursor-pointer",
|
||||||
|
hb.status === "up"
|
||||||
|
? "bg-green-500"
|
||||||
|
: hb.status === "down"
|
||||||
|
? "bg-red-500"
|
||||||
|
: hb.status === "paused"
|
||||||
|
? "bg-gray-400"
|
||||||
|
: "bg-yellow-500"
|
||||||
|
)}
|
||||||
|
title={`${hb.status} • ${formatPing(hb.ping)} • ${formatDate(hb.time || "")}`}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between text-xs text-muted-foreground">
|
||||||
|
<span>{recent.length} recent checks</span>
|
||||||
|
<span>
|
||||||
|
<span className="inline-flex items-center gap-1">
|
||||||
|
<span className="w-2 h-2 rounded-full bg-green-500" />
|
||||||
|
{recent.filter((h) => h.status === "up").length} up
|
||||||
|
</span>
|
||||||
|
<span className="inline-flex items-center gap-1 ml-3">
|
||||||
|
<span className="w-2 h-2 rounded-full bg-red-500" />
|
||||||
|
{recent.filter((h) => h.status === "down").length} down
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Response time statistics component
|
||||||
|
function ResponseTimeStats({ heartbeats }: { heartbeats?: HeartbeatRow[] }) {
|
||||||
|
const stats = useMemo(() => {
|
||||||
|
if (!heartbeats?.length) return null
|
||||||
|
const pings = heartbeats.filter((h) => h.ping && h.ping > 0).map((h) => h.ping)
|
||||||
|
if (!pings.length) return null
|
||||||
|
|
||||||
|
const sorted = [...pings].sort((a, b) => a - b)
|
||||||
|
const avg = Math.round(pings.reduce((a, b) => a + b, 0) / pings.length)
|
||||||
|
const min = sorted[0]
|
||||||
|
const max = sorted[sorted.length - 1]
|
||||||
|
const p95 = sorted[Math.floor(sorted.length * 0.95)]
|
||||||
|
const p99 = sorted[Math.floor(sorted.length * 0.99)]
|
||||||
|
|
||||||
|
return { avg, min, max, p95, p99, count: pings.length }
|
||||||
|
}, [heartbeats])
|
||||||
|
|
||||||
|
if (!stats) return null
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="grid grid-cols-5 gap-2 text-center">
|
||||||
|
<div className="p-2 bg-muted/50 rounded-lg">
|
||||||
|
<div className="text-xs text-muted-foreground">Avg</div>
|
||||||
|
<div className="text-lg font-semibold">{formatPing(stats.avg)}</div>
|
||||||
|
</div>
|
||||||
|
<div className="p-2 bg-muted/50 rounded-lg">
|
||||||
|
<div className="text-xs text-muted-foreground">Min</div>
|
||||||
|
<div className="text-lg font-semibold text-green-600">{formatPing(stats.min)}</div>
|
||||||
|
</div>
|
||||||
|
<div className="p-2 bg-muted/50 rounded-lg">
|
||||||
|
<div className="text-xs text-muted-foreground">Max</div>
|
||||||
|
<div className="text-lg font-semibold text-red-600">{formatPing(stats.max)}</div>
|
||||||
|
</div>
|
||||||
|
<div className="p-2 bg-muted/50 rounded-lg">
|
||||||
|
<div className="text-xs text-muted-foreground">P95</div>
|
||||||
|
<div className="text-lg font-semibold">{formatPing(stats.p95)}</div>
|
||||||
|
</div>
|
||||||
|
<div className="p-2 bg-muted/50 rounded-lg">
|
||||||
|
<div className="text-xs text-muted-foreground">P99</div>
|
||||||
|
<div className="text-lg font-semibold">{formatPing(stats.p99)}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function getVitalColor(status: string): string {
|
||||||
|
switch (status) {
|
||||||
|
case "good":
|
||||||
|
return "text-green-500"
|
||||||
|
case "needs-improvement":
|
||||||
|
return "text-yellow-500"
|
||||||
|
default:
|
||||||
|
return "text-red-500"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getVitalBg(status: string): string {
|
||||||
|
switch (status) {
|
||||||
|
case "good":
|
||||||
|
return "bg-green-500/10 border-green-500/20"
|
||||||
|
case "needs-improvement":
|
||||||
|
return "bg-yellow-500/10 border-yellow-500/20"
|
||||||
|
default:
|
||||||
|
return "bg-red-500/10 border-red-500/20"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function ScoreRing({ score, label }: { score: number; label: string }) {
|
||||||
|
const color = score >= 90 ? "text-green-500" : score >= 70 ? "text-yellow-500" : "text-red-500"
|
||||||
|
const bg = score >= 90 ? "stroke-green-500" : score >= 70 ? "stroke-yellow-500" : "stroke-red-500"
|
||||||
|
const circumference = 2 * Math.PI * 18
|
||||||
|
const offset = circumference - (score / 100) * circumference
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col items-center gap-1">
|
||||||
|
<div className="relative w-12 h-12">
|
||||||
|
<svg
|
||||||
|
className="w-12 h-12 -rotate-90"
|
||||||
|
viewBox="0 0 44 44"
|
||||||
|
role="img"
|
||||||
|
aria-label={`Score ${Math.round(score)} for ${label}`}
|
||||||
|
>
|
||||||
|
<title>
|
||||||
|
Score {Math.round(score)} for {label}
|
||||||
|
</title>
|
||||||
|
<circle cx="22" cy="22" r="18" fill="none" stroke="currentColor" strokeWidth="4" className="text-muted/30" />
|
||||||
|
<circle
|
||||||
|
cx="22"
|
||||||
|
cy="22"
|
||||||
|
r="18"
|
||||||
|
fill="none"
|
||||||
|
strokeWidth="4"
|
||||||
|
strokeLinecap="round"
|
||||||
|
className={bg}
|
||||||
|
strokeDasharray={circumference}
|
||||||
|
strokeDashoffset={offset}
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
<span className={cn("absolute inset-0 flex items-center justify-center text-xs font-bold", color)}>
|
||||||
|
{Math.round(score)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<span className="text-[10px] text-muted-foreground uppercase tracking-wider">{label}</span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function VitalCard({ label, value, status, detail }: { label: string; value: string; status: string; detail: string }) {
|
||||||
|
return (
|
||||||
|
<div className={cn("p-3 rounded-lg border", getVitalBg(status))}>
|
||||||
|
<div className="flex items-center justify-between mb-1">
|
||||||
|
<span className="text-xs text-muted-foreground">{label}</span>
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"w-2 h-2 rounded-full",
|
||||||
|
status === "good" ? "bg-green-500" : status === "needs-improvement" ? "bg-yellow-500" : "bg-red-500"
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className={cn("text-lg font-bold", getVitalColor(status))}>{value}</div>
|
||||||
|
<div className="text-[10px] text-muted-foreground">{detail}</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatMs(ms: number): string {
|
||||||
|
if (ms < 1000) return `${Math.round(ms)}ms`
|
||||||
|
return `${(ms / 1000).toFixed(1)}s`
|
||||||
|
}
|
||||||
|
|
||||||
|
function CoreWebVitalsCard({ monitorId, url }: { monitorId: string; url?: string }) {
|
||||||
|
const [strategy, setStrategy] = useState<"mobile" | "desktop">("mobile")
|
||||||
|
const { toast } = useToast()
|
||||||
|
|
||||||
|
const {
|
||||||
|
data,
|
||||||
|
isPending: isPageSpeedLoading,
|
||||||
|
mutate,
|
||||||
|
} = useMutation({
|
||||||
|
mutationFn: () => runPageSpeedCheck(monitorId, strategy),
|
||||||
|
onSuccess: () => {
|
||||||
|
toast({ title: "Lighthouse check complete" })
|
||||||
|
},
|
||||||
|
onError: (err: Error) => {
|
||||||
|
toast({ title: "Check failed", description: err.message, variant: "destructive" })
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!url) return null
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card>
|
||||||
|
<CardHeader className="flex flex-col sm:flex-row sm:items-center justify-between gap-4">
|
||||||
|
<div>
|
||||||
|
<CardTitle className="flex items-center gap-2">
|
||||||
|
<Zap className="h-5 w-5 text-yellow-500" />
|
||||||
|
Core Web Vitals
|
||||||
|
</CardTitle>
|
||||||
|
<CardDescription>
|
||||||
|
{data
|
||||||
|
? `Checked ${new Date(data.checkedAt).toLocaleString()}`
|
||||||
|
: "Run a Lighthouse check to get performance metrics"}
|
||||||
|
</CardDescription>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<div className="flex rounded-lg border overflow-hidden">
|
||||||
|
<button
|
||||||
|
onClick={() => setStrategy("mobile")}
|
||||||
|
className={cn(
|
||||||
|
"px-3 py-1.5 text-xs font-medium transition-colors",
|
||||||
|
strategy === "mobile" ? "bg-primary text-primary-foreground" : "bg-muted hover:bg-muted/80"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<Smartphone className="h-3 w-3 inline mr-1" />
|
||||||
|
Mobile
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => setStrategy("desktop")}
|
||||||
|
className={cn(
|
||||||
|
"px-3 py-1.5 text-xs font-medium transition-colors",
|
||||||
|
strategy === "desktop" ? "bg-primary text-primary-foreground" : "bg-muted hover:bg-muted/80"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<Gauge className="h-3 w-3 inline mr-1" />
|
||||||
|
Desktop
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<Button size="sm" onClick={() => mutate()} disabled={isPageSpeedLoading}>
|
||||||
|
<RefreshCw className={cn("mr-2 h-4 w-4", isPageSpeedLoading && "animate-spin")} />
|
||||||
|
{isPageSpeedLoading ? "Running..." : "Run Check"}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
{data ? (
|
||||||
|
<div className="space-y-4">
|
||||||
|
{/* Lighthouse Scores */}
|
||||||
|
<div className="flex items-center gap-4 justify-center sm:justify-start">
|
||||||
|
<ScoreRing score={data.performance} label="Perf" />
|
||||||
|
<ScoreRing score={data.accessibility} label="A11y" />
|
||||||
|
<ScoreRing score={data.bestPractices} label="BP" />
|
||||||
|
<ScoreRing score={data.seo} label="SEO" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Core Web Vitals */}
|
||||||
|
<div className="grid grid-cols-2 sm:grid-cols-3 gap-2">
|
||||||
|
<VitalCard
|
||||||
|
label="LCP"
|
||||||
|
value={formatMs(data.lcp)}
|
||||||
|
status={data.vitals.lcp || "poor"}
|
||||||
|
detail="Largest Contentful Paint"
|
||||||
|
/>
|
||||||
|
<VitalCard
|
||||||
|
label="FID"
|
||||||
|
value={formatMs(data.tbt)}
|
||||||
|
status={data.vitals.fid || "poor"}
|
||||||
|
detail="Total Blocking Time (proxy)"
|
||||||
|
/>
|
||||||
|
<VitalCard
|
||||||
|
label="CLS"
|
||||||
|
value={data.cls.toFixed(3)}
|
||||||
|
status={data.vitals.cls || "poor"}
|
||||||
|
detail="Cumulative Layout Shift"
|
||||||
|
/>
|
||||||
|
<VitalCard
|
||||||
|
label="FCP"
|
||||||
|
value={formatMs(data.fcp)}
|
||||||
|
status={data.vitals.fcp || "poor"}
|
||||||
|
detail="First Contentful Paint"
|
||||||
|
/>
|
||||||
|
<VitalCard
|
||||||
|
label="TTFB"
|
||||||
|
value={formatMs(data.ttfb)}
|
||||||
|
status={data.vitals.ttfb || "poor"}
|
||||||
|
detail="Time to First Byte"
|
||||||
|
/>
|
||||||
|
<VitalCard
|
||||||
|
label="TTI"
|
||||||
|
value={formatMs(data.tti)}
|
||||||
|
status={data.vitals.tti || "poor"}
|
||||||
|
detail="Time to Interactive"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="flex flex-col items-center justify-center py-8 gap-3 text-muted-foreground">
|
||||||
|
<div className="p-3 bg-muted/50 rounded-full">
|
||||||
|
<Gauge className="h-6 w-6 opacity-50" />
|
||||||
|
</div>
|
||||||
|
<p className="text-sm">No Lighthouse data yet. Click "Run Check" to analyze performance.</p>
|
||||||
|
<p className="text-xs text-muted-foreground">Powered by Google PageSpeed Insights</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// Status badge component
|
// Status badge component
|
||||||
function StatusBadge({ status }: { status: string }) {
|
function StatusBadge({ status }: { status: string }) {
|
||||||
@@ -136,6 +446,15 @@ function StatCard({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function MonitorFaviconImage({ monitor, iconColor }: { monitor: Monitor; iconColor: string }) {
|
||||||
|
const [error, setError] = useState(false)
|
||||||
|
const faviconUrl = getMonitorFaviconUrl(monitor)
|
||||||
|
if (!faviconUrl || error) {
|
||||||
|
return <Globe className={cn("h-6 w-6", iconColor)} />
|
||||||
|
}
|
||||||
|
return <img src={faviconUrl} alt="" className="h-6 w-6 object-contain" onError={() => setError(true)} />
|
||||||
|
}
|
||||||
|
|
||||||
export default memo(function MonitorDetail({ id }: { id: string }) {
|
export default memo(function MonitorDetail({ id }: { id: string }) {
|
||||||
const { toast } = useToast()
|
const { toast } = useToast()
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
@@ -197,6 +516,7 @@ export default memo(function MonitorDetail({ id }: { id: string }) {
|
|||||||
const [isCreateStatusPageOpen, setIsCreateStatusPageOpen] = useState(false)
|
const [isCreateStatusPageOpen, setIsCreateStatusPageOpen] = useState(false)
|
||||||
const [statusPageName, setStatusPageName] = useState("")
|
const [statusPageName, setStatusPageName] = useState("")
|
||||||
const [statusPageSlug, setStatusPageSlug] = useState("")
|
const [statusPageSlug, setStatusPageSlug] = useState("")
|
||||||
|
const [statusPagePublic, setStatusPagePublic] = useState(true)
|
||||||
|
|
||||||
const { data: statusPages } = useQuery({
|
const { data: statusPages } = useQuery({
|
||||||
queryKey: ["status-pages"],
|
queryKey: ["status-pages"],
|
||||||
@@ -233,19 +553,29 @@ export default memo(function MonitorDetail({ id }: { id: string }) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const createStatusPageMutation = useMutation({
|
const createStatusPageMutation = useMutation({
|
||||||
mutationFn: () =>
|
mutationFn: async () => {
|
||||||
createStatusPage({
|
const page = await createStatusPage({
|
||||||
name: statusPageName || `${monitor?.name} Status`,
|
name: statusPageName || `${monitor?.name} Status`,
|
||||||
slug: statusPageSlug || monitor?.name?.toLowerCase().replace(/\s+/g, "-") || "status",
|
slug: statusPageSlug || monitor?.name?.toLowerCase().replace(/\s+/g, "-") || "status",
|
||||||
title: statusPageName || `${monitor?.name} Status Page`,
|
title: statusPageName || `${monitor?.name} Status Page`,
|
||||||
public: true,
|
public: statusPagePublic,
|
||||||
}),
|
})
|
||||||
onSuccess: () => {
|
// Auto-link this monitor to the newly created status page
|
||||||
|
await addMonitorToStatusPage(page.id, { monitor: id })
|
||||||
|
return page
|
||||||
|
},
|
||||||
|
onSuccess: (page) => {
|
||||||
queryClient.invalidateQueries({ queryKey: ["status-pages"] })
|
queryClient.invalidateQueries({ queryKey: ["status-pages"] })
|
||||||
toast({ title: "Status page created" })
|
queryClient.invalidateQueries({ queryKey: ["monitor-status-page-links", id] })
|
||||||
|
toast({ title: "Status page created and monitor linked" })
|
||||||
setIsCreateStatusPageOpen(false)
|
setIsCreateStatusPageOpen(false)
|
||||||
setStatusPageName("")
|
setStatusPageName("")
|
||||||
setStatusPageSlug("")
|
setStatusPageSlug("")
|
||||||
|
setStatusPagePublic(true)
|
||||||
|
// Open private pages in new tab since user is authenticated
|
||||||
|
if (!page.public) {
|
||||||
|
window.open(getStatusPageUrl(page.slug), "_blank", "noopener,noreferrer")
|
||||||
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -264,7 +594,7 @@ export default memo(function MonitorDetail({ id }: { id: string }) {
|
|||||||
}
|
}
|
||||||
const cutoff = now - (ranges[timeRange] || ranges["24h"])
|
const cutoff = now - (ranges[timeRange] || ranges["24h"])
|
||||||
return heartbeats.filter((h: HeartbeatRow) => {
|
return heartbeats.filter((h: HeartbeatRow) => {
|
||||||
const t = new Date(h.time || h.timestamp || "").getTime()
|
const t = new Date(h.time || "").getTime()
|
||||||
return t >= cutoff
|
return t >= cutoff
|
||||||
})
|
})
|
||||||
}, [heartbeats, timeRange])
|
}, [heartbeats, timeRange])
|
||||||
@@ -276,7 +606,7 @@ export default memo(function MonitorDetail({ id }: { id: string }) {
|
|||||||
.slice()
|
.slice()
|
||||||
.reverse()
|
.reverse()
|
||||||
.map((h: HeartbeatRow) => ({
|
.map((h: HeartbeatRow) => ({
|
||||||
time: new Date(h.time || h.timestamp || "").toLocaleTimeString(),
|
time: new Date(h.time || "").toLocaleTimeString(),
|
||||||
responseTime: h.ping || 0,
|
responseTime: h.ping || 0,
|
||||||
status: h.status === "up" ? 1 : 0,
|
status: h.status === "up" ? 1 : 0,
|
||||||
}))
|
}))
|
||||||
@@ -320,8 +650,20 @@ export default memo(function MonitorDetail({ id }: { id: string }) {
|
|||||||
const isPaused = monitor.status === "paused"
|
const isPaused = monitor.status === "paused"
|
||||||
const isPending = monitor.status === "pending"
|
const isPending = monitor.status === "pending"
|
||||||
|
|
||||||
const headerIconColor = isUp ? "text-green-500" : isPaused ? "text-gray-500" : isPending ? "text-yellow-500" : "text-red-500"
|
const headerIconColor = isUp
|
||||||
const headerBgColor = isUp ? "bg-green-500/10" : isPaused ? "bg-gray-500/10" : isPending ? "bg-yellow-500/10" : "bg-red-500/10"
|
? "text-green-500"
|
||||||
|
: isPaused
|
||||||
|
? "text-gray-500"
|
||||||
|
: isPending
|
||||||
|
? "text-yellow-500"
|
||||||
|
: "text-red-500"
|
||||||
|
const headerBgColor = isUp
|
||||||
|
? "bg-green-500/10"
|
||||||
|
: isPaused
|
||||||
|
? "bg-gray-500/10"
|
||||||
|
: isPending
|
||||||
|
? "bg-yellow-500/10"
|
||||||
|
: "bg-red-500/10"
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="grid gap-4 mb-14">
|
<div className="grid gap-4 mb-14">
|
||||||
@@ -330,13 +672,8 @@ export default memo(function MonitorDetail({ id }: { id: string }) {
|
|||||||
<CardContent className="p-6">
|
<CardContent className="p-6">
|
||||||
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4">
|
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4">
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-4">
|
||||||
<div
|
<div className={cn("h-12 w-12 rounded-full flex items-center justify-center", headerBgColor)}>
|
||||||
className={cn(
|
<MonitorFaviconImage monitor={monitor} iconColor={headerIconColor} />
|
||||||
"h-12 w-12 rounded-full flex items-center justify-center",
|
|
||||||
headerBgColor
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<Globe className={cn("h-6 w-6", headerIconColor)} />
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-2xl font-bold">{monitor.name}</h1>
|
<h1 className="text-2xl font-bold">{monitor.name}</h1>
|
||||||
@@ -421,13 +758,41 @@ export default memo(function MonitorDetail({ id }: { id: string }) {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Combined Uptime & Response Chart */}
|
{/* Uptime Bar & Response Stats */}
|
||||||
|
<div className="grid sm:grid-cols-2 gap-4">
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>Recent Uptime</CardTitle>
|
||||||
|
<CardDescription>Visual timeline of the last 30 checks</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<UptimeBarVisualization heartbeats={heartbeats} />
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>Response Time Statistics</CardTitle>
|
||||||
|
<CardDescription>Distribution of response times</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<ResponseTimeStats heartbeats={heartbeats} />
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Core Web Vitals */}
|
||||||
|
<CoreWebVitalsCard monitorId={id} url={monitor.url} />
|
||||||
|
|
||||||
|
{/* Domain Info Sections */}
|
||||||
|
<MonitorInfoSections monitor={monitor} heartbeats={heartbeats} />
|
||||||
|
|
||||||
|
{/* Response Time Chart */}
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader className="flex flex-col sm:flex-row sm:items-center justify-between gap-4">
|
<CardHeader className="flex flex-col sm:flex-row sm:items-center justify-between gap-4">
|
||||||
<div>
|
<div>
|
||||||
<CardTitle>Uptime & Response Time</CardTitle>
|
<CardTitle>Response Time</CardTitle>
|
||||||
<CardDescription>
|
<CardDescription>
|
||||||
<Trans>Status and response time over the selected period</Trans>
|
<Trans>Response time over the selected period</Trans>
|
||||||
</CardDescription>
|
</CardDescription>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
@@ -447,45 +812,36 @@ export default memo(function MonitorDetail({ id }: { id: string }) {
|
|||||||
<div className="h-[300px]">
|
<div className="h-[300px]">
|
||||||
{chartData.length > 0 ? (
|
{chartData.length > 0 ? (
|
||||||
<ResponsiveContainer width="100%" height="100%">
|
<ResponsiveContainer width="100%" height="100%">
|
||||||
<ComposedChart data={chartData}>
|
<LineChart data={chartData}>
|
||||||
<defs>
|
<defs>
|
||||||
<linearGradient id="colorResponse" x1="0" y1="0" x2="0" y2="1">
|
<linearGradient id="colorResponse" x1="0" y1="0" x2="0" y2="1">
|
||||||
<stop offset="5%" stopColor="#3b82f6" stopOpacity={0.3} />
|
<stop offset="5%" stopColor="hsl(var(--chart-1))" stopOpacity={0.3} />
|
||||||
<stop offset="95%" stopColor="#3b82f6" stopOpacity={0} />
|
<stop offset="95%" stopColor="hsl(var(--chart-1))" stopOpacity={0} />
|
||||||
</linearGradient>
|
</linearGradient>
|
||||||
</defs>
|
</defs>
|
||||||
<CartesianGrid strokeDasharray="3 3" opacity={0.3} />
|
<CartesianGrid vertical={false} strokeDasharray="3 3" opacity={0.3} />
|
||||||
<XAxis dataKey="time" tick={{ fontSize: 12 }} />
|
<XAxis dataKey="time" tick={{ fontSize: 11 }} tickLine={false} axisLine={false} />
|
||||||
<YAxis yAxisId="left" tick={{ fontSize: 12 }} unit="ms" />
|
<YAxis tick={{ fontSize: 11 }} tickLine={false} axisLine={false} unit="ms" />
|
||||||
<YAxis
|
|
||||||
yAxisId="right"
|
|
||||||
orientation="right"
|
|
||||||
tick={{ fontSize: 12 }}
|
|
||||||
domain={[0, 1]}
|
|
||||||
tickFormatter={(v) => (v === 1 ? "Up" : "Down")}
|
|
||||||
/>
|
|
||||||
<Tooltip
|
<Tooltip
|
||||||
contentStyle={{
|
contentStyle={{
|
||||||
backgroundColor: "hsl(var(--card))",
|
backgroundColor: "hsl(var(--card))",
|
||||||
border: "1px solid hsl(var(--border))",
|
border: "1px solid hsl(var(--border))",
|
||||||
|
borderRadius: "8px",
|
||||||
}}
|
}}
|
||||||
|
formatter={(value: number) => [`${value}ms`, "Response Time"]}
|
||||||
/>
|
/>
|
||||||
<Legend />
|
<Line
|
||||||
<Area
|
|
||||||
yAxisId="left"
|
|
||||||
type="monotone"
|
type="monotone"
|
||||||
dataKey="responseTime"
|
dataKey="responseTime"
|
||||||
stroke="#3b82f6"
|
stroke="hsl(var(--chart-1))"
|
||||||
|
strokeWidth={1.5}
|
||||||
fillOpacity={1}
|
fillOpacity={1}
|
||||||
fill="url(#colorResponse)"
|
fill="url(#colorResponse)"
|
||||||
name="Response Time (ms)"
|
name="Response Time"
|
||||||
|
isAnimationActive={false}
|
||||||
|
dot={false}
|
||||||
/>
|
/>
|
||||||
<Bar yAxisId="right" dataKey="status" barSize={4} name="Status">
|
</LineChart>
|
||||||
{chartData.map((entry, index) => (
|
|
||||||
<Cell key={`cell-${index}`} fill={entry.status === 1 ? "#22c55e" : "#ef4444"} />
|
|
||||||
))}
|
|
||||||
</Bar>
|
|
||||||
</ComposedChart>
|
|
||||||
</ResponsiveContainer>
|
</ResponsiveContainer>
|
||||||
) : (
|
) : (
|
||||||
<div className="h-full flex flex-col items-center justify-center gap-3 text-muted-foreground">
|
<div className="h-full flex flex-col items-center justify-center gap-3 text-muted-foreground">
|
||||||
@@ -547,12 +903,20 @@ export default memo(function MonitorDetail({ id }: { id: string }) {
|
|||||||
|
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div>
|
||||||
<CardTitle>Status Page</CardTitle>
|
<CardTitle>Status Page</CardTitle>
|
||||||
<CardDescription>Link this monitor to public status pages</CardDescription>
|
<CardDescription>Create or link to status pages</CardDescription>
|
||||||
|
</div>
|
||||||
|
<Button variant="outline" size="sm" onClick={() => setIsCreateStatusPageOpen(true)}>
|
||||||
|
<Plus className="mr-2 h-4 w-4" />
|
||||||
|
Create New
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="space-y-3">
|
<CardContent className="space-y-3">
|
||||||
{statusPages && statusPages.length > 0 ? (
|
{statusPages && statusPages.length > 0 ? (
|
||||||
<div className="space-y-3">
|
<div className="space-y-2">
|
||||||
{statusPages.map((page) => {
|
{statusPages.map((page) => {
|
||||||
const isLinked = linkedStatusPageMonitors?.some((link) => link.status_page_id === page.id) || false
|
const isLinked = linkedStatusPageMonitors?.some((link) => link.status_page_id === page.id) || false
|
||||||
const linkInfo = linkedStatusPageMonitors?.find((link) => link.status_page_id === page.id)
|
const linkInfo = linkedStatusPageMonitors?.find((link) => link.status_page_id === page.id)
|
||||||
@@ -560,15 +924,25 @@ export default memo(function MonitorDetail({ id }: { id: string }) {
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={page.id}
|
key={page.id}
|
||||||
className={`flex items-center justify-between p-3 rounded-lg border ${
|
className={cn(
|
||||||
isLinked ? 'bg-primary/5 border-primary/20' : 'bg-muted/30'
|
"flex items-center justify-between p-3 rounded-lg border transition-colors",
|
||||||
}`}
|
isLinked ? "bg-primary/5 border-primary/20" : "bg-muted/30 border-border"
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
<div className="min-w-0 flex-1">
|
<div className="min-w-0 flex-1">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
|
<LayoutDashboard className="h-3.5 w-3.5 text-muted-foreground" />
|
||||||
<span className="font-medium text-sm truncate">{page.name}</span>
|
<span className="font-medium text-sm truncate">{page.name}</span>
|
||||||
{page.public && (
|
{page.public ? (
|
||||||
<Globe className="h-3 w-3 text-muted-foreground flex-shrink-0" />
|
<Badge variant="outline" className="text-[10px] gap-1">
|
||||||
|
<Eye className="h-2.5 w-2.5" />
|
||||||
|
Public
|
||||||
|
</Badge>
|
||||||
|
) : (
|
||||||
|
<Badge variant="outline" className="text-[10px] gap-1">
|
||||||
|
<Lock className="h-2.5 w-2.5" />
|
||||||
|
Private
|
||||||
|
</Badge>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{isLinked && linkInfo && (
|
{isLinked && linkInfo && (
|
||||||
@@ -577,30 +951,24 @@ export default memo(function MonitorDetail({ id }: { id: string }) {
|
|||||||
{linkInfo.group && ` • Group: ${linkInfo.group}`}
|
{linkInfo.group && ` • Group: ${linkInfo.group}`}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
{!isLinked && page.public && (
|
{!isLinked && (
|
||||||
<p className="text-xs text-muted-foreground mt-1">
|
<p className="text-xs text-muted-foreground mt-1">
|
||||||
{page.monitor_count} monitor{page.monitor_count !== 1 ? 's' : ''} linked
|
{page.monitor_count} monitor{page.monitor_count !== 1 ? "s" : ""} linked
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2 ml-2">
|
<div className="flex items-center gap-2 ml-2">
|
||||||
{isLinked && page.public && (
|
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="icon"
|
size="icon"
|
||||||
className="h-8 w-8"
|
className="h-8 w-8"
|
||||||
asChild
|
asChild
|
||||||
|
title={page.public ? "View public status page" : "View private status page"}
|
||||||
>
|
>
|
||||||
<a
|
<a href={getStatusPageUrl(page.slug)} target="_blank" rel="noopener noreferrer">
|
||||||
href={getStatusPageUrl(page.slug)}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
title="View public status page"
|
|
||||||
>
|
|
||||||
<ExternalLink className="h-4 w-4" />
|
<ExternalLink className="h-4 w-4" />
|
||||||
</a>
|
</a>
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
|
||||||
<Button
|
<Button
|
||||||
variant={isLinked ? "default" : "outline"}
|
variant={isLinked ? "default" : "outline"}
|
||||||
size="sm"
|
size="sm"
|
||||||
@@ -629,74 +997,14 @@ export default memo(function MonitorDetail({ id }: { id: string }) {
|
|||||||
) : (
|
) : (
|
||||||
<div className="text-center py-4">
|
<div className="text-center py-4">
|
||||||
<p className="text-sm text-muted-foreground">No status pages yet.</p>
|
<p className="text-sm text-muted-foreground">No status pages yet.</p>
|
||||||
<p className="text-xs text-muted-foreground mt-1">Create one to share your service status publicly.</p>
|
<p className="text-xs text-muted-foreground mt-1">
|
||||||
</div>
|
Create one to share your service status or keep it private for internal use.
|
||||||
)}
|
|
||||||
<Button variant="outline" size="sm" className="w-full" onClick={() => setIsCreateStatusPageOpen(true)}>
|
|
||||||
<Plus className="mr-2 h-4 w-4" />
|
|
||||||
Create Status Page
|
|
||||||
</Button>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Card>
|
|
||||||
<CardHeader>
|
|
||||||
<CardTitle>Recent Checks</CardTitle>
|
|
||||||
<CardDescription>Last 50 monitor checks</CardDescription>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
<Table>
|
|
||||||
<TableHeader>
|
|
||||||
<TableRow>
|
|
||||||
<TableHead>Time</TableHead>
|
|
||||||
<TableHead>Status</TableHead>
|
|
||||||
<TableHead>Response Time</TableHead>
|
|
||||||
<TableHead>Message</TableHead>
|
|
||||||
</TableRow>
|
|
||||||
</TableHeader>
|
|
||||||
<TableBody>
|
|
||||||
{heartbeats?.slice(0, 50).map((hb: HeartbeatRow) => (
|
|
||||||
<TableRow key={hb.id}>
|
|
||||||
<TableCell>{formatDate(hb.time || hb.timestamp)}</TableCell>
|
|
||||||
<TableCell>
|
|
||||||
<Badge variant={hb.status === "up" ? "default" : "destructive"}>{hb.status}</Badge>
|
|
||||||
</TableCell>
|
|
||||||
<TableCell>{formatPing(hb.ping)}</TableCell>
|
|
||||||
<TableCell className="max-w-xs truncate">{hb.msg || "-"}</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
))}
|
|
||||||
{!heartbeats?.length && (
|
|
||||||
<TableRow>
|
|
||||||
<TableCell colSpan={4}>
|
|
||||||
<div className="flex flex-col items-center justify-center py-8 gap-3 text-muted-foreground">
|
|
||||||
<div className="p-2 bg-muted/50 rounded-full">
|
|
||||||
<Clock className="h-5 w-5 opacity-50" />
|
|
||||||
</div>
|
|
||||||
<p className="text-sm">
|
|
||||||
{isPending
|
|
||||||
? "No checks have been run yet."
|
|
||||||
: "No check history available for the selected period."}
|
|
||||||
</p>
|
</p>
|
||||||
{isPending && (
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
size="sm"
|
|
||||||
onClick={() => checkMutation.mutate()}
|
|
||||||
disabled={checkMutation.isPending}
|
|
||||||
>
|
|
||||||
<RefreshCw className={cn("mr-2 h-4 w-4", checkMutation.isPending && "animate-spin")} />
|
|
||||||
Run First Check
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
)}
|
)}
|
||||||
</TableBody>
|
|
||||||
</Table>
|
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Create Status Page Dialog */}
|
{/* Create Status Page Dialog */}
|
||||||
{isCreateStatusPageOpen && (
|
{isCreateStatusPageOpen && (
|
||||||
@@ -704,7 +1012,7 @@ export default memo(function MonitorDetail({ id }: { id: string }) {
|
|||||||
<AlertDialogContent>
|
<AlertDialogContent>
|
||||||
<AlertDialogHeader>
|
<AlertDialogHeader>
|
||||||
<AlertDialogTitle>Create Status Page</AlertDialogTitle>
|
<AlertDialogTitle>Create Status Page</AlertDialogTitle>
|
||||||
<AlertDialogDescription>Create a public status page for this monitor.</AlertDialogDescription>
|
<AlertDialogDescription>Create a status page and optionally link this monitor.</AlertDialogDescription>
|
||||||
</AlertDialogHeader>
|
</AlertDialogHeader>
|
||||||
<div className="grid gap-4 py-4">
|
<div className="grid gap-4 py-4">
|
||||||
<div className="grid gap-2">
|
<div className="grid gap-2">
|
||||||
@@ -725,6 +1033,15 @@ export default memo(function MonitorDetail({ id }: { id: string }) {
|
|||||||
placeholder={monitor.name?.toLowerCase().replace(/\s+/g, "-")}
|
placeholder={monitor.name?.toLowerCase().replace(/\s+/g, "-")}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex items-center justify-between rounded-lg border p-3">
|
||||||
|
<div className="space-y-0.5">
|
||||||
|
<Label htmlFor="sp-public" className="text-sm font-medium">
|
||||||
|
Public Status Page
|
||||||
|
</Label>
|
||||||
|
<p className="text-xs text-muted-foreground">Anyone can view this page without authentication.</p>
|
||||||
|
</div>
|
||||||
|
<Switch id="sp-public" checked={statusPagePublic} onCheckedChange={setStatusPagePublic} />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<AlertDialogFooter>
|
<AlertDialogFooter>
|
||||||
<AlertDialogCancel onClick={() => setIsCreateStatusPageOpen(false)}>Cancel</AlertDialogCancel>
|
<AlertDialogCancel onClick={() => setIsCreateStatusPageOpen(false)}>Cancel</AlertDialogCancel>
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
import { memo, useEffect } from "react"
|
import { memo, useEffect } from "react"
|
||||||
import { useLingui } from "@lingui/react/macro"
|
import { useLingui } from "@lingui/react/macro"
|
||||||
import { StatusPagesTable } from "@/components/status-pages/status-pages-table"
|
import { StatusPageManager } from "@/components/status-pages/status-page-manager"
|
||||||
|
|
||||||
export default memo(() => {
|
export default memo(() => {
|
||||||
const { t } = useLingui()
|
const { t } = useLingui()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.title = `${t`Status Pages`} / Beszel`
|
document.title = `${t`Status Page Manager`} / Beszel`
|
||||||
}, [t])
|
}, [t])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-8">
|
<div className="container mx-auto py-6">
|
||||||
<StatusPagesTable />
|
<StatusPageManager />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -0,0 +1,853 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import { useState, useMemo } from "react"
|
||||||
|
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"
|
||||||
|
import { useToast } from "@/components/ui/use-toast"
|
||||||
|
import { Button } from "@/components/ui/button"
|
||||||
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
|
||||||
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
|
||||||
|
import { Badge } from "@/components/ui/badge"
|
||||||
|
import { Input } from "@/components/ui/input"
|
||||||
|
import {
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableHead,
|
||||||
|
TableHeader,
|
||||||
|
TableRow,
|
||||||
|
} from "@/components/ui/table"
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogDescription,
|
||||||
|
DialogFooter,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
} from "@/components/ui/dialog"
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from "@/components/ui/select"
|
||||||
|
import { Textarea } from "@/components/ui/textarea"
|
||||||
|
import {
|
||||||
|
Plus,
|
||||||
|
ExternalLink,
|
||||||
|
Globe,
|
||||||
|
Lock,
|
||||||
|
AlertTriangle,
|
||||||
|
CheckCircle2,
|
||||||
|
Clock,
|
||||||
|
XCircle,
|
||||||
|
LayoutTemplate,
|
||||||
|
Activity,
|
||||||
|
TrendingUp,
|
||||||
|
Filter,
|
||||||
|
Search,
|
||||||
|
Wrench,
|
||||||
|
Trash2,
|
||||||
|
} from "lucide-react"
|
||||||
|
import {
|
||||||
|
getStatusPages,
|
||||||
|
deleteStatusPage,
|
||||||
|
getStatusPageUrl,
|
||||||
|
type StatusPage,
|
||||||
|
} from "@/lib/statuspages"
|
||||||
|
import {
|
||||||
|
getIncidents,
|
||||||
|
createIncident,
|
||||||
|
acknowledgeIncident,
|
||||||
|
resolveIncident,
|
||||||
|
closeIncident,
|
||||||
|
getIncidentStats,
|
||||||
|
type Incident,
|
||||||
|
type CreateIncidentRequest,
|
||||||
|
getSeverityColor,
|
||||||
|
getStatusColor,
|
||||||
|
formatDuration,
|
||||||
|
} from "@/lib/incidents"
|
||||||
|
import { StatusPageDialog } from "./status-page-dialog"
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
// Quick Stats Card Component
|
||||||
|
function QuickStatCard({
|
||||||
|
title,
|
||||||
|
value,
|
||||||
|
subtitle,
|
||||||
|
icon: Icon,
|
||||||
|
trend,
|
||||||
|
color = "blue",
|
||||||
|
}: {
|
||||||
|
title: string
|
||||||
|
value: string | number
|
||||||
|
subtitle?: string
|
||||||
|
icon: React.ElementType
|
||||||
|
trend?: { value: number; positive: boolean }
|
||||||
|
color?: "blue" | "green" | "yellow" | "red" | "purple"
|
||||||
|
}) {
|
||||||
|
const colorClasses = {
|
||||||
|
blue: "bg-blue-500/10 text-blue-600 border-blue-500/20",
|
||||||
|
green: "bg-green-500/10 text-green-600 border-green-500/20",
|
||||||
|
yellow: "bg-yellow-500/10 text-yellow-600 border-yellow-500/20",
|
||||||
|
red: "bg-red-500/10 text-red-600 border-red-500/20",
|
||||||
|
purple: "bg-purple-500/10 text-purple-600 border-purple-500/20",
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card className="relative overflow-hidden">
|
||||||
|
<CardHeader className="flex flex-row items-center justify-between pb-2">
|
||||||
|
<CardTitle className="text-sm font-medium text-muted-foreground">
|
||||||
|
{title}
|
||||||
|
</CardTitle>
|
||||||
|
<div className={cn("p-2 rounded-lg border", colorClasses[color])}>
|
||||||
|
<Icon className="h-4 w-4" />
|
||||||
|
</div>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<div className="text-2xl font-bold">{value}</div>
|
||||||
|
{subtitle && (
|
||||||
|
<p className="text-xs text-muted-foreground mt-1">{subtitle}</p>
|
||||||
|
)}
|
||||||
|
{trend && (
|
||||||
|
<div className={cn(
|
||||||
|
"flex items-center gap-1 text-xs mt-2",
|
||||||
|
trend.positive ? "text-green-600" : "text-red-600"
|
||||||
|
)}>
|
||||||
|
<TrendingUp className={cn("h-3 w-3", !trend.positive && "rotate-180")} />
|
||||||
|
<span>{trend.value}%</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Incident Quick Actions Menu
|
||||||
|
function IncidentQuickActions({
|
||||||
|
incident,
|
||||||
|
onAcknowledge,
|
||||||
|
onResolve,
|
||||||
|
onClose,
|
||||||
|
}: {
|
||||||
|
incident: Incident
|
||||||
|
onAcknowledge: (id: string) => void
|
||||||
|
onResolve: (id: string) => void
|
||||||
|
onClose: (id: string) => void
|
||||||
|
}) {
|
||||||
|
const [showResolveDialog, setShowResolveDialog] = useState(false)
|
||||||
|
const [resolution, setResolution] = useState("")
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
{incident.status === "open" && (
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
className="h-8 text-yellow-600 border-yellow-200 hover:bg-yellow-50"
|
||||||
|
onClick={() => onAcknowledge(incident.id)}
|
||||||
|
>
|
||||||
|
<Clock className="mr-1 h-3.5 w-3.5" />
|
||||||
|
Ack
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
{(incident.status === "open" || incident.status === "acknowledged") && (
|
||||||
|
<>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
className="h-8 text-green-600 border-green-200 hover:bg-green-50"
|
||||||
|
onClick={() => setShowResolveDialog(true)}
|
||||||
|
>
|
||||||
|
<CheckCircle2 className="mr-1 h-3.5 w-3.5" />
|
||||||
|
Resolve
|
||||||
|
</Button>
|
||||||
|
<Dialog open={showResolveDialog} onOpenChange={setShowResolveDialog}>
|
||||||
|
<DialogContent className="sm:max-w-[425px]">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>Resolve Incident</DialogTitle>
|
||||||
|
<DialogDescription>
|
||||||
|
Add resolution details for this incident.
|
||||||
|
</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
<div className="grid gap-4 py-4">
|
||||||
|
<div className="space-y-2">
|
||||||
|
<label className="text-sm font-medium">Resolution</label>
|
||||||
|
<Textarea
|
||||||
|
placeholder="How was this incident resolved?"
|
||||||
|
value={resolution}
|
||||||
|
onChange={(e) => setResolution(e.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<DialogFooter>
|
||||||
|
<Button variant="outline" onClick={() => setShowResolveDialog(false)}>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
onResolve(incident.id)
|
||||||
|
setShowResolveDialog(false)
|
||||||
|
setResolution("")
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Resolve Incident
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{incident.status === "resolved" && (
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
className="h-8 text-gray-600 border-gray-200 hover:bg-gray-50"
|
||||||
|
onClick={() => onClose(incident.id)}
|
||||||
|
>
|
||||||
|
<XCircle className="mr-1 h-3.5 w-3.5" />
|
||||||
|
Close
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create Incident Dialog
|
||||||
|
function CreateIncidentDialog({
|
||||||
|
open,
|
||||||
|
onOpenChange,
|
||||||
|
onCreate,
|
||||||
|
}: {
|
||||||
|
open: boolean
|
||||||
|
onOpenChange: (open: boolean) => void
|
||||||
|
onCreate: (data: CreateIncidentRequest) => void
|
||||||
|
}) {
|
||||||
|
const [title, setTitle] = useState("")
|
||||||
|
const [description, setDescription] = useState("")
|
||||||
|
const [severity, setSeverity] = useState<"critical" | "high" | "medium" | "low">("high")
|
||||||
|
const [type, setType] = useState("monitor_down")
|
||||||
|
|
||||||
|
const handleSubmit = () => {
|
||||||
|
onCreate({
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
severity,
|
||||||
|
type,
|
||||||
|
})
|
||||||
|
onOpenChange(false)
|
||||||
|
setTitle("")
|
||||||
|
setDescription("")
|
||||||
|
setSeverity("high")
|
||||||
|
setType("monitor_down")
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||||
|
<DialogContent className="sm:max-w-[500px]">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>Create New Incident</DialogTitle>
|
||||||
|
<DialogDescription>
|
||||||
|
Report a new incident or maintenance event.
|
||||||
|
</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
<div className="grid gap-4 py-4">
|
||||||
|
<div className="space-y-2">
|
||||||
|
<label className="text-sm font-medium">Title</label>
|
||||||
|
<Input
|
||||||
|
placeholder="Incident title"
|
||||||
|
value={title}
|
||||||
|
onChange={(e) => setTitle(e.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<label className="text-sm font-medium">Description</label>
|
||||||
|
<Textarea
|
||||||
|
placeholder="Describe the incident..."
|
||||||
|
value={description}
|
||||||
|
onChange={(e) => setDescription(e.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-2 gap-4">
|
||||||
|
<div className="space-y-2">
|
||||||
|
<label className="text-sm font-medium">Severity</label>
|
||||||
|
<Select value={severity} onValueChange={(v) => setSeverity(v as typeof severity)}>
|
||||||
|
<SelectTrigger>
|
||||||
|
<SelectValue />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="critical">Critical</SelectItem>
|
||||||
|
<SelectItem value="high">High</SelectItem>
|
||||||
|
<SelectItem value="medium">Medium</SelectItem>
|
||||||
|
<SelectItem value="low">Low</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<label className="text-sm font-medium">Type</label>
|
||||||
|
<Select value={type} onValueChange={setType}>
|
||||||
|
<SelectTrigger>
|
||||||
|
<SelectValue />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="monitor_down">Monitor Down</SelectItem>
|
||||||
|
<SelectItem value="domain_expiring">Domain Expiring</SelectItem>
|
||||||
|
<SelectItem value="ssl_expiring">SSL Expiring</SelectItem>
|
||||||
|
<SelectItem value="system_offline">System Offline</SelectItem>
|
||||||
|
<SelectItem value="maintenance">Maintenance</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<DialogFooter>
|
||||||
|
<Button variant="outline" onClick={() => onOpenChange(false)}>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button onClick={handleSubmit} disabled={!title}>
|
||||||
|
Create Incident
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main Status Page Manager Component
|
||||||
|
export function StatusPageManager() {
|
||||||
|
const { toast } = useToast()
|
||||||
|
const queryClient = useQueryClient()
|
||||||
|
const [activeTab, setActiveTab] = useState("overview")
|
||||||
|
const [statusPageDialogOpen, setStatusPageDialogOpen] = useState(false)
|
||||||
|
const [editingPage, setEditingPage] = useState<StatusPage | null>(null)
|
||||||
|
const [createIncidentOpen, setCreateIncidentOpen] = useState(false)
|
||||||
|
const [incidentFilter, setIncidentFilter] = useState<string>("all")
|
||||||
|
const [searchQuery, setSearchQuery] = useState("")
|
||||||
|
|
||||||
|
// Fetch data
|
||||||
|
const { data: pages, isLoading: pagesLoading } = useQuery({
|
||||||
|
queryKey: ["status-pages"],
|
||||||
|
queryFn: getStatusPages,
|
||||||
|
})
|
||||||
|
|
||||||
|
const { data: incidents, isLoading: incidentsLoading } = useQuery({
|
||||||
|
queryKey: ["incidents", incidentFilter],
|
||||||
|
queryFn: () => getIncidents(incidentFilter === "all" ? {} : { status: incidentFilter }),
|
||||||
|
})
|
||||||
|
|
||||||
|
const { data: stats } = useQuery({
|
||||||
|
queryKey: ["incident-stats"],
|
||||||
|
queryFn: getIncidentStats,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Mutations
|
||||||
|
const deletePageMutation = useMutation({
|
||||||
|
mutationFn: deleteStatusPage,
|
||||||
|
onSuccess: () => {
|
||||||
|
toast({ title: "Status page deleted" })
|
||||||
|
queryClient.invalidateQueries({ queryKey: ["status-pages"] })
|
||||||
|
},
|
||||||
|
onError: (error: Error) => {
|
||||||
|
toast({ title: "Failed to delete", description: error.message, variant: "destructive" })
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const createIncidentMutation = useMutation({
|
||||||
|
mutationFn: createIncident,
|
||||||
|
onSuccess: () => {
|
||||||
|
toast({ title: "Incident created" })
|
||||||
|
queryClient.invalidateQueries({ queryKey: ["incidents"] })
|
||||||
|
queryClient.invalidateQueries({ queryKey: ["incident-stats"] })
|
||||||
|
},
|
||||||
|
onError: (error: Error) => {
|
||||||
|
toast({ title: "Failed to create incident", description: error.message, variant: "destructive" })
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const acknowledgeMutation = useMutation({
|
||||||
|
mutationFn: acknowledgeIncident,
|
||||||
|
onSuccess: () => {
|
||||||
|
toast({ title: "Incident acknowledged" })
|
||||||
|
queryClient.invalidateQueries({ queryKey: ["incidents"] })
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const resolveMutation = useMutation({
|
||||||
|
mutationFn: (id: string) => resolveIncident(id),
|
||||||
|
onSuccess: () => {
|
||||||
|
toast({ title: "Incident resolved" })
|
||||||
|
queryClient.invalidateQueries({ queryKey: ["incidents"] })
|
||||||
|
queryClient.invalidateQueries({ queryKey: ["incident-stats"] })
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const closeMutation = useMutation({
|
||||||
|
mutationFn: closeIncident,
|
||||||
|
onSuccess: () => {
|
||||||
|
toast({ title: "Incident closed" })
|
||||||
|
queryClient.invalidateQueries({ queryKey: ["incidents"] })
|
||||||
|
queryClient.invalidateQueries({ queryKey: ["incident-stats"] })
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// Filtered incidents
|
||||||
|
const filteredIncidents = useMemo(() => {
|
||||||
|
if (!incidents) return []
|
||||||
|
if (!searchQuery) return incidents
|
||||||
|
return incidents.filter(
|
||||||
|
(i) =>
|
||||||
|
i.title.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||||
|
i.description?.toLowerCase().includes(searchQuery.toLowerCase())
|
||||||
|
)
|
||||||
|
}, [incidents, searchQuery])
|
||||||
|
|
||||||
|
// Active incidents count
|
||||||
|
const activeIncidents = useMemo(
|
||||||
|
() => incidents?.filter((i) => i.status === "open" || i.status === "acknowledged").length || 0,
|
||||||
|
[incidents]
|
||||||
|
)
|
||||||
|
|
||||||
|
const handleEdit = (page: StatusPage) => {
|
||||||
|
setEditingPage(page)
|
||||||
|
setStatusPageDialogOpen(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleAdd = () => {
|
||||||
|
setEditingPage(null)
|
||||||
|
setStatusPageDialogOpen(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleDelete = (page: StatusPage) => {
|
||||||
|
if (confirm(`Delete "${page.name}"? This will unlink all ${page.monitor_count} monitor(s).`)) {
|
||||||
|
deletePageMutation.mutate(page.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="space-y-6">
|
||||||
|
{/* Header */}
|
||||||
|
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4">
|
||||||
|
<div>
|
||||||
|
<h1 className="text-2xl font-bold tracking-tight">Status Page Manager</h1>
|
||||||
|
<p className="text-muted-foreground">
|
||||||
|
Manage status pages, incidents, and public communications
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Button variant="outline" onClick={() => setCreateIncidentOpen(true)}>
|
||||||
|
<AlertTriangle className="mr-2 h-4 w-4" />
|
||||||
|
New Incident
|
||||||
|
</Button>
|
||||||
|
<Button onClick={handleAdd}>
|
||||||
|
<Plus className="mr-2 h-4 w-4" />
|
||||||
|
New Status Page
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Quick Stats */}
|
||||||
|
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
|
||||||
|
<QuickStatCard
|
||||||
|
title="Status Pages"
|
||||||
|
value={pages?.length || 0}
|
||||||
|
subtitle={`${pages?.filter((p) => p.public).length || 0} public`}
|
||||||
|
icon={LayoutTemplate}
|
||||||
|
color="blue"
|
||||||
|
/>
|
||||||
|
<QuickStatCard
|
||||||
|
title="Active Incidents"
|
||||||
|
value={activeIncidents}
|
||||||
|
subtitle={`${incidents?.filter((i) => i.severity === "critical").length || 0} critical`}
|
||||||
|
icon={AlertTriangle}
|
||||||
|
color={activeIncidents > 0 ? "red" : "green"}
|
||||||
|
/>
|
||||||
|
<QuickStatCard
|
||||||
|
title="Total Monitors"
|
||||||
|
value={pages?.reduce((acc, p) => acc + p.monitor_count, 0) || 0}
|
||||||
|
subtitle="Across all pages"
|
||||||
|
icon={Activity}
|
||||||
|
color="purple"
|
||||||
|
/>
|
||||||
|
<QuickStatCard
|
||||||
|
title="MTTR (Hours)"
|
||||||
|
value={stats?.mttr_hours?.toFixed(1) || "-"}
|
||||||
|
subtitle="Mean time to resolution"
|
||||||
|
icon={Clock}
|
||||||
|
color="yellow"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Main Tabs */}
|
||||||
|
<Tabs value={activeTab} onValueChange={setActiveTab} className="space-y-4">
|
||||||
|
<TabsList className="grid w-full grid-cols-3 lg:w-[400px]">
|
||||||
|
<TabsTrigger value="overview">Overview</TabsTrigger>
|
||||||
|
<TabsTrigger value="pages">
|
||||||
|
Status Pages
|
||||||
|
{pages && pages.length > 0 && (
|
||||||
|
<Badge variant="secondary" className="ml-2">
|
||||||
|
{pages.length}
|
||||||
|
</Badge>
|
||||||
|
)}
|
||||||
|
</TabsTrigger>
|
||||||
|
<TabsTrigger value="incidents">
|
||||||
|
Incidents
|
||||||
|
{activeIncidents > 0 && (
|
||||||
|
<Badge variant="destructive" className="ml-2">
|
||||||
|
{activeIncidents}
|
||||||
|
</Badge>
|
||||||
|
)}
|
||||||
|
</TabsTrigger>
|
||||||
|
</TabsList>
|
||||||
|
|
||||||
|
{/* Overview Tab */}
|
||||||
|
<TabsContent value="overview" className="space-y-4">
|
||||||
|
<div className="grid gap-4 md:grid-cols-2">
|
||||||
|
{/* Recent Status Pages */}
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle className="text-lg flex items-center gap-2">
|
||||||
|
<LayoutTemplate className="h-5 w-5" />
|
||||||
|
Recent Status Pages
|
||||||
|
</CardTitle>
|
||||||
|
<CardDescription>
|
||||||
|
Your public and private status pages
|
||||||
|
</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
{pagesLoading ? (
|
||||||
|
<div className="space-y-2">
|
||||||
|
{[1, 2, 3].map((i) => (
|
||||||
|
<div key={i} className="h-12 bg-muted rounded animate-pulse" />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
) : pages?.length === 0 ? (
|
||||||
|
<div className="text-center py-8">
|
||||||
|
<LayoutTemplate className="h-8 w-8 text-muted-foreground mx-auto mb-2" />
|
||||||
|
<p className="text-sm text-muted-foreground">No status pages yet</p>
|
||||||
|
<Button variant="outline" size="sm" className="mt-2" onClick={handleAdd}>
|
||||||
|
Create one
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="space-y-2">
|
||||||
|
{pages?.slice(0, 5).map((page) => (
|
||||||
|
<div
|
||||||
|
key={page.id}
|
||||||
|
className="flex items-center justify-between p-3 rounded-lg border hover:bg-muted/50 transition-colors"
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
{page.public ? (
|
||||||
|
<Globe className="h-4 w-4 text-green-500" />
|
||||||
|
) : (
|
||||||
|
<Lock className="h-4 w-4 text-muted-foreground" />
|
||||||
|
)}
|
||||||
|
<div>
|
||||||
|
<p className="font-medium text-sm">{page.name}</p>
|
||||||
|
<p className="text-xs text-muted-foreground">
|
||||||
|
{page.monitor_count} monitors
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
{page.public && (
|
||||||
|
<a
|
||||||
|
href={getStatusPageUrl(page.slug)}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="text-muted-foreground hover:text-foreground"
|
||||||
|
>
|
||||||
|
<ExternalLink className="h-4 w-4" />
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => handleEdit(page)}
|
||||||
|
>
|
||||||
|
Edit
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{/* Recent Incidents */}
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle className="text-lg flex items-center gap-2">
|
||||||
|
<AlertTriangle className="h-5 w-5" />
|
||||||
|
Active Incidents
|
||||||
|
</CardTitle>
|
||||||
|
<CardDescription>
|
||||||
|
Incidents requiring attention
|
||||||
|
</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
{incidentsLoading ? (
|
||||||
|
<div className="space-y-2">
|
||||||
|
{[1, 2, 3].map((i) => (
|
||||||
|
<div key={i} className="h-12 bg-muted rounded animate-pulse" />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
) : filteredIncidents.filter((i) => i.status !== "closed").length === 0 ? (
|
||||||
|
<div className="text-center py-8">
|
||||||
|
<CheckCircle2 className="h-8 w-8 text-green-500 mx-auto mb-2" />
|
||||||
|
<p className="text-sm text-muted-foreground">All clear! No active incidents.</p>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="space-y-2">
|
||||||
|
{filteredIncidents
|
||||||
|
.filter((i) => i.status !== "closed")
|
||||||
|
.slice(0, 5)
|
||||||
|
.map((incident) => (
|
||||||
|
<div
|
||||||
|
key={incident.id}
|
||||||
|
className="flex items-center justify-between p-3 rounded-lg border hover:bg-muted/50 transition-colors"
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<Badge className={getSeverityColor(incident.severity)}>
|
||||||
|
{incident.severity}
|
||||||
|
</Badge>
|
||||||
|
<div>
|
||||||
|
<p className="font-medium text-sm">{incident.title}</p>
|
||||||
|
<p className="text-xs text-muted-foreground">
|
||||||
|
{formatDuration(incident.started_at)}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<IncidentQuickActions
|
||||||
|
incident={incident}
|
||||||
|
onAcknowledge={acknowledgeMutation.mutate}
|
||||||
|
onResolve={resolveMutation.mutate}
|
||||||
|
onClose={closeMutation.mutate}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
</TabsContent>
|
||||||
|
|
||||||
|
{/* Status Pages Tab */}
|
||||||
|
<TabsContent value="pages">
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>All Status Pages</CardTitle>
|
||||||
|
<CardDescription>
|
||||||
|
Manage your public and private status pages
|
||||||
|
</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
{pagesLoading ? (
|
||||||
|
<div className="space-y-2">
|
||||||
|
{[1, 2, 3].map((i) => (
|
||||||
|
<div key={i} className="h-16 bg-muted rounded animate-pulse" />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<Table>
|
||||||
|
<TableHeader>
|
||||||
|
<TableRow>
|
||||||
|
<TableHead>Name</TableHead>
|
||||||
|
<TableHead>Slug</TableHead>
|
||||||
|
<TableHead>Monitors</TableHead>
|
||||||
|
<TableHead>Visibility</TableHead>
|
||||||
|
<TableHead>Updated</TableHead>
|
||||||
|
<TableHead className="w-[150px]">Actions</TableHead>
|
||||||
|
</TableRow>
|
||||||
|
</TableHeader>
|
||||||
|
<TableBody>
|
||||||
|
{pages?.map((page) => (
|
||||||
|
<TableRow key={page.id}>
|
||||||
|
<TableCell className="font-medium">{page.name}</TableCell>
|
||||||
|
<TableCell>{page.slug}</TableCell>
|
||||||
|
<TableCell>{page.monitor_count}</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
{page.public ? (
|
||||||
|
<Badge variant="default" className="bg-green-500">
|
||||||
|
<Globe className="mr-1 h-3 w-3" />
|
||||||
|
Public
|
||||||
|
</Badge>
|
||||||
|
) : (
|
||||||
|
<Badge variant="secondary">
|
||||||
|
<Lock className="mr-1 h-3 w-3" />
|
||||||
|
Private
|
||||||
|
</Badge>
|
||||||
|
)}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
{new Date(page.updated).toLocaleDateString()}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
{page.public && (
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
asChild
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
href={getStatusPageUrl(page.slug)}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
<ExternalLink className="h-4 w-4" />
|
||||||
|
</a>
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => handleEdit(page)}
|
||||||
|
>
|
||||||
|
<Wrench className="mr-1 h-3.5 w-3.5" />
|
||||||
|
Edit
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
className="text-destructive"
|
||||||
|
onClick={() => handleDelete(page)}
|
||||||
|
>
|
||||||
|
<Trash2 className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
)}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</TabsContent>
|
||||||
|
|
||||||
|
{/* Incidents Tab */}
|
||||||
|
<TabsContent value="incidents">
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4">
|
||||||
|
<div>
|
||||||
|
<CardTitle>All Incidents</CardTitle>
|
||||||
|
<CardDescription>
|
||||||
|
Manage and track all incidents
|
||||||
|
</CardDescription>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<div className="relative">
|
||||||
|
<Search className="absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground" />
|
||||||
|
<Input
|
||||||
|
placeholder="Search incidents..."
|
||||||
|
className="pl-8 w-[200px]"
|
||||||
|
value={searchQuery}
|
||||||
|
onChange={(e) => setSearchQuery(e.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<Select value={incidentFilter} onValueChange={setIncidentFilter}>
|
||||||
|
<SelectTrigger className="w-[130px]">
|
||||||
|
<Filter className="mr-2 h-4 w-4" />
|
||||||
|
<SelectValue />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="all">All</SelectItem>
|
||||||
|
<SelectItem value="open">Open</SelectItem>
|
||||||
|
<SelectItem value="acknowledged">Acknowledged</SelectItem>
|
||||||
|
<SelectItem value="resolved">Resolved</SelectItem>
|
||||||
|
<SelectItem value="closed">Closed</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
{incidentsLoading ? (
|
||||||
|
<div className="space-y-2">
|
||||||
|
{[1, 2, 3].map((i) => (
|
||||||
|
<div key={i} className="h-16 bg-muted rounded animate-pulse" />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
) : filteredIncidents.length === 0 ? (
|
||||||
|
<div className="text-center py-12">
|
||||||
|
<CheckCircle2 className="h-12 w-12 text-green-500 mx-auto mb-4" />
|
||||||
|
<p className="text-lg font-medium">No incidents found</p>
|
||||||
|
<p className="text-muted-foreground">
|
||||||
|
{searchQuery
|
||||||
|
? "Try adjusting your search or filters"
|
||||||
|
: "All systems are running smoothly"}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<Table>
|
||||||
|
<TableHeader>
|
||||||
|
<TableRow>
|
||||||
|
<TableHead>Title</TableHead>
|
||||||
|
<TableHead>Severity</TableHead>
|
||||||
|
<TableHead>Status</TableHead>
|
||||||
|
<TableHead>Duration</TableHead>
|
||||||
|
<TableHead>Started</TableHead>
|
||||||
|
<TableHead className="w-[250px]">Actions</TableHead>
|
||||||
|
</TableRow>
|
||||||
|
</TableHeader>
|
||||||
|
<TableBody>
|
||||||
|
{filteredIncidents.map((incident) => (
|
||||||
|
<TableRow key={incident.id}>
|
||||||
|
<TableCell className="font-medium max-w-[300px] truncate">
|
||||||
|
{incident.title}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<Badge className={getSeverityColor(incident.severity)}>
|
||||||
|
{incident.severity}
|
||||||
|
</Badge>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<Badge
|
||||||
|
variant="outline"
|
||||||
|
className={getStatusColor(incident.status)}
|
||||||
|
>
|
||||||
|
{incident.status}
|
||||||
|
</Badge>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>{formatDuration(incident.started_at)}</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
{new Date(incident.started_at).toLocaleDateString()}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<IncidentQuickActions
|
||||||
|
incident={incident}
|
||||||
|
onAcknowledge={acknowledgeMutation.mutate}
|
||||||
|
onResolve={resolveMutation.mutate}
|
||||||
|
onClose={closeMutation.mutate}
|
||||||
|
/>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
)}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</TabsContent>
|
||||||
|
</Tabs>
|
||||||
|
|
||||||
|
{/* Dialogs */}
|
||||||
|
<StatusPageDialog
|
||||||
|
open={statusPageDialogOpen}
|
||||||
|
onOpenChange={setStatusPageDialogOpen}
|
||||||
|
page={editingPage}
|
||||||
|
isEdit={!!editingPage}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<CreateIncidentDialog
|
||||||
|
open={createIncidentOpen}
|
||||||
|
onOpenChange={setCreateIncidentOpen}
|
||||||
|
onCreate={createIncidentMutation.mutate}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -6,7 +6,6 @@ import { getPagePath } from "@nanostores/router"
|
|||||||
import type { CellContext, ColumnDef, HeaderContext } from "@tanstack/react-table"
|
import type { CellContext, ColumnDef, HeaderContext } from "@tanstack/react-table"
|
||||||
import type { ClassValue } from "clsx"
|
import type { ClassValue } from "clsx"
|
||||||
import {
|
import {
|
||||||
ArrowUpDownIcon,
|
|
||||||
ChevronRightSquareIcon,
|
ChevronRightSquareIcon,
|
||||||
ClockArrowUp,
|
ClockArrowUp,
|
||||||
CopyIcon,
|
CopyIcon,
|
||||||
@@ -265,7 +264,6 @@ export function SystemsTableColumns(viewMode: "table" | "grid"): ColumnDef<Syste
|
|||||||
id: "temp",
|
id: "temp",
|
||||||
name: () => t({ message: "Temp", comment: "Temperature label in systems table" }),
|
name: () => t({ message: "Temp", comment: "Temperature label in systems table" }),
|
||||||
size: 50,
|
size: 50,
|
||||||
hideSort: true,
|
|
||||||
Icon: ThermometerIcon,
|
Icon: ThermometerIcon,
|
||||||
header: sortableHeader,
|
header: sortableHeader,
|
||||||
cell(info) {
|
cell(info) {
|
||||||
@@ -289,7 +287,6 @@ export function SystemsTableColumns(viewMode: "table" | "grid"): ColumnDef<Syste
|
|||||||
size: 70,
|
size: 70,
|
||||||
Icon: BatteryMediumIcon,
|
Icon: BatteryMediumIcon,
|
||||||
header: sortableHeader,
|
header: sortableHeader,
|
||||||
hideSort: true,
|
|
||||||
cell(info) {
|
cell(info) {
|
||||||
const [pct, state] = info.row.original.info.bat ?? []
|
const [pct, state] = info.row.original.info.bat ?? []
|
||||||
if (pct === undefined) {
|
if (pct === undefined) {
|
||||||
@@ -335,7 +332,6 @@ export function SystemsTableColumns(viewMode: "table" | "grid"): ColumnDef<Syste
|
|||||||
size: 50,
|
size: 50,
|
||||||
Icon: TerminalSquareIcon,
|
Icon: TerminalSquareIcon,
|
||||||
header: sortableHeader,
|
header: sortableHeader,
|
||||||
hideSort: true,
|
|
||||||
sortingFn: (a, b) => {
|
sortingFn: (a, b) => {
|
||||||
// sort priorities: 1) failed services, 2) total services
|
// sort priorities: 1) failed services, 2) total services
|
||||||
const [totalCountA, numFailedA] = a.original.info.sv ?? [0, 0]
|
const [totalCountA, numFailedA] = a.original.info.sv ?? [0, 0]
|
||||||
@@ -374,7 +370,6 @@ export function SystemsTableColumns(viewMode: "table" | "grid"): ColumnDef<Syste
|
|||||||
size: 50,
|
size: 50,
|
||||||
Icon: ClockArrowUp,
|
Icon: ClockArrowUp,
|
||||||
header: sortableHeader,
|
header: sortableHeader,
|
||||||
hideSort: true,
|
|
||||||
cell(info) {
|
cell(info) {
|
||||||
const uptime = info.getValue() as number
|
const uptime = info.getValue() as number
|
||||||
if (!uptime) {
|
if (!uptime) {
|
||||||
@@ -389,7 +384,6 @@ export function SystemsTableColumns(viewMode: "table" | "grid"): ColumnDef<Syste
|
|||||||
name: () => t`Agent`,
|
name: () => t`Agent`,
|
||||||
size: 50,
|
size: 50,
|
||||||
Icon: WifiIcon,
|
Icon: WifiIcon,
|
||||||
hideSort: true,
|
|
||||||
header: sortableHeader,
|
header: sortableHeader,
|
||||||
cell(info) {
|
cell(info) {
|
||||||
const version = info.getValue() as string
|
const version = info.getValue() as string
|
||||||
@@ -443,17 +437,16 @@ export function SystemsTableColumns(viewMode: "table" | "grid"): ColumnDef<Syste
|
|||||||
function sortableHeader(context: HeaderContext<SystemRecord, unknown>) {
|
function sortableHeader(context: HeaderContext<SystemRecord, unknown>) {
|
||||||
const { column } = context
|
const { column } = context
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
const { Icon, hideSort, name }: { Icon: React.ElementType; name: () => string; hideSort: boolean } = column.columnDef
|
const { Icon, name }: { Icon: React.ElementType; name: () => string } = column.columnDef
|
||||||
const isSorted = column.getIsSorted()
|
const isSorted = column.getIsSorted()
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
className={cn("h-9 px-3 flex duration-50", isSorted && "bg-accent/70 light:bg-accent text-accent-foreground/90")}
|
className={cn("h-9 px-3 flex items-center gap-2 duration-50", isSorted && "bg-accent/70 light:bg-accent text-accent-foreground/90")}
|
||||||
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
|
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
|
||||||
>
|
>
|
||||||
{Icon && <Icon className="me-2 size-4" />}
|
{Icon && <Icon className="size-4" />}
|
||||||
{name()}
|
{name()}
|
||||||
{hideSort || <ArrowUpDownIcon className="ms-2 size-4" />}
|
|
||||||
</Button>
|
</Button>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import {
|
|||||||
ArrowUpIcon,
|
ArrowUpIcon,
|
||||||
EyeIcon,
|
EyeIcon,
|
||||||
FilterIcon,
|
FilterIcon,
|
||||||
|
GripVertical,
|
||||||
LayoutGridIcon,
|
LayoutGridIcon,
|
||||||
LayoutListIcon,
|
LayoutListIcon,
|
||||||
PlusIcon,
|
PlusIcon,
|
||||||
@@ -96,6 +97,58 @@ export default function SystemsTable() {
|
|||||||
window.innerWidth < 1024 && filteredData.length < 200 ? "grid" : "table"
|
window.innerWidth < 1024 && filteredData.length < 200 ? "grid" : "table"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Drag and drop state
|
||||||
|
const [draggedItem, setDraggedItem] = useState<SystemRecord | null>(null)
|
||||||
|
const [dragOverIndex, setDragOverIndex] = useState<number | null>(null)
|
||||||
|
|
||||||
|
// Handle drag start
|
||||||
|
const handleDragStart = (e: React.DragEvent, item: SystemRecord) => {
|
||||||
|
setDraggedItem(item)
|
||||||
|
e.dataTransfer.effectAllowed = 'move'
|
||||||
|
e.dataTransfer.setData('text/html', e.currentTarget.outerHTML)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle drag over
|
||||||
|
const handleDragOver = (e: React.DragEvent, index: number) => {
|
||||||
|
e.preventDefault()
|
||||||
|
e.dataTransfer.dropEffect = 'move'
|
||||||
|
setDragOverIndex(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle drag leave
|
||||||
|
const handleDragLeave = () => {
|
||||||
|
setDragOverIndex(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle drop
|
||||||
|
const handleDrop = (e: React.DragEvent, dropIndex: number) => {
|
||||||
|
e.preventDefault()
|
||||||
|
setDragOverIndex(null)
|
||||||
|
|
||||||
|
if (!draggedItem) return
|
||||||
|
|
||||||
|
// Find the dragged item's current index
|
||||||
|
const draggedIndex = filteredData.findIndex(item => item.id === draggedItem.id)
|
||||||
|
if (draggedIndex === dropIndex) return
|
||||||
|
|
||||||
|
// Reorder the data
|
||||||
|
const reorderedData = [...filteredData]
|
||||||
|
reorderedData.splice(draggedIndex, 1)
|
||||||
|
reorderedData.splice(dropIndex, 0, draggedItem)
|
||||||
|
|
||||||
|
// Update the systems store with new order
|
||||||
|
// This would require backend support to persist the order
|
||||||
|
console.log('Reordered systems:', reorderedData.map(item => ({ id: item.id, name: item.name })))
|
||||||
|
|
||||||
|
setDraggedItem(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle drag end
|
||||||
|
const handleDragEnd = () => {
|
||||||
|
setDraggedItem(null)
|
||||||
|
setDragOverIndex(null)
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (filter !== undefined) {
|
if (filter !== undefined) {
|
||||||
table.getColumn("system")?.setFilterValue(filter)
|
table.getColumn("system")?.setFilterValue(filter)
|
||||||
@@ -138,7 +191,9 @@ export default function SystemsTable() {
|
|||||||
const CardHead = useMemo(() => {
|
const CardHead = useMemo(() => {
|
||||||
return (
|
return (
|
||||||
<CardHeader className="p-0 mb-3 sm:mb-4">
|
<CardHeader className="p-0 mb-3 sm:mb-4">
|
||||||
<div className="grid md:flex gap-x-5 gap-y-3 w-full items-end">
|
<div className="flex flex-col gap-4">
|
||||||
|
{/* Title and Add Button Row */}
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
<div className="px-2 sm:px-1">
|
<div className="px-2 sm:px-1">
|
||||||
<CardTitle className="mb-2">
|
<CardTitle className="mb-2">
|
||||||
<Trans>All Systems</Trans>
|
<Trans>All Systems</Trans>
|
||||||
@@ -147,12 +202,14 @@ export default function SystemsTable() {
|
|||||||
<Trans>Click on a system to view more information.</Trans>
|
<Trans>Click on a system to view more information.</Trans>
|
||||||
</CardDescription>
|
</CardDescription>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex gap-2 ms-auto w-full md:w-80">
|
|
||||||
<Button onClick={() => setIsAddDialogOpen(true)} className="shrink-0">
|
<Button onClick={() => setIsAddDialogOpen(true)} className="shrink-0">
|
||||||
<PlusIcon className="mr-2 h-4 w-4" />
|
<PlusIcon className="mr-2 h-4 w-4" />
|
||||||
<Trans>Add System</Trans>
|
<Trans>Add System</Trans>
|
||||||
</Button>
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Filter and View Controls Row */}
|
||||||
|
<div className="flex gap-2 w-full md:w-96">
|
||||||
<div className="relative flex-1">
|
<div className="relative flex-1">
|
||||||
<Input
|
<Input
|
||||||
placeholder={t`Filter...`}
|
placeholder={t`Filter...`}
|
||||||
@@ -250,11 +307,12 @@ export default function SystemsTable() {
|
|||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
onSelect={(e) => {
|
|
||||||
e.preventDefault()
|
|
||||||
setSorting([{ id: column.id, desc: sorting[0]?.id === column.id && !sorting[0]?.desc }])
|
|
||||||
}}
|
|
||||||
key={column.id}
|
key={column.id}
|
||||||
|
onClick={() => {
|
||||||
|
const isDesc = sorting[0]?.id === column.id && !sorting[0]?.desc
|
||||||
|
setSorting([{ id: column.id, desc: isDesc }])
|
||||||
|
}}
|
||||||
|
className="gap-2"
|
||||||
>
|
>
|
||||||
{Icon}
|
{Icon}
|
||||||
{/* @ts-ignore */}
|
{/* @ts-ignore */}
|
||||||
@@ -268,13 +326,12 @@ export default function SystemsTable() {
|
|||||||
<div>
|
<div>
|
||||||
<DropdownMenuLabel className="pt-2 px-3.5 flex items-center gap-2">
|
<DropdownMenuLabel className="pt-2 px-3.5 flex items-center gap-2">
|
||||||
<EyeIcon className="size-4" />
|
<EyeIcon className="size-4" />
|
||||||
<Trans>Visible Fields</Trans>
|
<Trans>Columns</Trans>
|
||||||
</DropdownMenuLabel>
|
</DropdownMenuLabel>
|
||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
<div className="px-1.5 pb-1">
|
<div className="px-1 pb-1">
|
||||||
{columns
|
{columns.map((column) => {
|
||||||
.filter((column) => column.getCanHide())
|
if (column.id === "select") return null
|
||||||
.map((column) => {
|
|
||||||
return (
|
return (
|
||||||
<DropdownMenuCheckboxItem
|
<DropdownMenuCheckboxItem
|
||||||
key={column.id}
|
key={column.id}
|
||||||
@@ -315,7 +372,18 @@ export default function SystemsTable() {
|
|||||||
{viewMode === "table" ? (
|
{viewMode === "table" ? (
|
||||||
// table layout
|
// table layout
|
||||||
<div className="rounded-md">
|
<div className="rounded-md">
|
||||||
<AllSystemsTable table={table} rows={rows} colLength={visibleColumns.length} />
|
<AllSystemsTable
|
||||||
|
table={table}
|
||||||
|
rows={rows}
|
||||||
|
colLength={visibleColumns.length}
|
||||||
|
draggedItem={draggedItem}
|
||||||
|
dragOverIndex={dragOverIndex}
|
||||||
|
handleDragStart={handleDragStart}
|
||||||
|
handleDragOver={handleDragOver}
|
||||||
|
handleDragLeave={handleDragLeave}
|
||||||
|
handleDrop={handleDrop}
|
||||||
|
handleDragEnd={handleDragEnd}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
// grid layout
|
// grid layout
|
||||||
@@ -338,7 +406,29 @@ export default function SystemsTable() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const AllSystemsTable = memo(
|
const AllSystemsTable = memo(
|
||||||
({ table, rows, colLength }: { table: TableType<SystemRecord>; rows: Row<SystemRecord>[]; colLength: number }) => {
|
({
|
||||||
|
table,
|
||||||
|
rows,
|
||||||
|
colLength,
|
||||||
|
draggedItem,
|
||||||
|
dragOverIndex,
|
||||||
|
handleDragStart,
|
||||||
|
handleDragOver,
|
||||||
|
handleDragLeave,
|
||||||
|
handleDrop,
|
||||||
|
handleDragEnd
|
||||||
|
}: {
|
||||||
|
table: TableType<SystemRecord>;
|
||||||
|
rows: Row<SystemRecord>[];
|
||||||
|
colLength: number
|
||||||
|
draggedItem: SystemRecord | null
|
||||||
|
dragOverIndex: number | null
|
||||||
|
handleDragStart: (e: React.DragEvent, item: SystemRecord) => void
|
||||||
|
handleDragOver: (e: React.DragEvent, index: number) => void
|
||||||
|
handleDragLeave: () => void
|
||||||
|
handleDrop: (e: React.DragEvent, index: number) => void
|
||||||
|
handleDragEnd: () => void
|
||||||
|
}) => {
|
||||||
// The virtualizer will need a reference to the scrollable container element
|
// The virtualizer will need a reference to the scrollable container element
|
||||||
const scrollRef = useRef<HTMLDivElement>(null)
|
const scrollRef = useRef<HTMLDivElement>(null)
|
||||||
|
|
||||||
@@ -377,6 +467,13 @@ const AllSystemsTable = memo(
|
|||||||
virtualRow={virtualRow}
|
virtualRow={virtualRow}
|
||||||
length={rows.length}
|
length={rows.length}
|
||||||
colLength={colLength}
|
colLength={colLength}
|
||||||
|
draggedItem={draggedItem}
|
||||||
|
dragOverIndex={dragOverIndex}
|
||||||
|
handleDragStart={handleDragStart}
|
||||||
|
handleDragOver={handleDragOver}
|
||||||
|
handleDragLeave={handleDragLeave}
|
||||||
|
handleDrop={handleDrop}
|
||||||
|
handleDragEnd={handleDragEnd}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@@ -396,7 +493,6 @@ const AllSystemsTable = memo(
|
|||||||
)
|
)
|
||||||
|
|
||||||
function SystemsTableHead({ table }: { table: TableType<SystemRecord> }) {
|
function SystemsTableHead({ table }: { table: TableType<SystemRecord> }) {
|
||||||
const { t } = useLingui()
|
|
||||||
return (
|
return (
|
||||||
<TableHeader className="sticky top-0 z-50 w-full border-b-2">
|
<TableHeader className="sticky top-0 z-50 w-full border-b-2">
|
||||||
{table.getHeaderGroups().map((headerGroup) => (
|
{table.getHeaderGroups().map((headerGroup) => (
|
||||||
@@ -419,32 +515,73 @@ const SystemTableRow = memo(
|
|||||||
row,
|
row,
|
||||||
virtualRow,
|
virtualRow,
|
||||||
colLength,
|
colLength,
|
||||||
|
draggedItem,
|
||||||
|
dragOverIndex,
|
||||||
|
handleDragStart,
|
||||||
|
handleDragOver,
|
||||||
|
handleDragLeave,
|
||||||
|
handleDrop,
|
||||||
|
handleDragEnd,
|
||||||
}: {
|
}: {
|
||||||
row: Row<SystemRecord>
|
row: Row<SystemRecord>
|
||||||
virtualRow: VirtualItem
|
virtualRow: VirtualItem
|
||||||
length: number
|
length: number
|
||||||
colLength: number
|
colLength: number
|
||||||
|
draggedItem: SystemRecord | null
|
||||||
|
dragOverIndex: number | null
|
||||||
|
handleDragStart: (e: React.DragEvent, item: SystemRecord) => void
|
||||||
|
handleDragOver: (e: React.DragEvent, index: number) => void
|
||||||
|
handleDragLeave: () => void
|
||||||
|
handleDrop: (e: React.DragEvent, index: number) => void
|
||||||
|
handleDragEnd: () => void
|
||||||
}) => {
|
}) => {
|
||||||
const system = row.original
|
const system = row.original
|
||||||
const { t } = useLingui()
|
const { t } = useLingui()
|
||||||
|
const isDragged = draggedItem?.id === system.id
|
||||||
|
const isDragOver = dragOverIndex === virtualRow.index
|
||||||
|
|
||||||
return useMemo(() => {
|
return useMemo(() => {
|
||||||
return (
|
return (
|
||||||
<TableRow
|
<TableRow
|
||||||
|
draggable
|
||||||
|
onDragStart={(e) => handleDragStart(e, system)}
|
||||||
|
onDragOver={(e) => handleDragOver(e, virtualRow.index)}
|
||||||
|
onDragLeave={handleDragLeave}
|
||||||
|
onDrop={(e) => handleDrop(e, virtualRow.index)}
|
||||||
|
onDragEnd={handleDragEnd}
|
||||||
// data-state={row.getIsSelected() && "selected"}
|
// data-state={row.getIsSelected() && "selected"}
|
||||||
className={cn("cursor-pointer transition-opacity relative safari:transform-3d", {
|
className={cn("cursor-pointer transition-opacity relative safari:transform-3d", {
|
||||||
"opacity-50": system.status === SystemStatus.Paused,
|
"opacity-50": system.status === SystemStatus.Paused,
|
||||||
|
"opacity-30": isDragged,
|
||||||
|
"border-t-2 border-b-2 border-blue-500 bg-blue-50": isDragOver,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
{row.getVisibleCells().map((cell) => (
|
{row.getVisibleCells().map((cell, index) => (
|
||||||
<TableCell
|
<TableCell
|
||||||
key={cell.id}
|
key={cell.id}
|
||||||
style={{
|
style={{
|
||||||
width: cell.column.getSize(),
|
width: cell.column.getSize(),
|
||||||
height: virtualRow.size,
|
height: virtualRow.size,
|
||||||
}}
|
}}
|
||||||
className="py-0 ps-4.5"
|
className={cn("py-0", index === 0 ? "ps-2" : "ps-4.5")}
|
||||||
>
|
>
|
||||||
|
{index === 0 ? (
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<div
|
||||||
|
className="cursor-grab active:cursor-grabbing p-1 hover:bg-muted rounded"
|
||||||
|
onDragStart={(e) => handleDragStart(e, system)}
|
||||||
|
onDragOver={(e) => handleDragOver(e, virtualRow.index)}
|
||||||
|
onDragLeave={handleDragLeave}
|
||||||
|
onDrop={(e) => handleDrop(e, virtualRow.index)}
|
||||||
|
onDragEnd={handleDragEnd}
|
||||||
|
>
|
||||||
|
<GripVertical className="h-4 w-4 text-muted-foreground" />
|
||||||
|
</div>
|
||||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
flexRender(cell.column.columnDef.cell, cell.getContext())
|
||||||
|
)}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
))}
|
))}
|
||||||
</TableRow>
|
</TableRow>
|
||||||
|
|||||||
@@ -1,5 +1,20 @@
|
|||||||
import { pb } from "./api"
|
import { pb } from "./api"
|
||||||
|
|
||||||
|
export interface Subdomain {
|
||||||
|
id: string
|
||||||
|
domain: string
|
||||||
|
subdomain_name: string
|
||||||
|
full_domain: string
|
||||||
|
status: "active" | "inactive" | "error"
|
||||||
|
ip_addresses?: string
|
||||||
|
http_status?: number
|
||||||
|
server_header?: string
|
||||||
|
discovery_source: string
|
||||||
|
last_checked?: string
|
||||||
|
created: string
|
||||||
|
updated: string
|
||||||
|
}
|
||||||
|
|
||||||
export interface Domain {
|
export interface Domain {
|
||||||
id: string
|
id: string
|
||||||
domain_name: string
|
domain_name: string
|
||||||
@@ -106,6 +121,66 @@ export interface Domain {
|
|||||||
dns_spf_records?: string[]
|
dns_spf_records?: string[]
|
||||||
dns_dkim_records?: string[]
|
dns_dkim_records?: string[]
|
||||||
dns_dmarc_records?: string[]
|
dns_dmarc_records?: string[]
|
||||||
|
|
||||||
|
// Provider Detection
|
||||||
|
dns_provider?: string
|
||||||
|
hosting_provider?: string
|
||||||
|
email_provider?: string
|
||||||
|
ca_provider?: string
|
||||||
|
|
||||||
|
// HTTP Headers
|
||||||
|
headers?: { name: string; value: string }[]
|
||||||
|
|
||||||
|
// Certificate Chain
|
||||||
|
certificates?: {
|
||||||
|
issuer: string
|
||||||
|
subject: string
|
||||||
|
alt_names: string[]
|
||||||
|
valid_from: string
|
||||||
|
valid_to: string
|
||||||
|
ca_provider: string
|
||||||
|
}[]
|
||||||
|
|
||||||
|
// SEO Metadata
|
||||||
|
seo_meta?: {
|
||||||
|
openGraph: {
|
||||||
|
url: string
|
||||||
|
type: string
|
||||||
|
title: string
|
||||||
|
images: string[]
|
||||||
|
description: string
|
||||||
|
}
|
||||||
|
twitter: {
|
||||||
|
title: string
|
||||||
|
description: string
|
||||||
|
image: string
|
||||||
|
card: string
|
||||||
|
}
|
||||||
|
general: {
|
||||||
|
title: string
|
||||||
|
author: string
|
||||||
|
robots: string
|
||||||
|
keywords: string
|
||||||
|
canonical: string
|
||||||
|
description: string
|
||||||
|
}
|
||||||
|
robots: {
|
||||||
|
fetched: boolean
|
||||||
|
groups: {
|
||||||
|
userAgents: string[]
|
||||||
|
rules: { type: string; value: string }[]
|
||||||
|
}[]
|
||||||
|
sitemaps: string[]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Raw WHOIS & Registration Details
|
||||||
|
whois_raw?: string
|
||||||
|
privacy_enabled?: boolean
|
||||||
|
transfer_lock?: boolean
|
||||||
|
tld?: string
|
||||||
|
domain_statuses?: string[]
|
||||||
|
host_country_code?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DomainHistory {
|
export interface DomainHistory {
|
||||||
@@ -139,6 +214,9 @@ export interface CreateDomainRequest {
|
|||||||
quiet_hours_enabled?: boolean
|
quiet_hours_enabled?: boolean
|
||||||
quiet_hours_start?: string
|
quiet_hours_start?: string
|
||||||
quiet_hours_end?: string
|
quiet_hours_end?: string
|
||||||
|
// Manual expiry override when WHOIS fails
|
||||||
|
expiry_date?: string
|
||||||
|
creation_date?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UpdateDomainRequest {
|
export interface UpdateDomainRequest {
|
||||||
@@ -186,6 +264,66 @@ export interface DomainLookupResult {
|
|||||||
host_isp?: string
|
host_isp?: string
|
||||||
favicon_url?: string
|
favicon_url?: string
|
||||||
last_checked?: string
|
last_checked?: string
|
||||||
|
|
||||||
|
// Provider Detection
|
||||||
|
dns_provider?: string
|
||||||
|
hosting_provider?: string
|
||||||
|
email_provider?: string
|
||||||
|
ca_provider?: string
|
||||||
|
|
||||||
|
// HTTP Headers
|
||||||
|
headers?: { name: string; value: string }[]
|
||||||
|
|
||||||
|
// Certificate Chain
|
||||||
|
certificates?: {
|
||||||
|
issuer: string
|
||||||
|
subject: string
|
||||||
|
alt_names: string[]
|
||||||
|
valid_from: string
|
||||||
|
valid_to: string
|
||||||
|
ca_provider: string
|
||||||
|
}[]
|
||||||
|
|
||||||
|
// SEO Metadata
|
||||||
|
seo_meta?: {
|
||||||
|
openGraph: {
|
||||||
|
url: string
|
||||||
|
type: string
|
||||||
|
title: string
|
||||||
|
images: string[]
|
||||||
|
description: string
|
||||||
|
}
|
||||||
|
twitter: {
|
||||||
|
title: string
|
||||||
|
description: string
|
||||||
|
image: string
|
||||||
|
card: string
|
||||||
|
}
|
||||||
|
general: {
|
||||||
|
title: string
|
||||||
|
author: string
|
||||||
|
robots: string
|
||||||
|
keywords: string
|
||||||
|
canonical: string
|
||||||
|
description: string
|
||||||
|
}
|
||||||
|
robots: {
|
||||||
|
fetched: boolean
|
||||||
|
groups: {
|
||||||
|
userAgents: string[]
|
||||||
|
rules: { type: string; value: string }[]
|
||||||
|
}[]
|
||||||
|
sitemaps: string[]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Raw WHOIS & Registration Details
|
||||||
|
whois_raw?: string
|
||||||
|
privacy_enabled?: boolean
|
||||||
|
transfer_lock?: boolean
|
||||||
|
tld?: string
|
||||||
|
domain_statuses?: string[]
|
||||||
|
host_country_code?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const API_BASE = "/api/beszel/domains"
|
const API_BASE = "/api/beszel/domains"
|
||||||
@@ -377,3 +515,65 @@ export function cleanDomain(domain: string): string {
|
|||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.trim()
|
.trim()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Subdomain API functions
|
||||||
|
export async function getDomainSubdomains(domainId: string): Promise<Subdomain[]> {
|
||||||
|
const response = await fetch(`/api/beszel/domains/${domainId}/subdomains`, {
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${pb.authStore.token}`,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Failed to fetch subdomains: ${response.statusText}`)
|
||||||
|
}
|
||||||
|
return response.json()
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function refreshSubdomainDiscovery(domainId: string): Promise<void> {
|
||||||
|
const response = await fetch(`/api/beszel/domains/${domainId}/discover-subdomains`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${pb.authStore.token}`,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Failed to start subdomain discovery: ${response.statusText}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteSubdomain(subdomainId: string): Promise<void> {
|
||||||
|
const response = await fetch(`/api/beszel/subdomains/${subdomainId}`, {
|
||||||
|
method: "DELETE",
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${pb.authStore.token}`,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Failed to delete subdomain: ${response.statusText}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function extractDomainFromUrl(url: string): string {
|
||||||
|
try {
|
||||||
|
const urlObj = new URL(url.startsWith("http") ? url : `https://${url}`)
|
||||||
|
return urlObj.hostname.toLowerCase()
|
||||||
|
} catch {
|
||||||
|
return cleanDomain(url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isSubdomain(fullDomain: string, parentDomain: string): boolean {
|
||||||
|
const cleanFull = cleanDomain(fullDomain)
|
||||||
|
const cleanParent = cleanDomain(parentDomain)
|
||||||
|
return cleanFull.endsWith(`.${cleanParent}`) || cleanFull === cleanParent
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getSubdomainName(fullDomain: string, parentDomain: string): string {
|
||||||
|
const cleanFull = cleanDomain(fullDomain)
|
||||||
|
const cleanParent = cleanDomain(parentDomain)
|
||||||
|
if (cleanFull === cleanParent) return "@"
|
||||||
|
if (cleanFull.endsWith(`.${cleanParent}`)) {
|
||||||
|
return cleanFull.slice(0, -cleanParent.length - 1)
|
||||||
|
}
|
||||||
|
return cleanFull
|
||||||
|
}
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ export interface Monitor {
|
|||||||
description?: string
|
description?: string
|
||||||
last_check?: string
|
last_check?: string
|
||||||
uptime_stats?: Record<string, number>
|
uptime_stats?: Record<string, number>
|
||||||
|
recent_heartbeats?: Array<{ status: string; time: string; ping?: number }>
|
||||||
tags?: string[]
|
tags?: string[]
|
||||||
keyword?: string
|
keyword?: string
|
||||||
json_query?: string
|
json_query?: string
|
||||||
@@ -196,6 +197,25 @@ export interface CheckResult {
|
|||||||
time?: string
|
time?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface PageSpeedMetrics {
|
||||||
|
performance: number
|
||||||
|
accessibility: number
|
||||||
|
bestPractices: number
|
||||||
|
seo: number
|
||||||
|
pwa: number
|
||||||
|
fcp: number
|
||||||
|
lcp: number
|
||||||
|
ttfb: number
|
||||||
|
cls: number
|
||||||
|
tbt: number
|
||||||
|
speedIndex: number
|
||||||
|
tti: number
|
||||||
|
strategy: string
|
||||||
|
checkedAt: string
|
||||||
|
url: string
|
||||||
|
vitals: Record<string, string>
|
||||||
|
}
|
||||||
|
|
||||||
// API Functions
|
// API Functions
|
||||||
export async function listMonitors(): Promise<Monitor[]> {
|
export async function listMonitors(): Promise<Monitor[]> {
|
||||||
const response = await pb.send<{ monitors: Monitor[] }>("/api/beszel/monitors", {})
|
const response = await pb.send<{ monitors: Monitor[] }>("/api/beszel/monitors", {})
|
||||||
@@ -260,6 +280,12 @@ export function getMonitorHeartbeats(id: string): Promise<{ heartbeats: Heartbea
|
|||||||
return pb.send(`/api/beszel/monitors/${id}/heartbeats`, {})
|
return pb.send(`/api/beszel/monitors/${id}/heartbeats`, {})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function runPageSpeedCheck(id: string, strategy: string = "mobile"): Promise<PageSpeedMetrics> {
|
||||||
|
return pb.send(`/api/beszel/monitors/${id}/pagespeed?strategy=${strategy}`, {
|
||||||
|
method: "POST",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Helper functions
|
// Helper functions
|
||||||
export function getMonitorTypeLabel(type: MonitorType): string {
|
export function getMonitorTypeLabel(type: MonitorType): string {
|
||||||
const labels: Record<MonitorType, string> = {
|
const labels: Record<MonitorType, string> = {
|
||||||
@@ -338,3 +364,105 @@ export function formatPing(ping: number): string {
|
|||||||
if (ping < 1000) return `${ping}ms`
|
if (ping < 1000) return `${ping}ms`
|
||||||
return `${(ping / 1000).toFixed(2)}s`
|
return `${(ping / 1000).toFixed(2)}s`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Favicon URL helper - uses Google's favicon service as fallback
|
||||||
|
export function getMonitorFaviconUrl(monitor: Monitor): string | null {
|
||||||
|
const hostname = extractHostnameFromMonitor(monitor)
|
||||||
|
if (!hostname) return null
|
||||||
|
return `https://www.google.com/s2/favicons?domain=${encodeURIComponent(hostname)}&sz=32`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Domain extraction and grouping utilities
|
||||||
|
export function extractHostnameFromMonitor(monitor: Monitor): string | null {
|
||||||
|
if (monitor.hostname) {
|
||||||
|
return monitor.hostname.toLowerCase()
|
||||||
|
}
|
||||||
|
if (monitor.url) {
|
||||||
|
try {
|
||||||
|
const url = new URL(monitor.url.startsWith("http") ? monitor.url : `https://${monitor.url}`)
|
||||||
|
return url.hostname.toLowerCase()
|
||||||
|
} catch {
|
||||||
|
return monitor.url.toLowerCase()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getDomainFromHostname(hostname: string): string {
|
||||||
|
// Remove www prefix
|
||||||
|
const clean = hostname.replace(/^www\./, "")
|
||||||
|
// Extract root domain (last 2 parts for most domains, last 3 for co.uk etc)
|
||||||
|
const parts = clean.split(".")
|
||||||
|
if (parts.length <= 2) {
|
||||||
|
return clean
|
||||||
|
}
|
||||||
|
// Handle special TLDs
|
||||||
|
const specialTLDs = ["co.uk", "com.au", "co.jp", "com.br", "co.nz", "co.za", "co.in", "com.cn"]
|
||||||
|
const lastTwo = parts.slice(-2).join(".")
|
||||||
|
const lastThree = parts.slice(-3).join(".")
|
||||||
|
if (specialTLDs.includes(lastThree)) {
|
||||||
|
return lastThree
|
||||||
|
}
|
||||||
|
return lastTwo
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isSubdomain(hostname: string, domain: string): boolean {
|
||||||
|
const cleanHostname = hostname.toLowerCase().replace(/^www\./, "")
|
||||||
|
const cleanDomain = domain.toLowerCase().replace(/^www\./, "")
|
||||||
|
return cleanHostname.endsWith(`.${cleanDomain}`) || cleanHostname === cleanDomain
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getSubdomainPart(hostname: string, domain: string): string | null {
|
||||||
|
const cleanHostname = hostname.toLowerCase().replace(/^www\./, "")
|
||||||
|
const cleanDomain = domain.toLowerCase().replace(/^www\./, "")
|
||||||
|
if (cleanHostname === cleanDomain) {
|
||||||
|
return "@" // Root domain
|
||||||
|
}
|
||||||
|
if (cleanHostname.endsWith(`.${cleanDomain}`)) {
|
||||||
|
return cleanHostname.slice(0, -cleanDomain.length - 1)
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GroupedMonitors {
|
||||||
|
domain: string
|
||||||
|
isRootDomain: boolean
|
||||||
|
monitors: Monitor[]
|
||||||
|
subdomains: Map<string, Monitor[]>
|
||||||
|
}
|
||||||
|
|
||||||
|
export function groupMonitorsByDomain(monitors: Monitor[]): Map<string, GroupedMonitors> {
|
||||||
|
const groups = new Map<string, GroupedMonitors>()
|
||||||
|
|
||||||
|
for (const monitor of monitors) {
|
||||||
|
const hostname = extractHostnameFromMonitor(monitor)
|
||||||
|
if (!hostname) continue
|
||||||
|
|
||||||
|
const rootDomain = getDomainFromHostname(hostname)
|
||||||
|
const subdomain = getSubdomainPart(hostname, rootDomain)
|
||||||
|
|
||||||
|
if (!groups.has(rootDomain)) {
|
||||||
|
groups.set(rootDomain, {
|
||||||
|
domain: rootDomain,
|
||||||
|
isRootDomain: true,
|
||||||
|
monitors: [],
|
||||||
|
subdomains: new Map(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const group = groups.get(rootDomain)!
|
||||||
|
|
||||||
|
if (subdomain === "@" || subdomain === null) {
|
||||||
|
// Root domain monitor
|
||||||
|
group.monitors.push(monitor)
|
||||||
|
} else {
|
||||||
|
// Subdomain monitor
|
||||||
|
if (!group.subdomains.has(subdomain)) {
|
||||||
|
group.subdomains.set(subdomain, [])
|
||||||
|
}
|
||||||
|
group.subdomains.get(subdomain)!.push(monitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return groups
|
||||||
|
}
|
||||||
|
|||||||
@@ -230,6 +230,15 @@ msgstr "جميع الحاويات"
|
|||||||
msgid "All Systems"
|
msgid "All Systems"
|
||||||
msgstr "جميع الأنظمة"
|
msgstr "جميع الأنظمة"
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "All Tags"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "All Types"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "App update"
|
msgid "App update"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -465,8 +474,8 @@ msgstr ""
|
|||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "Check Now"
|
#~ msgid "Check Now"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/heartbeat.tsx
|
#: src/components/routes/settings/heartbeat.tsx
|
||||||
msgid "Check your monitoring service"
|
msgid "Check your monitoring service"
|
||||||
@@ -503,8 +512,8 @@ msgid "Close"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
#~ msgid "Columns"
|
msgid "Columns"
|
||||||
#~ msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
@@ -784,6 +793,14 @@ msgctxt "Layout display options"
|
|||||||
msgid "Display"
|
msgid "Display"
|
||||||
msgstr "عرض"
|
msgstr "عرض"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "Display"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
msgid "Display Columns"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/domain.tsx
|
#: src/components/routes/domain.tsx
|
||||||
#~ msgid "DNS Records"
|
#~ msgid "DNS Records"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
@@ -814,7 +831,6 @@ msgid "Domain and SSL expiry calendar"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/domains-table/domains-table.tsx
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Domain Monitoring"
|
msgid "Domain Monitoring"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1407,7 +1423,6 @@ msgid "Monitor updated successfully"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
msgid "Monitor websites, APIs, and services"
|
msgid "Monitor websites, APIs, and services"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -1448,6 +1463,10 @@ msgstr ""
|
|||||||
msgid "Net"
|
msgid "Net"
|
||||||
msgstr "الشبكة"
|
msgstr "الشبكة"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
#~ msgid "Network (Grouped)"
|
||||||
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system/charts/network-charts.tsx
|
#: src/components/routes/system/charts/network-charts.tsx
|
||||||
msgid "Network traffic of docker containers"
|
msgid "Network traffic of docker containers"
|
||||||
msgstr "حركة مرور الشبكة لحاويات الدوكر"
|
msgstr "حركة مرور الشبكة لحاويات الدوكر"
|
||||||
@@ -1471,8 +1490,8 @@ msgid "No"
|
|||||||
msgstr "لا"
|
msgstr "لا"
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "No data available for selected time range"
|
#~ msgid "No data available for selected time range"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/incidents.tsx
|
#: src/components/routes/incidents.tsx
|
||||||
msgid "No incidents found."
|
msgid "No incidents found."
|
||||||
@@ -1553,6 +1572,11 @@ msgstr "فتح القائمة"
|
|||||||
msgid "Optional description for this monitor"
|
msgid "Optional description for this monitor"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "Options"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/login/auth-form.tsx
|
#: src/components/login/auth-form.tsx
|
||||||
msgid "Or continue with"
|
msgid "Or continue with"
|
||||||
msgstr "أو المتابعة باستخدام"
|
msgstr "أو المتابعة باستخدام"
|
||||||
@@ -1626,6 +1650,8 @@ msgid "Paused"
|
|||||||
msgstr "متوقف مؤقتا"
|
msgstr "متوقف مؤقتا"
|
||||||
|
|
||||||
#. placeholder {0}: stats.paused
|
#. placeholder {0}: stats.paused
|
||||||
|
#. placeholder {0}: statusCounts.paused
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
msgid "Paused ({0})"
|
msgid "Paused ({0})"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1804,6 +1830,10 @@ msgstr "تم حلها"
|
|||||||
msgid "Response"
|
msgid "Response"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/routes/monitor.tsx
|
||||||
|
msgid "Response time over the selected period"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
#~ msgid "Response Times"
|
#~ msgid "Response Times"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
@@ -2004,7 +2034,6 @@ msgstr "الحالة"
|
|||||||
|
|
||||||
#: src/components/containers-table/containers-table-columns.tsx
|
#: src/components/containers-table/containers-table-columns.tsx
|
||||||
#: src/components/domains-table/domains-table.tsx
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/domains-table/domains-table.tsx
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
@@ -2016,12 +2045,15 @@ msgid "Status"
|
|||||||
msgstr "الحالة"
|
msgstr "الحالة"
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "Status and response time over the selected period"
|
#~ msgid "Status and response time over the selected period"
|
||||||
|
#~ msgstr ""
|
||||||
|
|
||||||
|
#: src/components/routes/status-pages.tsx
|
||||||
|
msgid "Status Page Manager"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
#: src/components/routes/status-pages.tsx
|
|
||||||
msgid "Status Pages"
|
msgid "Status Pages"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -2065,8 +2097,8 @@ msgid "System load averages over time"
|
|||||||
msgstr "متوسط تحميل النظام مع مرور الوقت"
|
msgstr "متوسط تحميل النظام مع مرور الوقت"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
msgid "System Monitoring"
|
#~ msgid "System Monitoring"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/systemd-table/systemd-table.tsx
|
#: src/components/systemd-table/systemd-table.tsx
|
||||||
msgid "Systemd Services"
|
msgid "Systemd Services"
|
||||||
@@ -2091,6 +2123,8 @@ msgctxt "Tabs system layout option"
|
|||||||
msgid "Tabs"
|
msgid "Tabs"
|
||||||
msgstr "تبويبات"
|
msgstr "تبويبات"
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2213,7 +2247,6 @@ msgstr ""
|
|||||||
msgid "Total: {0}"
|
msgid "Total: {0}"
|
||||||
msgstr "الإجمالي: {0}"
|
msgstr "الإجمالي: {0}"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Track domain expiry dates and DNS status"
|
msgid "Track domain expiry dates and DNS status"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2223,8 +2256,8 @@ msgid "Track domain expiry dates and watch domains for purchase"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
msgid "Track system resources, containers, and health"
|
#~ msgid "Track system resources, containers, and health"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Track uptime, response times, and service health"
|
msgid "Track uptime, response times, and service health"
|
||||||
@@ -2282,6 +2315,7 @@ msgstr "يتم التفعيل عندما يتغير الحالة بين التش
|
|||||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||||
msgstr "يتم التفعيل عندما يتجاوز استخدام أي قرص عتبة معينة"
|
msgstr "يتم التفعيل عندما يتجاوز استخدام أي قرص عتبة معينة"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/routes/settings/quiet-hours.tsx
|
#: src/components/routes/settings/quiet-hours.tsx
|
||||||
#: src/components/routes/settings/quiet-hours.tsx
|
#: src/components/routes/settings/quiet-hours.tsx
|
||||||
@@ -2416,7 +2450,6 @@ msgstr "الاستخدام"
|
|||||||
msgid "Value"
|
msgid "Value"
|
||||||
msgstr "القيمة"
|
msgstr "القيمة"
|
||||||
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "View"
|
msgid "View"
|
||||||
msgstr "عرض"
|
msgstr "عرض"
|
||||||
@@ -2436,8 +2469,8 @@ msgid "View your 200 most recent alerts."
|
|||||||
msgstr "عرض أحدث 200 تنبيه."
|
msgstr "عرض أحدث 200 تنبيه."
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "Visible Fields"
|
#~ msgid "Visible Fields"
|
||||||
msgstr "الأعمدة الظاهرة"
|
#~ msgstr "الأعمدة الظاهرة"
|
||||||
|
|
||||||
#: src/components/routes/domain.tsx
|
#: src/components/routes/domain.tsx
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
@@ -2468,7 +2501,6 @@ msgstr "عتبات التحذير"
|
|||||||
msgid "Webhook / Push notifications"
|
msgid "Webhook / Push notifications"
|
||||||
msgstr "إشعارات Webhook / Push"
|
msgstr "إشعارات Webhook / Push"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Website & Service Monitoring"
|
msgid "Website & Service Monitoring"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|||||||
@@ -230,6 +230,15 @@ msgstr "Всички контейнери"
|
|||||||
msgid "All Systems"
|
msgid "All Systems"
|
||||||
msgstr "Всички системи"
|
msgstr "Всички системи"
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "All Tags"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "All Types"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "App update"
|
msgid "App update"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -465,8 +474,8 @@ msgstr ""
|
|||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "Check Now"
|
#~ msgid "Check Now"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/heartbeat.tsx
|
#: src/components/routes/settings/heartbeat.tsx
|
||||||
msgid "Check your monitoring service"
|
msgid "Check your monitoring service"
|
||||||
@@ -503,8 +512,8 @@ msgid "Close"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
#~ msgid "Columns"
|
msgid "Columns"
|
||||||
#~ msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
@@ -784,6 +793,14 @@ msgctxt "Layout display options"
|
|||||||
msgid "Display"
|
msgid "Display"
|
||||||
msgstr "Показване"
|
msgstr "Показване"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "Display"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
msgid "Display Columns"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/domain.tsx
|
#: src/components/routes/domain.tsx
|
||||||
#~ msgid "DNS Records"
|
#~ msgid "DNS Records"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
@@ -814,7 +831,6 @@ msgid "Domain and SSL expiry calendar"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/domains-table/domains-table.tsx
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Domain Monitoring"
|
msgid "Domain Monitoring"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1407,7 +1423,6 @@ msgid "Monitor updated successfully"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
msgid "Monitor websites, APIs, and services"
|
msgid "Monitor websites, APIs, and services"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -1448,6 +1463,10 @@ msgstr ""
|
|||||||
msgid "Net"
|
msgid "Net"
|
||||||
msgstr "Мрежа"
|
msgstr "Мрежа"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
#~ msgid "Network (Grouped)"
|
||||||
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system/charts/network-charts.tsx
|
#: src/components/routes/system/charts/network-charts.tsx
|
||||||
msgid "Network traffic of docker containers"
|
msgid "Network traffic of docker containers"
|
||||||
msgstr "Мрежов трафик на docker контейнери"
|
msgstr "Мрежов трафик на docker контейнери"
|
||||||
@@ -1471,8 +1490,8 @@ msgid "No"
|
|||||||
msgstr "Не"
|
msgstr "Не"
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "No data available for selected time range"
|
#~ msgid "No data available for selected time range"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/incidents.tsx
|
#: src/components/routes/incidents.tsx
|
||||||
msgid "No incidents found."
|
msgid "No incidents found."
|
||||||
@@ -1553,6 +1572,11 @@ msgstr "Отвори менюто"
|
|||||||
msgid "Optional description for this monitor"
|
msgid "Optional description for this monitor"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "Options"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/login/auth-form.tsx
|
#: src/components/login/auth-form.tsx
|
||||||
msgid "Or continue with"
|
msgid "Or continue with"
|
||||||
msgstr "Или продължи с"
|
msgstr "Или продължи с"
|
||||||
@@ -1626,6 +1650,8 @@ msgid "Paused"
|
|||||||
msgstr "На пауза"
|
msgstr "На пауза"
|
||||||
|
|
||||||
#. placeholder {0}: stats.paused
|
#. placeholder {0}: stats.paused
|
||||||
|
#. placeholder {0}: statusCounts.paused
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
msgid "Paused ({0})"
|
msgid "Paused ({0})"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1804,6 +1830,10 @@ msgstr "Решен"
|
|||||||
msgid "Response"
|
msgid "Response"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/routes/monitor.tsx
|
||||||
|
msgid "Response time over the selected period"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
#~ msgid "Response Times"
|
#~ msgid "Response Times"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
@@ -2004,7 +2034,6 @@ msgstr "Състояние"
|
|||||||
|
|
||||||
#: src/components/containers-table/containers-table-columns.tsx
|
#: src/components/containers-table/containers-table-columns.tsx
|
||||||
#: src/components/domains-table/domains-table.tsx
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/domains-table/domains-table.tsx
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
@@ -2016,12 +2045,15 @@ msgid "Status"
|
|||||||
msgstr "Статус"
|
msgstr "Статус"
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "Status and response time over the selected period"
|
#~ msgid "Status and response time over the selected period"
|
||||||
|
#~ msgstr ""
|
||||||
|
|
||||||
|
#: src/components/routes/status-pages.tsx
|
||||||
|
msgid "Status Page Manager"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
#: src/components/routes/status-pages.tsx
|
|
||||||
msgid "Status Pages"
|
msgid "Status Pages"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -2065,8 +2097,8 @@ msgid "System load averages over time"
|
|||||||
msgstr "Средно натоварване на системата във времето"
|
msgstr "Средно натоварване на системата във времето"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
msgid "System Monitoring"
|
#~ msgid "System Monitoring"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/systemd-table/systemd-table.tsx
|
#: src/components/systemd-table/systemd-table.tsx
|
||||||
msgid "Systemd Services"
|
msgid "Systemd Services"
|
||||||
@@ -2091,6 +2123,8 @@ msgctxt "Tabs system layout option"
|
|||||||
msgid "Tabs"
|
msgid "Tabs"
|
||||||
msgstr "Табове"
|
msgstr "Табове"
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2213,7 +2247,6 @@ msgstr ""
|
|||||||
msgid "Total: {0}"
|
msgid "Total: {0}"
|
||||||
msgstr "Общо: {0}"
|
msgstr "Общо: {0}"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Track domain expiry dates and DNS status"
|
msgid "Track domain expiry dates and DNS status"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2223,8 +2256,8 @@ msgid "Track domain expiry dates and watch domains for purchase"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
msgid "Track system resources, containers, and health"
|
#~ msgid "Track system resources, containers, and health"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Track uptime, response times, and service health"
|
msgid "Track uptime, response times, and service health"
|
||||||
@@ -2282,6 +2315,7 @@ msgstr "Задейства се, когато статуса превключв
|
|||||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||||
msgstr "Задейства се, когато употребата на някой диск надивши зададен праг"
|
msgstr "Задейства се, когато употребата на някой диск надивши зададен праг"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/routes/settings/quiet-hours.tsx
|
#: src/components/routes/settings/quiet-hours.tsx
|
||||||
#: src/components/routes/settings/quiet-hours.tsx
|
#: src/components/routes/settings/quiet-hours.tsx
|
||||||
@@ -2416,7 +2450,6 @@ msgstr "Натоварване"
|
|||||||
msgid "Value"
|
msgid "Value"
|
||||||
msgstr "Стойност"
|
msgstr "Стойност"
|
||||||
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "View"
|
msgid "View"
|
||||||
msgstr "Изглед"
|
msgstr "Изглед"
|
||||||
@@ -2436,8 +2469,8 @@ msgid "View your 200 most recent alerts."
|
|||||||
msgstr "Прегледайте последните си 200 сигнала."
|
msgstr "Прегледайте последните си 200 сигнала."
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "Visible Fields"
|
#~ msgid "Visible Fields"
|
||||||
msgstr "Видими полета"
|
#~ msgstr "Видими полета"
|
||||||
|
|
||||||
#: src/components/routes/domain.tsx
|
#: src/components/routes/domain.tsx
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
@@ -2468,7 +2501,6 @@ msgstr "Прагове за предупреждение"
|
|||||||
msgid "Webhook / Push notifications"
|
msgid "Webhook / Push notifications"
|
||||||
msgstr "Webhook / Пуш нотификации"
|
msgstr "Webhook / Пуш нотификации"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Website & Service Monitoring"
|
msgid "Website & Service Monitoring"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|||||||
@@ -230,6 +230,15 @@ msgstr "Všechny kontejnery"
|
|||||||
msgid "All Systems"
|
msgid "All Systems"
|
||||||
msgstr "Všechny systémy"
|
msgstr "Všechny systémy"
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "All Tags"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "All Types"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "App update"
|
msgid "App update"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -465,8 +474,8 @@ msgstr ""
|
|||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "Check Now"
|
#~ msgid "Check Now"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/heartbeat.tsx
|
#: src/components/routes/settings/heartbeat.tsx
|
||||||
msgid "Check your monitoring service"
|
msgid "Check your monitoring service"
|
||||||
@@ -503,8 +512,8 @@ msgid "Close"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
#~ msgid "Columns"
|
msgid "Columns"
|
||||||
#~ msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
@@ -784,6 +793,14 @@ msgctxt "Layout display options"
|
|||||||
msgid "Display"
|
msgid "Display"
|
||||||
msgstr "Zobrazení"
|
msgstr "Zobrazení"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "Display"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
msgid "Display Columns"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/domain.tsx
|
#: src/components/routes/domain.tsx
|
||||||
#~ msgid "DNS Records"
|
#~ msgid "DNS Records"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
@@ -814,7 +831,6 @@ msgid "Domain and SSL expiry calendar"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/domains-table/domains-table.tsx
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Domain Monitoring"
|
msgid "Domain Monitoring"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1407,7 +1423,6 @@ msgid "Monitor updated successfully"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
msgid "Monitor websites, APIs, and services"
|
msgid "Monitor websites, APIs, and services"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -1448,6 +1463,10 @@ msgstr ""
|
|||||||
msgid "Net"
|
msgid "Net"
|
||||||
msgstr "Síť"
|
msgstr "Síť"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
#~ msgid "Network (Grouped)"
|
||||||
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system/charts/network-charts.tsx
|
#: src/components/routes/system/charts/network-charts.tsx
|
||||||
msgid "Network traffic of docker containers"
|
msgid "Network traffic of docker containers"
|
||||||
msgstr "Síťový provoz kontejnerů docker"
|
msgstr "Síťový provoz kontejnerů docker"
|
||||||
@@ -1471,8 +1490,8 @@ msgid "No"
|
|||||||
msgstr "Ne"
|
msgstr "Ne"
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "No data available for selected time range"
|
#~ msgid "No data available for selected time range"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/incidents.tsx
|
#: src/components/routes/incidents.tsx
|
||||||
msgid "No incidents found."
|
msgid "No incidents found."
|
||||||
@@ -1553,6 +1572,11 @@ msgstr "Otevřít menu"
|
|||||||
msgid "Optional description for this monitor"
|
msgid "Optional description for this monitor"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "Options"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/login/auth-form.tsx
|
#: src/components/login/auth-form.tsx
|
||||||
msgid "Or continue with"
|
msgid "Or continue with"
|
||||||
msgstr "Nebo pokračujte s"
|
msgstr "Nebo pokračujte s"
|
||||||
@@ -1626,6 +1650,8 @@ msgid "Paused"
|
|||||||
msgstr "Pozastaveno"
|
msgstr "Pozastaveno"
|
||||||
|
|
||||||
#. placeholder {0}: stats.paused
|
#. placeholder {0}: stats.paused
|
||||||
|
#. placeholder {0}: statusCounts.paused
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
msgid "Paused ({0})"
|
msgid "Paused ({0})"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1804,6 +1830,10 @@ msgstr "Vyřešeno"
|
|||||||
msgid "Response"
|
msgid "Response"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/routes/monitor.tsx
|
||||||
|
msgid "Response time over the selected period"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
#~ msgid "Response Times"
|
#~ msgid "Response Times"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
@@ -2004,7 +2034,6 @@ msgstr "Stav"
|
|||||||
|
|
||||||
#: src/components/containers-table/containers-table-columns.tsx
|
#: src/components/containers-table/containers-table-columns.tsx
|
||||||
#: src/components/domains-table/domains-table.tsx
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/domains-table/domains-table.tsx
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
@@ -2016,12 +2045,15 @@ msgid "Status"
|
|||||||
msgstr "Stav"
|
msgstr "Stav"
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "Status and response time over the selected period"
|
#~ msgid "Status and response time over the selected period"
|
||||||
|
#~ msgstr ""
|
||||||
|
|
||||||
|
#: src/components/routes/status-pages.tsx
|
||||||
|
msgid "Status Page Manager"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
#: src/components/routes/status-pages.tsx
|
|
||||||
msgid "Status Pages"
|
msgid "Status Pages"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -2065,8 +2097,8 @@ msgid "System load averages over time"
|
|||||||
msgstr "Průměry zatížení systému v průběhu času"
|
msgstr "Průměry zatížení systému v průběhu času"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
msgid "System Monitoring"
|
#~ msgid "System Monitoring"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/systemd-table/systemd-table.tsx
|
#: src/components/systemd-table/systemd-table.tsx
|
||||||
msgid "Systemd Services"
|
msgid "Systemd Services"
|
||||||
@@ -2091,6 +2123,8 @@ msgctxt "Tabs system layout option"
|
|||||||
msgid "Tabs"
|
msgid "Tabs"
|
||||||
msgstr "Karty"
|
msgstr "Karty"
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2213,7 +2247,6 @@ msgstr ""
|
|||||||
msgid "Total: {0}"
|
msgid "Total: {0}"
|
||||||
msgstr "Celkem: {0}"
|
msgstr "Celkem: {0}"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Track domain expiry dates and DNS status"
|
msgid "Track domain expiry dates and DNS status"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2223,8 +2256,8 @@ msgid "Track domain expiry dates and watch domains for purchase"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
msgid "Track system resources, containers, and health"
|
#~ msgid "Track system resources, containers, and health"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Track uptime, response times, and service health"
|
msgid "Track uptime, response times, and service health"
|
||||||
@@ -2282,6 +2315,7 @@ msgstr "Spouští se, když se změní dostupnost"
|
|||||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||||
msgstr "Spustí se, když využití disku překročí prahovou hodnotu"
|
msgstr "Spustí se, když využití disku překročí prahovou hodnotu"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/routes/settings/quiet-hours.tsx
|
#: src/components/routes/settings/quiet-hours.tsx
|
||||||
#: src/components/routes/settings/quiet-hours.tsx
|
#: src/components/routes/settings/quiet-hours.tsx
|
||||||
@@ -2416,7 +2450,6 @@ msgstr "Využití"
|
|||||||
msgid "Value"
|
msgid "Value"
|
||||||
msgstr "Hodnota"
|
msgstr "Hodnota"
|
||||||
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "View"
|
msgid "View"
|
||||||
msgstr "Zobrazení"
|
msgstr "Zobrazení"
|
||||||
@@ -2436,8 +2469,8 @@ msgid "View your 200 most recent alerts."
|
|||||||
msgstr "Zobrazit vašich 200 nejnovějších upozornění."
|
msgstr "Zobrazit vašich 200 nejnovějších upozornění."
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "Visible Fields"
|
#~ msgid "Visible Fields"
|
||||||
msgstr "Viditelné sloupce"
|
#~ msgstr "Viditelné sloupce"
|
||||||
|
|
||||||
#: src/components/routes/domain.tsx
|
#: src/components/routes/domain.tsx
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
@@ -2468,7 +2501,6 @@ msgstr "Prahové hodnoty pro varování"
|
|||||||
msgid "Webhook / Push notifications"
|
msgid "Webhook / Push notifications"
|
||||||
msgstr "Webhook / Push oznámení"
|
msgstr "Webhook / Push oznámení"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Website & Service Monitoring"
|
msgid "Website & Service Monitoring"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|||||||
@@ -230,6 +230,15 @@ msgstr "Alle containere"
|
|||||||
msgid "All Systems"
|
msgid "All Systems"
|
||||||
msgstr "Alle systemer"
|
msgstr "Alle systemer"
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "All Tags"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "All Types"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "App update"
|
msgid "App update"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -465,8 +474,8 @@ msgstr ""
|
|||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "Check Now"
|
#~ msgid "Check Now"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/heartbeat.tsx
|
#: src/components/routes/settings/heartbeat.tsx
|
||||||
msgid "Check your monitoring service"
|
msgid "Check your monitoring service"
|
||||||
@@ -503,8 +512,8 @@ msgid "Close"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
#~ msgid "Columns"
|
msgid "Columns"
|
||||||
#~ msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
@@ -784,6 +793,14 @@ msgctxt "Layout display options"
|
|||||||
msgid "Display"
|
msgid "Display"
|
||||||
msgstr "Visning"
|
msgstr "Visning"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "Display"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
msgid "Display Columns"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/domain.tsx
|
#: src/components/routes/domain.tsx
|
||||||
#~ msgid "DNS Records"
|
#~ msgid "DNS Records"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
@@ -814,7 +831,6 @@ msgid "Domain and SSL expiry calendar"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/domains-table/domains-table.tsx
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Domain Monitoring"
|
msgid "Domain Monitoring"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1407,7 +1423,6 @@ msgid "Monitor updated successfully"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
msgid "Monitor websites, APIs, and services"
|
msgid "Monitor websites, APIs, and services"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -1448,6 +1463,10 @@ msgstr ""
|
|||||||
msgid "Net"
|
msgid "Net"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
#~ msgid "Network (Grouped)"
|
||||||
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system/charts/network-charts.tsx
|
#: src/components/routes/system/charts/network-charts.tsx
|
||||||
msgid "Network traffic of docker containers"
|
msgid "Network traffic of docker containers"
|
||||||
msgstr "Netværkstrafik af dockercontainere"
|
msgstr "Netværkstrafik af dockercontainere"
|
||||||
@@ -1471,8 +1490,8 @@ msgid "No"
|
|||||||
msgstr "Nej"
|
msgstr "Nej"
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "No data available for selected time range"
|
#~ msgid "No data available for selected time range"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/incidents.tsx
|
#: src/components/routes/incidents.tsx
|
||||||
msgid "No incidents found."
|
msgid "No incidents found."
|
||||||
@@ -1553,6 +1572,11 @@ msgstr "Åbn menu"
|
|||||||
msgid "Optional description for this monitor"
|
msgid "Optional description for this monitor"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "Options"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/login/auth-form.tsx
|
#: src/components/login/auth-form.tsx
|
||||||
msgid "Or continue with"
|
msgid "Or continue with"
|
||||||
msgstr "Eller fortsæt med"
|
msgstr "Eller fortsæt med"
|
||||||
@@ -1626,6 +1650,8 @@ msgid "Paused"
|
|||||||
msgstr "Sat på pause"
|
msgstr "Sat på pause"
|
||||||
|
|
||||||
#. placeholder {0}: stats.paused
|
#. placeholder {0}: stats.paused
|
||||||
|
#. placeholder {0}: statusCounts.paused
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
msgid "Paused ({0})"
|
msgid "Paused ({0})"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1804,6 +1830,10 @@ msgstr "Løst"
|
|||||||
msgid "Response"
|
msgid "Response"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/routes/monitor.tsx
|
||||||
|
msgid "Response time over the selected period"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
#~ msgid "Response Times"
|
#~ msgid "Response Times"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
@@ -2004,7 +2034,6 @@ msgstr "Tilstand"
|
|||||||
|
|
||||||
#: src/components/containers-table/containers-table-columns.tsx
|
#: src/components/containers-table/containers-table-columns.tsx
|
||||||
#: src/components/domains-table/domains-table.tsx
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/domains-table/domains-table.tsx
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
@@ -2016,12 +2045,15 @@ msgid "Status"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "Status and response time over the selected period"
|
#~ msgid "Status and response time over the selected period"
|
||||||
|
#~ msgstr ""
|
||||||
|
|
||||||
|
#: src/components/routes/status-pages.tsx
|
||||||
|
msgid "Status Page Manager"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
#: src/components/routes/status-pages.tsx
|
|
||||||
msgid "Status Pages"
|
msgid "Status Pages"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -2065,8 +2097,8 @@ msgid "System load averages over time"
|
|||||||
msgstr "Gennemsnitlig system belastning over tid"
|
msgstr "Gennemsnitlig system belastning over tid"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
msgid "System Monitoring"
|
#~ msgid "System Monitoring"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/systemd-table/systemd-table.tsx
|
#: src/components/systemd-table/systemd-table.tsx
|
||||||
msgid "Systemd Services"
|
msgid "Systemd Services"
|
||||||
@@ -2091,6 +2123,8 @@ msgctxt "Tabs system layout option"
|
|||||||
msgid "Tabs"
|
msgid "Tabs"
|
||||||
msgstr "Faner"
|
msgstr "Faner"
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2213,7 +2247,6 @@ msgstr ""
|
|||||||
msgid "Total: {0}"
|
msgid "Total: {0}"
|
||||||
msgstr "I alt: {0}"
|
msgstr "I alt: {0}"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Track domain expiry dates and DNS status"
|
msgid "Track domain expiry dates and DNS status"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2223,8 +2256,8 @@ msgid "Track domain expiry dates and watch domains for purchase"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
msgid "Track system resources, containers, and health"
|
#~ msgid "Track system resources, containers, and health"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Track uptime, response times, and service health"
|
msgid "Track uptime, response times, and service health"
|
||||||
@@ -2282,6 +2315,7 @@ msgstr "Udløser når status skifter mellem op og ned"
|
|||||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||||
msgstr "Udløser når brugen af en disk overstiger en tærskel"
|
msgstr "Udløser når brugen af en disk overstiger en tærskel"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/routes/settings/quiet-hours.tsx
|
#: src/components/routes/settings/quiet-hours.tsx
|
||||||
#: src/components/routes/settings/quiet-hours.tsx
|
#: src/components/routes/settings/quiet-hours.tsx
|
||||||
@@ -2416,7 +2450,6 @@ msgstr "Udnyttelse"
|
|||||||
msgid "Value"
|
msgid "Value"
|
||||||
msgstr "Værdi"
|
msgstr "Værdi"
|
||||||
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "View"
|
msgid "View"
|
||||||
msgstr "Vis"
|
msgstr "Vis"
|
||||||
@@ -2436,8 +2469,8 @@ msgid "View your 200 most recent alerts."
|
|||||||
msgstr "Se dine 200 nyeste alarmer."
|
msgstr "Se dine 200 nyeste alarmer."
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "Visible Fields"
|
#~ msgid "Visible Fields"
|
||||||
msgstr "Synlige felter"
|
#~ msgstr "Synlige felter"
|
||||||
|
|
||||||
#: src/components/routes/domain.tsx
|
#: src/components/routes/domain.tsx
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
@@ -2468,7 +2501,6 @@ msgstr "Advarselstærskler"
|
|||||||
msgid "Webhook / Push notifications"
|
msgid "Webhook / Push notifications"
|
||||||
msgstr "Webhook / Push notifikationer"
|
msgstr "Webhook / Push notifikationer"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Website & Service Monitoring"
|
msgid "Website & Service Monitoring"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|||||||
@@ -230,6 +230,15 @@ msgstr "Alle Container"
|
|||||||
msgid "All Systems"
|
msgid "All Systems"
|
||||||
msgstr "Alle Systeme"
|
msgstr "Alle Systeme"
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "All Tags"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "All Types"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "App update"
|
msgid "App update"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -465,8 +474,8 @@ msgstr ""
|
|||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "Check Now"
|
#~ msgid "Check Now"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/heartbeat.tsx
|
#: src/components/routes/settings/heartbeat.tsx
|
||||||
msgid "Check your monitoring service"
|
msgid "Check your monitoring service"
|
||||||
@@ -503,8 +512,8 @@ msgid "Close"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
#~ msgid "Columns"
|
msgid "Columns"
|
||||||
#~ msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
@@ -784,6 +793,14 @@ msgctxt "Layout display options"
|
|||||||
msgid "Display"
|
msgid "Display"
|
||||||
msgstr "Anzeige"
|
msgstr "Anzeige"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "Display"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
msgid "Display Columns"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/domain.tsx
|
#: src/components/routes/domain.tsx
|
||||||
#~ msgid "DNS Records"
|
#~ msgid "DNS Records"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
@@ -814,7 +831,6 @@ msgid "Domain and SSL expiry calendar"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/domains-table/domains-table.tsx
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Domain Monitoring"
|
msgid "Domain Monitoring"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1407,7 +1423,6 @@ msgid "Monitor updated successfully"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
msgid "Monitor websites, APIs, and services"
|
msgid "Monitor websites, APIs, and services"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -1448,6 +1463,10 @@ msgstr ""
|
|||||||
msgid "Net"
|
msgid "Net"
|
||||||
msgstr "Netzwerk"
|
msgstr "Netzwerk"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
#~ msgid "Network (Grouped)"
|
||||||
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system/charts/network-charts.tsx
|
#: src/components/routes/system/charts/network-charts.tsx
|
||||||
msgid "Network traffic of docker containers"
|
msgid "Network traffic of docker containers"
|
||||||
msgstr "Netzwerkverkehr der Docker-Container"
|
msgstr "Netzwerkverkehr der Docker-Container"
|
||||||
@@ -1471,8 +1490,8 @@ msgid "No"
|
|||||||
msgstr "Nein"
|
msgstr "Nein"
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "No data available for selected time range"
|
#~ msgid "No data available for selected time range"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/incidents.tsx
|
#: src/components/routes/incidents.tsx
|
||||||
msgid "No incidents found."
|
msgid "No incidents found."
|
||||||
@@ -1553,6 +1572,11 @@ msgstr "Menü öffnen"
|
|||||||
msgid "Optional description for this monitor"
|
msgid "Optional description for this monitor"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "Options"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/login/auth-form.tsx
|
#: src/components/login/auth-form.tsx
|
||||||
msgid "Or continue with"
|
msgid "Or continue with"
|
||||||
msgstr "Oder fortfahren mit"
|
msgstr "Oder fortfahren mit"
|
||||||
@@ -1626,6 +1650,8 @@ msgid "Paused"
|
|||||||
msgstr "Pausiert"
|
msgstr "Pausiert"
|
||||||
|
|
||||||
#. placeholder {0}: stats.paused
|
#. placeholder {0}: stats.paused
|
||||||
|
#. placeholder {0}: statusCounts.paused
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
msgid "Paused ({0})"
|
msgid "Paused ({0})"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1804,6 +1830,10 @@ msgstr "Gelöst"
|
|||||||
msgid "Response"
|
msgid "Response"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/routes/monitor.tsx
|
||||||
|
msgid "Response time over the selected period"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
#~ msgid "Response Times"
|
#~ msgid "Response Times"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
@@ -2004,7 +2034,6 @@ msgstr "Status"
|
|||||||
|
|
||||||
#: src/components/containers-table/containers-table-columns.tsx
|
#: src/components/containers-table/containers-table-columns.tsx
|
||||||
#: src/components/domains-table/domains-table.tsx
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/domains-table/domains-table.tsx
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
@@ -2016,12 +2045,15 @@ msgid "Status"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "Status and response time over the selected period"
|
#~ msgid "Status and response time over the selected period"
|
||||||
|
#~ msgstr ""
|
||||||
|
|
||||||
|
#: src/components/routes/status-pages.tsx
|
||||||
|
msgid "Status Page Manager"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
#: src/components/routes/status-pages.tsx
|
|
||||||
msgid "Status Pages"
|
msgid "Status Pages"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -2065,8 +2097,8 @@ msgid "System load averages over time"
|
|||||||
msgstr "Systemlastdurchschnitt im Zeitverlauf"
|
msgstr "Systemlastdurchschnitt im Zeitverlauf"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
msgid "System Monitoring"
|
#~ msgid "System Monitoring"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/systemd-table/systemd-table.tsx
|
#: src/components/systemd-table/systemd-table.tsx
|
||||||
msgid "Systemd Services"
|
msgid "Systemd Services"
|
||||||
@@ -2091,6 +2123,8 @@ msgctxt "Tabs system layout option"
|
|||||||
msgid "Tabs"
|
msgid "Tabs"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2213,7 +2247,6 @@ msgstr "Gesamtzeit für Lese-/Schreibvorgänge (kann 100% überschreiten)"
|
|||||||
msgid "Total: {0}"
|
msgid "Total: {0}"
|
||||||
msgstr "Gesamt: {0}"
|
msgstr "Gesamt: {0}"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Track domain expiry dates and DNS status"
|
msgid "Track domain expiry dates and DNS status"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2223,8 +2256,8 @@ msgid "Track domain expiry dates and watch domains for purchase"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
msgid "Track system resources, containers, and health"
|
#~ msgid "Track system resources, containers, and health"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Track uptime, response times, and service health"
|
msgid "Track uptime, response times, and service health"
|
||||||
@@ -2282,6 +2315,7 @@ msgstr "Löst aus, wenn der Status zwischen online und offline wechselt"
|
|||||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||||
msgstr "Löst aus, wenn die Nutzung einer Festplatte einen Schwellenwert überschreitet"
|
msgstr "Löst aus, wenn die Nutzung einer Festplatte einen Schwellenwert überschreitet"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/routes/settings/quiet-hours.tsx
|
#: src/components/routes/settings/quiet-hours.tsx
|
||||||
#: src/components/routes/settings/quiet-hours.tsx
|
#: src/components/routes/settings/quiet-hours.tsx
|
||||||
@@ -2416,7 +2450,6 @@ msgstr "Auslastung"
|
|||||||
msgid "Value"
|
msgid "Value"
|
||||||
msgstr "Wert"
|
msgstr "Wert"
|
||||||
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "View"
|
msgid "View"
|
||||||
msgstr "Ansicht"
|
msgstr "Ansicht"
|
||||||
@@ -2436,8 +2469,8 @@ msgid "View your 200 most recent alerts."
|
|||||||
msgstr "Sieh dir die neusten 200 Alarme an."
|
msgstr "Sieh dir die neusten 200 Alarme an."
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "Visible Fields"
|
#~ msgid "Visible Fields"
|
||||||
msgstr "Sichtbare Spalten"
|
#~ msgstr "Sichtbare Spalten"
|
||||||
|
|
||||||
#: src/components/routes/domain.tsx
|
#: src/components/routes/domain.tsx
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
@@ -2468,7 +2501,6 @@ msgstr "Warnschwellen"
|
|||||||
msgid "Webhook / Push notifications"
|
msgid "Webhook / Push notifications"
|
||||||
msgstr "Webhook / Push-Benachrichtigungen"
|
msgstr "Webhook / Push-Benachrichtigungen"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Website & Service Monitoring"
|
msgid "Website & Service Monitoring"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|||||||
@@ -225,6 +225,15 @@ msgstr "All Containers"
|
|||||||
msgid "All Systems"
|
msgid "All Systems"
|
||||||
msgstr "All Systems"
|
msgstr "All Systems"
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "All Tags"
|
||||||
|
msgstr "All Tags"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "All Types"
|
||||||
|
msgstr "All Types"
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "App update"
|
msgid "App update"
|
||||||
msgstr "App update"
|
msgstr "App update"
|
||||||
@@ -460,8 +469,8 @@ msgstr "Check now"
|
|||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "Check Now"
|
#~ msgid "Check Now"
|
||||||
msgstr "Check Now"
|
#~ msgstr "Check Now"
|
||||||
|
|
||||||
#: src/components/routes/settings/heartbeat.tsx
|
#: src/components/routes/settings/heartbeat.tsx
|
||||||
msgid "Check your monitoring service"
|
msgid "Check your monitoring service"
|
||||||
@@ -498,8 +507,8 @@ msgid "Close"
|
|||||||
msgstr "Close"
|
msgstr "Close"
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
#~ msgid "Columns"
|
msgid "Columns"
|
||||||
#~ msgstr "Columns"
|
msgstr "Columns"
|
||||||
|
|
||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
@@ -779,6 +788,14 @@ msgctxt "Layout display options"
|
|||||||
msgid "Display"
|
msgid "Display"
|
||||||
msgstr "Display"
|
msgstr "Display"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "Display"
|
||||||
|
msgstr "Display"
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
msgid "Display Columns"
|
||||||
|
msgstr "Display Columns"
|
||||||
|
|
||||||
#: src/components/routes/domain.tsx
|
#: src/components/routes/domain.tsx
|
||||||
#~ msgid "DNS Records"
|
#~ msgid "DNS Records"
|
||||||
#~ msgstr "DNS Records"
|
#~ msgstr "DNS Records"
|
||||||
@@ -809,7 +826,6 @@ msgid "Domain and SSL expiry calendar"
|
|||||||
msgstr "Domain and SSL expiry calendar"
|
msgstr "Domain and SSL expiry calendar"
|
||||||
|
|
||||||
#: src/components/domains-table/domains-table.tsx
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Domain Monitoring"
|
msgid "Domain Monitoring"
|
||||||
msgstr "Domain Monitoring"
|
msgstr "Domain Monitoring"
|
||||||
@@ -1402,7 +1418,6 @@ msgid "Monitor updated successfully"
|
|||||||
msgstr "Monitor updated successfully"
|
msgstr "Monitor updated successfully"
|
||||||
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
msgid "Monitor websites, APIs, and services"
|
msgid "Monitor websites, APIs, and services"
|
||||||
msgstr "Monitor websites, APIs, and services"
|
msgstr "Monitor websites, APIs, and services"
|
||||||
|
|
||||||
@@ -1443,6 +1458,10 @@ msgstr "Name is required"
|
|||||||
msgid "Net"
|
msgid "Net"
|
||||||
msgstr "Net"
|
msgstr "Net"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
#~ msgid "Network (Grouped)"
|
||||||
|
#~ msgstr "Network (Grouped)"
|
||||||
|
|
||||||
#: src/components/routes/system/charts/network-charts.tsx
|
#: src/components/routes/system/charts/network-charts.tsx
|
||||||
msgid "Network traffic of docker containers"
|
msgid "Network traffic of docker containers"
|
||||||
msgstr "Network traffic of docker containers"
|
msgstr "Network traffic of docker containers"
|
||||||
@@ -1466,8 +1485,8 @@ msgid "No"
|
|||||||
msgstr "No"
|
msgstr "No"
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "No data available for selected time range"
|
#~ msgid "No data available for selected time range"
|
||||||
msgstr "No data available for selected time range"
|
#~ msgstr "No data available for selected time range"
|
||||||
|
|
||||||
#: src/components/routes/incidents.tsx
|
#: src/components/routes/incidents.tsx
|
||||||
msgid "No incidents found."
|
msgid "No incidents found."
|
||||||
@@ -1548,6 +1567,11 @@ msgstr "Open menu"
|
|||||||
msgid "Optional description for this monitor"
|
msgid "Optional description for this monitor"
|
||||||
msgstr "Optional description for this monitor"
|
msgstr "Optional description for this monitor"
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "Options"
|
||||||
|
msgstr "Options"
|
||||||
|
|
||||||
#: src/components/login/auth-form.tsx
|
#: src/components/login/auth-form.tsx
|
||||||
msgid "Or continue with"
|
msgid "Or continue with"
|
||||||
msgstr "Or continue with"
|
msgstr "Or continue with"
|
||||||
@@ -1621,6 +1645,8 @@ msgid "Paused"
|
|||||||
msgstr "Paused"
|
msgstr "Paused"
|
||||||
|
|
||||||
#. placeholder {0}: stats.paused
|
#. placeholder {0}: stats.paused
|
||||||
|
#. placeholder {0}: statusCounts.paused
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
msgid "Paused ({0})"
|
msgid "Paused ({0})"
|
||||||
msgstr "Paused ({0})"
|
msgstr "Paused ({0})"
|
||||||
@@ -1799,6 +1825,10 @@ msgstr "Resolved"
|
|||||||
msgid "Response"
|
msgid "Response"
|
||||||
msgstr "Response"
|
msgstr "Response"
|
||||||
|
|
||||||
|
#: src/components/routes/monitor.tsx
|
||||||
|
msgid "Response time over the selected period"
|
||||||
|
msgstr "Response time over the selected period"
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
#~ msgid "Response Times"
|
#~ msgid "Response Times"
|
||||||
#~ msgstr "Response Times"
|
#~ msgstr "Response Times"
|
||||||
@@ -1999,7 +2029,6 @@ msgstr "State"
|
|||||||
|
|
||||||
#: src/components/containers-table/containers-table-columns.tsx
|
#: src/components/containers-table/containers-table-columns.tsx
|
||||||
#: src/components/domains-table/domains-table.tsx
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/domains-table/domains-table.tsx
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
@@ -2011,12 +2040,15 @@ msgid "Status"
|
|||||||
msgstr "Status"
|
msgstr "Status"
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "Status and response time over the selected period"
|
#~ msgid "Status and response time over the selected period"
|
||||||
msgstr "Status and response time over the selected period"
|
#~ msgstr "Status and response time over the selected period"
|
||||||
|
|
||||||
|
#: src/components/routes/status-pages.tsx
|
||||||
|
msgid "Status Page Manager"
|
||||||
|
msgstr "Status Page Manager"
|
||||||
|
|
||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
#: src/components/routes/status-pages.tsx
|
|
||||||
msgid "Status Pages"
|
msgid "Status Pages"
|
||||||
msgstr "Status Pages"
|
msgstr "Status Pages"
|
||||||
|
|
||||||
@@ -2060,8 +2092,8 @@ msgid "System load averages over time"
|
|||||||
msgstr "System load averages over time"
|
msgstr "System load averages over time"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
msgid "System Monitoring"
|
#~ msgid "System Monitoring"
|
||||||
msgstr "System Monitoring"
|
#~ msgstr "System Monitoring"
|
||||||
|
|
||||||
#: src/components/systemd-table/systemd-table.tsx
|
#: src/components/systemd-table/systemd-table.tsx
|
||||||
msgid "Systemd Services"
|
msgid "Systemd Services"
|
||||||
@@ -2086,6 +2118,8 @@ msgctxt "Tabs system layout option"
|
|||||||
msgid "Tabs"
|
msgid "Tabs"
|
||||||
msgstr "Tabs"
|
msgstr "Tabs"
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr "Tags"
|
msgstr "Tags"
|
||||||
@@ -2208,7 +2242,6 @@ msgstr "Total time spent on read/write (can exceed 100%)"
|
|||||||
msgid "Total: {0}"
|
msgid "Total: {0}"
|
||||||
msgstr "Total: {0}"
|
msgstr "Total: {0}"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Track domain expiry dates and DNS status"
|
msgid "Track domain expiry dates and DNS status"
|
||||||
msgstr "Track domain expiry dates and DNS status"
|
msgstr "Track domain expiry dates and DNS status"
|
||||||
@@ -2218,8 +2251,8 @@ msgid "Track domain expiry dates and watch domains for purchase"
|
|||||||
msgstr "Track domain expiry dates and watch domains for purchase"
|
msgstr "Track domain expiry dates and watch domains for purchase"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
msgid "Track system resources, containers, and health"
|
#~ msgid "Track system resources, containers, and health"
|
||||||
msgstr "Track system resources, containers, and health"
|
#~ msgstr "Track system resources, containers, and health"
|
||||||
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Track uptime, response times, and service health"
|
msgid "Track uptime, response times, and service health"
|
||||||
@@ -2277,6 +2310,7 @@ msgstr "Triggers when status switches between up and down"
|
|||||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||||
msgstr "Triggers when usage of any disk exceeds a threshold"
|
msgstr "Triggers when usage of any disk exceeds a threshold"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/routes/settings/quiet-hours.tsx
|
#: src/components/routes/settings/quiet-hours.tsx
|
||||||
#: src/components/routes/settings/quiet-hours.tsx
|
#: src/components/routes/settings/quiet-hours.tsx
|
||||||
@@ -2411,7 +2445,6 @@ msgstr "Utilization"
|
|||||||
msgid "Value"
|
msgid "Value"
|
||||||
msgstr "Value"
|
msgstr "Value"
|
||||||
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "View"
|
msgid "View"
|
||||||
msgstr "View"
|
msgstr "View"
|
||||||
@@ -2431,8 +2464,8 @@ msgid "View your 200 most recent alerts."
|
|||||||
msgstr "View your 200 most recent alerts."
|
msgstr "View your 200 most recent alerts."
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "Visible Fields"
|
#~ msgid "Visible Fields"
|
||||||
msgstr "Visible Fields"
|
#~ msgstr "Visible Fields"
|
||||||
|
|
||||||
#: src/components/routes/domain.tsx
|
#: src/components/routes/domain.tsx
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
@@ -2463,7 +2496,6 @@ msgstr "Warning thresholds"
|
|||||||
msgid "Webhook / Push notifications"
|
msgid "Webhook / Push notifications"
|
||||||
msgstr "Webhook / Push notifications"
|
msgstr "Webhook / Push notifications"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Website & Service Monitoring"
|
msgid "Website & Service Monitoring"
|
||||||
msgstr "Website & Service Monitoring"
|
msgstr "Website & Service Monitoring"
|
||||||
|
|||||||
@@ -230,6 +230,15 @@ msgstr "Todos los contenedores"
|
|||||||
msgid "All Systems"
|
msgid "All Systems"
|
||||||
msgstr "Todos los sistemas"
|
msgstr "Todos los sistemas"
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "All Tags"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "All Types"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "App update"
|
msgid "App update"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -465,8 +474,8 @@ msgstr ""
|
|||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "Check Now"
|
#~ msgid "Check Now"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/heartbeat.tsx
|
#: src/components/routes/settings/heartbeat.tsx
|
||||||
msgid "Check your monitoring service"
|
msgid "Check your monitoring service"
|
||||||
@@ -503,8 +512,8 @@ msgid "Close"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
#~ msgid "Columns"
|
msgid "Columns"
|
||||||
#~ msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
@@ -784,6 +793,14 @@ msgctxt "Layout display options"
|
|||||||
msgid "Display"
|
msgid "Display"
|
||||||
msgstr "Pantalla"
|
msgstr "Pantalla"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "Display"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
msgid "Display Columns"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/domain.tsx
|
#: src/components/routes/domain.tsx
|
||||||
#~ msgid "DNS Records"
|
#~ msgid "DNS Records"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
@@ -814,7 +831,6 @@ msgid "Domain and SSL expiry calendar"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/domains-table/domains-table.tsx
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Domain Monitoring"
|
msgid "Domain Monitoring"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1407,7 +1423,6 @@ msgid "Monitor updated successfully"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
msgid "Monitor websites, APIs, and services"
|
msgid "Monitor websites, APIs, and services"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -1448,6 +1463,10 @@ msgstr ""
|
|||||||
msgid "Net"
|
msgid "Net"
|
||||||
msgstr "Red"
|
msgstr "Red"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
#~ msgid "Network (Grouped)"
|
||||||
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system/charts/network-charts.tsx
|
#: src/components/routes/system/charts/network-charts.tsx
|
||||||
msgid "Network traffic of docker containers"
|
msgid "Network traffic of docker containers"
|
||||||
msgstr "Tráfico de red de los contenedores Docker"
|
msgstr "Tráfico de red de los contenedores Docker"
|
||||||
@@ -1471,8 +1490,8 @@ msgid "No"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "No data available for selected time range"
|
#~ msgid "No data available for selected time range"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/incidents.tsx
|
#: src/components/routes/incidents.tsx
|
||||||
msgid "No incidents found."
|
msgid "No incidents found."
|
||||||
@@ -1553,6 +1572,11 @@ msgstr "Abrir menú"
|
|||||||
msgid "Optional description for this monitor"
|
msgid "Optional description for this monitor"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "Options"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/login/auth-form.tsx
|
#: src/components/login/auth-form.tsx
|
||||||
msgid "Or continue with"
|
msgid "Or continue with"
|
||||||
msgstr "O continuar con"
|
msgstr "O continuar con"
|
||||||
@@ -1626,6 +1650,8 @@ msgid "Paused"
|
|||||||
msgstr "Pausado"
|
msgstr "Pausado"
|
||||||
|
|
||||||
#. placeholder {0}: stats.paused
|
#. placeholder {0}: stats.paused
|
||||||
|
#. placeholder {0}: statusCounts.paused
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
msgid "Paused ({0})"
|
msgid "Paused ({0})"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1804,6 +1830,10 @@ msgstr "Resuelto"
|
|||||||
msgid "Response"
|
msgid "Response"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/routes/monitor.tsx
|
||||||
|
msgid "Response time over the selected period"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
#~ msgid "Response Times"
|
#~ msgid "Response Times"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
@@ -2004,7 +2034,6 @@ msgstr "Estado"
|
|||||||
|
|
||||||
#: src/components/containers-table/containers-table-columns.tsx
|
#: src/components/containers-table/containers-table-columns.tsx
|
||||||
#: src/components/domains-table/domains-table.tsx
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/domains-table/domains-table.tsx
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
@@ -2016,12 +2045,15 @@ msgid "Status"
|
|||||||
msgstr "Estado"
|
msgstr "Estado"
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "Status and response time over the selected period"
|
#~ msgid "Status and response time over the selected period"
|
||||||
|
#~ msgstr ""
|
||||||
|
|
||||||
|
#: src/components/routes/status-pages.tsx
|
||||||
|
msgid "Status Page Manager"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
#: src/components/routes/status-pages.tsx
|
|
||||||
msgid "Status Pages"
|
msgid "Status Pages"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -2065,8 +2097,8 @@ msgid "System load averages over time"
|
|||||||
msgstr "Promedios de carga del sistema a lo largo del tiempo"
|
msgstr "Promedios de carga del sistema a lo largo del tiempo"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
msgid "System Monitoring"
|
#~ msgid "System Monitoring"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/systemd-table/systemd-table.tsx
|
#: src/components/systemd-table/systemd-table.tsx
|
||||||
msgid "Systemd Services"
|
msgid "Systemd Services"
|
||||||
@@ -2091,6 +2123,8 @@ msgctxt "Tabs system layout option"
|
|||||||
msgid "Tabs"
|
msgid "Tabs"
|
||||||
msgstr "Pestañas"
|
msgstr "Pestañas"
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2213,7 +2247,6 @@ msgstr ""
|
|||||||
msgid "Total: {0}"
|
msgid "Total: {0}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Track domain expiry dates and DNS status"
|
msgid "Track domain expiry dates and DNS status"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2223,8 +2256,8 @@ msgid "Track domain expiry dates and watch domains for purchase"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
msgid "Track system resources, containers, and health"
|
#~ msgid "Track system resources, containers, and health"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Track uptime, response times, and service health"
|
msgid "Track uptime, response times, and service health"
|
||||||
@@ -2282,6 +2315,7 @@ msgstr "Se activa cuando el estado cambia entre activo e inactivo"
|
|||||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||||
msgstr "Se activa cuando el uso de cualquier disco supera un umbral"
|
msgstr "Se activa cuando el uso de cualquier disco supera un umbral"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/routes/settings/quiet-hours.tsx
|
#: src/components/routes/settings/quiet-hours.tsx
|
||||||
#: src/components/routes/settings/quiet-hours.tsx
|
#: src/components/routes/settings/quiet-hours.tsx
|
||||||
@@ -2416,7 +2450,6 @@ msgstr "Utilización"
|
|||||||
msgid "Value"
|
msgid "Value"
|
||||||
msgstr "Valor"
|
msgstr "Valor"
|
||||||
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "View"
|
msgid "View"
|
||||||
msgstr "Vista"
|
msgstr "Vista"
|
||||||
@@ -2436,8 +2469,8 @@ msgid "View your 200 most recent alerts."
|
|||||||
msgstr "Ver tus 200 alertas más recientes."
|
msgstr "Ver tus 200 alertas más recientes."
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "Visible Fields"
|
#~ msgid "Visible Fields"
|
||||||
msgstr "Columnas visibles"
|
#~ msgstr "Columnas visibles"
|
||||||
|
|
||||||
#: src/components/routes/domain.tsx
|
#: src/components/routes/domain.tsx
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
@@ -2468,7 +2501,6 @@ msgstr "Umbrales de advertencia"
|
|||||||
msgid "Webhook / Push notifications"
|
msgid "Webhook / Push notifications"
|
||||||
msgstr "Notificaciones Webhook / Push"
|
msgstr "Notificaciones Webhook / Push"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Website & Service Monitoring"
|
msgid "Website & Service Monitoring"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|||||||
@@ -230,6 +230,15 @@ msgstr "همه کانتینرها"
|
|||||||
msgid "All Systems"
|
msgid "All Systems"
|
||||||
msgstr "همه سیستمها"
|
msgstr "همه سیستمها"
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "All Tags"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "All Types"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "App update"
|
msgid "App update"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -465,8 +474,8 @@ msgstr ""
|
|||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "Check Now"
|
#~ msgid "Check Now"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/heartbeat.tsx
|
#: src/components/routes/settings/heartbeat.tsx
|
||||||
msgid "Check your monitoring service"
|
msgid "Check your monitoring service"
|
||||||
@@ -503,8 +512,8 @@ msgid "Close"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
#~ msgid "Columns"
|
msgid "Columns"
|
||||||
#~ msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
@@ -784,6 +793,14 @@ msgctxt "Layout display options"
|
|||||||
msgid "Display"
|
msgid "Display"
|
||||||
msgstr "نمایش"
|
msgstr "نمایش"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "Display"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
msgid "Display Columns"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/domain.tsx
|
#: src/components/routes/domain.tsx
|
||||||
#~ msgid "DNS Records"
|
#~ msgid "DNS Records"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
@@ -814,7 +831,6 @@ msgid "Domain and SSL expiry calendar"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/domains-table/domains-table.tsx
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Domain Monitoring"
|
msgid "Domain Monitoring"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1407,7 +1423,6 @@ msgid "Monitor updated successfully"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
msgid "Monitor websites, APIs, and services"
|
msgid "Monitor websites, APIs, and services"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -1448,6 +1463,10 @@ msgstr ""
|
|||||||
msgid "Net"
|
msgid "Net"
|
||||||
msgstr "شبکه"
|
msgstr "شبکه"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
#~ msgid "Network (Grouped)"
|
||||||
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system/charts/network-charts.tsx
|
#: src/components/routes/system/charts/network-charts.tsx
|
||||||
msgid "Network traffic of docker containers"
|
msgid "Network traffic of docker containers"
|
||||||
msgstr "ترافیک شبکه کانتینرهای داکر"
|
msgstr "ترافیک شبکه کانتینرهای داکر"
|
||||||
@@ -1471,8 +1490,8 @@ msgid "No"
|
|||||||
msgstr "خیر"
|
msgstr "خیر"
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "No data available for selected time range"
|
#~ msgid "No data available for selected time range"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/incidents.tsx
|
#: src/components/routes/incidents.tsx
|
||||||
msgid "No incidents found."
|
msgid "No incidents found."
|
||||||
@@ -1553,6 +1572,11 @@ msgstr "باز کردن منو"
|
|||||||
msgid "Optional description for this monitor"
|
msgid "Optional description for this monitor"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "Options"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/login/auth-form.tsx
|
#: src/components/login/auth-form.tsx
|
||||||
msgid "Or continue with"
|
msgid "Or continue with"
|
||||||
msgstr "یا ادامه با"
|
msgstr "یا ادامه با"
|
||||||
@@ -1626,6 +1650,8 @@ msgid "Paused"
|
|||||||
msgstr "مکث شده"
|
msgstr "مکث شده"
|
||||||
|
|
||||||
#. placeholder {0}: stats.paused
|
#. placeholder {0}: stats.paused
|
||||||
|
#. placeholder {0}: statusCounts.paused
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
msgid "Paused ({0})"
|
msgid "Paused ({0})"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1804,6 +1830,10 @@ msgstr "حل شده"
|
|||||||
msgid "Response"
|
msgid "Response"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/routes/monitor.tsx
|
||||||
|
msgid "Response time over the selected period"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
#~ msgid "Response Times"
|
#~ msgid "Response Times"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
@@ -2004,7 +2034,6 @@ msgstr "وضعیت"
|
|||||||
|
|
||||||
#: src/components/containers-table/containers-table-columns.tsx
|
#: src/components/containers-table/containers-table-columns.tsx
|
||||||
#: src/components/domains-table/domains-table.tsx
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/domains-table/domains-table.tsx
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
@@ -2016,12 +2045,15 @@ msgid "Status"
|
|||||||
msgstr "وضعیت"
|
msgstr "وضعیت"
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "Status and response time over the selected period"
|
#~ msgid "Status and response time over the selected period"
|
||||||
|
#~ msgstr ""
|
||||||
|
|
||||||
|
#: src/components/routes/status-pages.tsx
|
||||||
|
msgid "Status Page Manager"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
#: src/components/routes/status-pages.tsx
|
|
||||||
msgid "Status Pages"
|
msgid "Status Pages"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -2065,8 +2097,8 @@ msgid "System load averages over time"
|
|||||||
msgstr "میانگین بار سیستم در طول زمان"
|
msgstr "میانگین بار سیستم در طول زمان"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
msgid "System Monitoring"
|
#~ msgid "System Monitoring"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/systemd-table/systemd-table.tsx
|
#: src/components/systemd-table/systemd-table.tsx
|
||||||
msgid "Systemd Services"
|
msgid "Systemd Services"
|
||||||
@@ -2091,6 +2123,8 @@ msgctxt "Tabs system layout option"
|
|||||||
msgid "Tabs"
|
msgid "Tabs"
|
||||||
msgstr "تبها"
|
msgstr "تبها"
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2213,7 +2247,6 @@ msgstr ""
|
|||||||
msgid "Total: {0}"
|
msgid "Total: {0}"
|
||||||
msgstr "کل: {0}"
|
msgstr "کل: {0}"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Track domain expiry dates and DNS status"
|
msgid "Track domain expiry dates and DNS status"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2223,8 +2256,8 @@ msgid "Track domain expiry dates and watch domains for purchase"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
msgid "Track system resources, containers, and health"
|
#~ msgid "Track system resources, containers, and health"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Track uptime, response times, and service health"
|
msgid "Track uptime, response times, and service health"
|
||||||
@@ -2282,6 +2315,7 @@ msgstr "هنگامی که وضعیت بین بالا و پایین تغییر م
|
|||||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||||
msgstr "هنگامی که استفاده از هر دیسکی از یک آستانه فراتر رود، فعال میشود"
|
msgstr "هنگامی که استفاده از هر دیسکی از یک آستانه فراتر رود، فعال میشود"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/routes/settings/quiet-hours.tsx
|
#: src/components/routes/settings/quiet-hours.tsx
|
||||||
#: src/components/routes/settings/quiet-hours.tsx
|
#: src/components/routes/settings/quiet-hours.tsx
|
||||||
@@ -2416,7 +2450,6 @@ msgstr "بهرهوری"
|
|||||||
msgid "Value"
|
msgid "Value"
|
||||||
msgstr "مقدار"
|
msgstr "مقدار"
|
||||||
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "View"
|
msgid "View"
|
||||||
msgstr "مشاهده"
|
msgstr "مشاهده"
|
||||||
@@ -2436,8 +2469,8 @@ msgid "View your 200 most recent alerts."
|
|||||||
msgstr "۲۰۰ هشدار اخیر خود را مشاهده کنید."
|
msgstr "۲۰۰ هشدار اخیر خود را مشاهده کنید."
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "Visible Fields"
|
#~ msgid "Visible Fields"
|
||||||
msgstr "فیلدهای قابل مشاهده"
|
#~ msgstr "فیلدهای قابل مشاهده"
|
||||||
|
|
||||||
#: src/components/routes/domain.tsx
|
#: src/components/routes/domain.tsx
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
@@ -2468,7 +2501,6 @@ msgstr "آستانه های هشدار"
|
|||||||
msgid "Webhook / Push notifications"
|
msgid "Webhook / Push notifications"
|
||||||
msgstr "اعلانهای Webhook / Push"
|
msgstr "اعلانهای Webhook / Push"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Website & Service Monitoring"
|
msgid "Website & Service Monitoring"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|||||||
@@ -230,6 +230,15 @@ msgstr "Tous les conteneurs"
|
|||||||
msgid "All Systems"
|
msgid "All Systems"
|
||||||
msgstr "Tous les systèmes"
|
msgstr "Tous les systèmes"
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "All Tags"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "All Types"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "App update"
|
msgid "App update"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -465,8 +474,8 @@ msgstr ""
|
|||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "Check Now"
|
#~ msgid "Check Now"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/heartbeat.tsx
|
#: src/components/routes/settings/heartbeat.tsx
|
||||||
msgid "Check your monitoring service"
|
msgid "Check your monitoring service"
|
||||||
@@ -503,8 +512,8 @@ msgid "Close"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
#~ msgid "Columns"
|
msgid "Columns"
|
||||||
#~ msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
@@ -784,6 +793,14 @@ msgctxt "Layout display options"
|
|||||||
msgid "Display"
|
msgid "Display"
|
||||||
msgstr "Affichage"
|
msgstr "Affichage"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "Display"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
msgid "Display Columns"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/domain.tsx
|
#: src/components/routes/domain.tsx
|
||||||
#~ msgid "DNS Records"
|
#~ msgid "DNS Records"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
@@ -814,7 +831,6 @@ msgid "Domain and SSL expiry calendar"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/domains-table/domains-table.tsx
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Domain Monitoring"
|
msgid "Domain Monitoring"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1407,7 +1423,6 @@ msgid "Monitor updated successfully"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
msgid "Monitor websites, APIs, and services"
|
msgid "Monitor websites, APIs, and services"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -1448,6 +1463,10 @@ msgstr ""
|
|||||||
msgid "Net"
|
msgid "Net"
|
||||||
msgstr "Rés"
|
msgstr "Rés"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
#~ msgid "Network (Grouped)"
|
||||||
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system/charts/network-charts.tsx
|
#: src/components/routes/system/charts/network-charts.tsx
|
||||||
msgid "Network traffic of docker containers"
|
msgid "Network traffic of docker containers"
|
||||||
msgstr "Trafic réseau des conteneurs Docker"
|
msgstr "Trafic réseau des conteneurs Docker"
|
||||||
@@ -1471,8 +1490,8 @@ msgid "No"
|
|||||||
msgstr "Non"
|
msgstr "Non"
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "No data available for selected time range"
|
#~ msgid "No data available for selected time range"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/incidents.tsx
|
#: src/components/routes/incidents.tsx
|
||||||
msgid "No incidents found."
|
msgid "No incidents found."
|
||||||
@@ -1553,6 +1572,11 @@ msgstr "Ouvrir le menu"
|
|||||||
msgid "Optional description for this monitor"
|
msgid "Optional description for this monitor"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "Options"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/login/auth-form.tsx
|
#: src/components/login/auth-form.tsx
|
||||||
msgid "Or continue with"
|
msgid "Or continue with"
|
||||||
msgstr "Ou continuer avec"
|
msgstr "Ou continuer avec"
|
||||||
@@ -1626,6 +1650,8 @@ msgid "Paused"
|
|||||||
msgstr "En pause"
|
msgstr "En pause"
|
||||||
|
|
||||||
#. placeholder {0}: stats.paused
|
#. placeholder {0}: stats.paused
|
||||||
|
#. placeholder {0}: statusCounts.paused
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
msgid "Paused ({0})"
|
msgid "Paused ({0})"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1804,6 +1830,10 @@ msgstr "Résolu"
|
|||||||
msgid "Response"
|
msgid "Response"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/routes/monitor.tsx
|
||||||
|
msgid "Response time over the selected period"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
#~ msgid "Response Times"
|
#~ msgid "Response Times"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
@@ -2004,7 +2034,6 @@ msgstr "État"
|
|||||||
|
|
||||||
#: src/components/containers-table/containers-table-columns.tsx
|
#: src/components/containers-table/containers-table-columns.tsx
|
||||||
#: src/components/domains-table/domains-table.tsx
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/domains-table/domains-table.tsx
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
@@ -2016,12 +2045,15 @@ msgid "Status"
|
|||||||
msgstr "Statut"
|
msgstr "Statut"
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "Status and response time over the selected period"
|
#~ msgid "Status and response time over the selected period"
|
||||||
|
#~ msgstr ""
|
||||||
|
|
||||||
|
#: src/components/routes/status-pages.tsx
|
||||||
|
msgid "Status Page Manager"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
#: src/components/routes/status-pages.tsx
|
|
||||||
msgid "Status Pages"
|
msgid "Status Pages"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -2065,8 +2097,8 @@ msgid "System load averages over time"
|
|||||||
msgstr "Charges moyennes du système dans le temps"
|
msgstr "Charges moyennes du système dans le temps"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
msgid "System Monitoring"
|
#~ msgid "System Monitoring"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/systemd-table/systemd-table.tsx
|
#: src/components/systemd-table/systemd-table.tsx
|
||||||
msgid "Systemd Services"
|
msgid "Systemd Services"
|
||||||
@@ -2091,6 +2123,8 @@ msgctxt "Tabs system layout option"
|
|||||||
msgid "Tabs"
|
msgid "Tabs"
|
||||||
msgstr "Onglets"
|
msgstr "Onglets"
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2213,7 +2247,6 @@ msgstr ""
|
|||||||
msgid "Total: {0}"
|
msgid "Total: {0}"
|
||||||
msgstr "Total : {0}"
|
msgstr "Total : {0}"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Track domain expiry dates and DNS status"
|
msgid "Track domain expiry dates and DNS status"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2223,8 +2256,8 @@ msgid "Track domain expiry dates and watch domains for purchase"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
msgid "Track system resources, containers, and health"
|
#~ msgid "Track system resources, containers, and health"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Track uptime, response times, and service health"
|
msgid "Track uptime, response times, and service health"
|
||||||
@@ -2282,6 +2315,7 @@ msgstr "Se déclenche lorsque le statut passe de \"Joignable\" à \"Injoignable\
|
|||||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||||
msgstr "Déclenchement lorsque l'utilisation de tout disque dépasse un seuil"
|
msgstr "Déclenchement lorsque l'utilisation de tout disque dépasse un seuil"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/routes/settings/quiet-hours.tsx
|
#: src/components/routes/settings/quiet-hours.tsx
|
||||||
#: src/components/routes/settings/quiet-hours.tsx
|
#: src/components/routes/settings/quiet-hours.tsx
|
||||||
@@ -2416,7 +2450,6 @@ msgstr "Utilisation"
|
|||||||
msgid "Value"
|
msgid "Value"
|
||||||
msgstr "Valeur"
|
msgstr "Valeur"
|
||||||
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "View"
|
msgid "View"
|
||||||
msgstr "Vue"
|
msgstr "Vue"
|
||||||
@@ -2436,8 +2469,8 @@ msgid "View your 200 most recent alerts."
|
|||||||
msgstr "Voir vos 200 dernières alertes."
|
msgstr "Voir vos 200 dernières alertes."
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "Visible Fields"
|
#~ msgid "Visible Fields"
|
||||||
msgstr "Colonnes visibles"
|
#~ msgstr "Colonnes visibles"
|
||||||
|
|
||||||
#: src/components/routes/domain.tsx
|
#: src/components/routes/domain.tsx
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
@@ -2468,7 +2501,6 @@ msgstr "Seuils d'avertissement"
|
|||||||
msgid "Webhook / Push notifications"
|
msgid "Webhook / Push notifications"
|
||||||
msgstr "Notifications Webhook / Push"
|
msgstr "Notifications Webhook / Push"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Website & Service Monitoring"
|
msgid "Website & Service Monitoring"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|||||||
@@ -230,6 +230,15 @@ msgstr "כל הקונטיינרים"
|
|||||||
msgid "All Systems"
|
msgid "All Systems"
|
||||||
msgstr "כל המערכות"
|
msgstr "כל המערכות"
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "All Tags"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "All Types"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "App update"
|
msgid "App update"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -465,8 +474,8 @@ msgstr ""
|
|||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "Check Now"
|
#~ msgid "Check Now"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/heartbeat.tsx
|
#: src/components/routes/settings/heartbeat.tsx
|
||||||
msgid "Check your monitoring service"
|
msgid "Check your monitoring service"
|
||||||
@@ -503,8 +512,8 @@ msgid "Close"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
#~ msgid "Columns"
|
msgid "Columns"
|
||||||
#~ msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
@@ -784,6 +793,14 @@ msgctxt "Layout display options"
|
|||||||
msgid "Display"
|
msgid "Display"
|
||||||
msgstr "תצוגה"
|
msgstr "תצוגה"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "Display"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
msgid "Display Columns"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/domain.tsx
|
#: src/components/routes/domain.tsx
|
||||||
#~ msgid "DNS Records"
|
#~ msgid "DNS Records"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
@@ -814,7 +831,6 @@ msgid "Domain and SSL expiry calendar"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/domains-table/domains-table.tsx
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Domain Monitoring"
|
msgid "Domain Monitoring"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1407,7 +1423,6 @@ msgid "Monitor updated successfully"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
msgid "Monitor websites, APIs, and services"
|
msgid "Monitor websites, APIs, and services"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -1448,6 +1463,10 @@ msgstr ""
|
|||||||
msgid "Net"
|
msgid "Net"
|
||||||
msgstr "רשת"
|
msgstr "רשת"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
#~ msgid "Network (Grouped)"
|
||||||
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system/charts/network-charts.tsx
|
#: src/components/routes/system/charts/network-charts.tsx
|
||||||
msgid "Network traffic of docker containers"
|
msgid "Network traffic of docker containers"
|
||||||
msgstr "תעבורת רשת של קונטיינרים של Docker"
|
msgstr "תעבורת רשת של קונטיינרים של Docker"
|
||||||
@@ -1471,8 +1490,8 @@ msgid "No"
|
|||||||
msgstr "לא"
|
msgstr "לא"
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "No data available for selected time range"
|
#~ msgid "No data available for selected time range"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/incidents.tsx
|
#: src/components/routes/incidents.tsx
|
||||||
msgid "No incidents found."
|
msgid "No incidents found."
|
||||||
@@ -1553,6 +1572,11 @@ msgstr "פתח תפריט"
|
|||||||
msgid "Optional description for this monitor"
|
msgid "Optional description for this monitor"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "Options"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/login/auth-form.tsx
|
#: src/components/login/auth-form.tsx
|
||||||
msgid "Or continue with"
|
msgid "Or continue with"
|
||||||
msgstr "או המשך עם"
|
msgstr "או המשך עם"
|
||||||
@@ -1626,6 +1650,8 @@ msgid "Paused"
|
|||||||
msgstr "מושהה"
|
msgstr "מושהה"
|
||||||
|
|
||||||
#. placeholder {0}: stats.paused
|
#. placeholder {0}: stats.paused
|
||||||
|
#. placeholder {0}: statusCounts.paused
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
msgid "Paused ({0})"
|
msgid "Paused ({0})"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1804,6 +1830,10 @@ msgstr "נפתר"
|
|||||||
msgid "Response"
|
msgid "Response"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/routes/monitor.tsx
|
||||||
|
msgid "Response time over the selected period"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
#~ msgid "Response Times"
|
#~ msgid "Response Times"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
@@ -2004,7 +2034,6 @@ msgstr "מצב"
|
|||||||
|
|
||||||
#: src/components/containers-table/containers-table-columns.tsx
|
#: src/components/containers-table/containers-table-columns.tsx
|
||||||
#: src/components/domains-table/domains-table.tsx
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/domains-table/domains-table.tsx
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
@@ -2016,12 +2045,15 @@ msgid "Status"
|
|||||||
msgstr "סטטוס"
|
msgstr "סטטוס"
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "Status and response time over the selected period"
|
#~ msgid "Status and response time over the selected period"
|
||||||
|
#~ msgstr ""
|
||||||
|
|
||||||
|
#: src/components/routes/status-pages.tsx
|
||||||
|
msgid "Status Page Manager"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
#: src/components/routes/status-pages.tsx
|
|
||||||
msgid "Status Pages"
|
msgid "Status Pages"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -2065,8 +2097,8 @@ msgid "System load averages over time"
|
|||||||
msgstr "ממוצעי עומס מערכת לאורך זמן"
|
msgstr "ממוצעי עומס מערכת לאורך זמן"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
msgid "System Monitoring"
|
#~ msgid "System Monitoring"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/systemd-table/systemd-table.tsx
|
#: src/components/systemd-table/systemd-table.tsx
|
||||||
msgid "Systemd Services"
|
msgid "Systemd Services"
|
||||||
@@ -2091,6 +2123,8 @@ msgctxt "Tabs system layout option"
|
|||||||
msgid "Tabs"
|
msgid "Tabs"
|
||||||
msgstr "לשוניות"
|
msgstr "לשוניות"
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2213,7 +2247,6 @@ msgstr ""
|
|||||||
msgid "Total: {0}"
|
msgid "Total: {0}"
|
||||||
msgstr "סה\"כ: {0}"
|
msgstr "סה\"כ: {0}"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Track domain expiry dates and DNS status"
|
msgid "Track domain expiry dates and DNS status"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2223,8 +2256,8 @@ msgid "Track domain expiry dates and watch domains for purchase"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
msgid "Track system resources, containers, and health"
|
#~ msgid "Track system resources, containers, and health"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Track uptime, response times, and service health"
|
msgid "Track uptime, response times, and service health"
|
||||||
@@ -2282,6 +2315,7 @@ msgstr "מופעל כאשר הסטטוס מתחלף בין למעלה ולמטה
|
|||||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||||
msgstr "מופעל כאשר שימוש בכל דיסק עולה על סף"
|
msgstr "מופעל כאשר שימוש בכל דיסק עולה על סף"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/routes/settings/quiet-hours.tsx
|
#: src/components/routes/settings/quiet-hours.tsx
|
||||||
#: src/components/routes/settings/quiet-hours.tsx
|
#: src/components/routes/settings/quiet-hours.tsx
|
||||||
@@ -2416,7 +2450,6 @@ msgstr "ניצולת"
|
|||||||
msgid "Value"
|
msgid "Value"
|
||||||
msgstr "ערך"
|
msgstr "ערך"
|
||||||
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "View"
|
msgid "View"
|
||||||
msgstr "צפה"
|
msgstr "צפה"
|
||||||
@@ -2436,8 +2469,8 @@ msgid "View your 200 most recent alerts."
|
|||||||
msgstr "צפה ב-200 ההתראות האחרונות שלך."
|
msgstr "צפה ב-200 ההתראות האחרונות שלך."
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "Visible Fields"
|
#~ msgid "Visible Fields"
|
||||||
msgstr "שדות גלויים"
|
#~ msgstr "שדות גלויים"
|
||||||
|
|
||||||
#: src/components/routes/domain.tsx
|
#: src/components/routes/domain.tsx
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
@@ -2468,7 +2501,6 @@ msgstr "ספי אזהרה"
|
|||||||
msgid "Webhook / Push notifications"
|
msgid "Webhook / Push notifications"
|
||||||
msgstr "Webhook / התראות דחיפה"
|
msgstr "Webhook / התראות דחיפה"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Website & Service Monitoring"
|
msgid "Website & Service Monitoring"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|||||||
@@ -230,6 +230,15 @@ msgstr "Svi spremnici"
|
|||||||
msgid "All Systems"
|
msgid "All Systems"
|
||||||
msgstr "Svi Sustavi"
|
msgstr "Svi Sustavi"
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "All Tags"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "All Types"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "App update"
|
msgid "App update"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -465,8 +474,8 @@ msgstr ""
|
|||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "Check Now"
|
#~ msgid "Check Now"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/heartbeat.tsx
|
#: src/components/routes/settings/heartbeat.tsx
|
||||||
msgid "Check your monitoring service"
|
msgid "Check your monitoring service"
|
||||||
@@ -503,8 +512,8 @@ msgid "Close"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
#~ msgid "Columns"
|
msgid "Columns"
|
||||||
#~ msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
@@ -784,6 +793,14 @@ msgctxt "Layout display options"
|
|||||||
msgid "Display"
|
msgid "Display"
|
||||||
msgstr "Prikaz"
|
msgstr "Prikaz"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "Display"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
msgid "Display Columns"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/domain.tsx
|
#: src/components/routes/domain.tsx
|
||||||
#~ msgid "DNS Records"
|
#~ msgid "DNS Records"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
@@ -814,7 +831,6 @@ msgid "Domain and SSL expiry calendar"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/domains-table/domains-table.tsx
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Domain Monitoring"
|
msgid "Domain Monitoring"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1407,7 +1423,6 @@ msgid "Monitor updated successfully"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
msgid "Monitor websites, APIs, and services"
|
msgid "Monitor websites, APIs, and services"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -1448,6 +1463,10 @@ msgstr ""
|
|||||||
msgid "Net"
|
msgid "Net"
|
||||||
msgstr "Mreža"
|
msgstr "Mreža"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
#~ msgid "Network (Grouped)"
|
||||||
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system/charts/network-charts.tsx
|
#: src/components/routes/system/charts/network-charts.tsx
|
||||||
msgid "Network traffic of docker containers"
|
msgid "Network traffic of docker containers"
|
||||||
msgstr "Mrežni promet Docker spremnika"
|
msgstr "Mrežni promet Docker spremnika"
|
||||||
@@ -1471,8 +1490,8 @@ msgid "No"
|
|||||||
msgstr "Ne"
|
msgstr "Ne"
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "No data available for selected time range"
|
#~ msgid "No data available for selected time range"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/incidents.tsx
|
#: src/components/routes/incidents.tsx
|
||||||
msgid "No incidents found."
|
msgid "No incidents found."
|
||||||
@@ -1553,6 +1572,11 @@ msgstr "Otvori meni"
|
|||||||
msgid "Optional description for this monitor"
|
msgid "Optional description for this monitor"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "Options"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/login/auth-form.tsx
|
#: src/components/login/auth-form.tsx
|
||||||
msgid "Or continue with"
|
msgid "Or continue with"
|
||||||
msgstr "Ili nastavi s"
|
msgstr "Ili nastavi s"
|
||||||
@@ -1626,6 +1650,8 @@ msgid "Paused"
|
|||||||
msgstr "Pauzirano"
|
msgstr "Pauzirano"
|
||||||
|
|
||||||
#. placeholder {0}: stats.paused
|
#. placeholder {0}: stats.paused
|
||||||
|
#. placeholder {0}: statusCounts.paused
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
msgid "Paused ({0})"
|
msgid "Paused ({0})"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1804,6 +1830,10 @@ msgstr "Razrješeno"
|
|||||||
msgid "Response"
|
msgid "Response"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/routes/monitor.tsx
|
||||||
|
msgid "Response time over the selected period"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
#~ msgid "Response Times"
|
#~ msgid "Response Times"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
@@ -2004,7 +2034,6 @@ msgstr "Stanje"
|
|||||||
|
|
||||||
#: src/components/containers-table/containers-table-columns.tsx
|
#: src/components/containers-table/containers-table-columns.tsx
|
||||||
#: src/components/domains-table/domains-table.tsx
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/domains-table/domains-table.tsx
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
@@ -2016,12 +2045,15 @@ msgid "Status"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "Status and response time over the selected period"
|
#~ msgid "Status and response time over the selected period"
|
||||||
|
#~ msgstr ""
|
||||||
|
|
||||||
|
#: src/components/routes/status-pages.tsx
|
||||||
|
msgid "Status Page Manager"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
#: src/components/routes/status-pages.tsx
|
|
||||||
msgid "Status Pages"
|
msgid "Status Pages"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -2065,8 +2097,8 @@ msgid "System load averages over time"
|
|||||||
msgstr "Prosječno opterećenje sustava kroz vrijeme"
|
msgstr "Prosječno opterećenje sustava kroz vrijeme"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
msgid "System Monitoring"
|
#~ msgid "System Monitoring"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/systemd-table/systemd-table.tsx
|
#: src/components/systemd-table/systemd-table.tsx
|
||||||
msgid "Systemd Services"
|
msgid "Systemd Services"
|
||||||
@@ -2091,6 +2123,8 @@ msgctxt "Tabs system layout option"
|
|||||||
msgid "Tabs"
|
msgid "Tabs"
|
||||||
msgstr "Kartice"
|
msgstr "Kartice"
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2213,7 +2247,6 @@ msgstr ""
|
|||||||
msgid "Total: {0}"
|
msgid "Total: {0}"
|
||||||
msgstr "Ukupno: {0}"
|
msgstr "Ukupno: {0}"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Track domain expiry dates and DNS status"
|
msgid "Track domain expiry dates and DNS status"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2223,8 +2256,8 @@ msgid "Track domain expiry dates and watch domains for purchase"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
msgid "Track system resources, containers, and health"
|
#~ msgid "Track system resources, containers, and health"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Track uptime, response times, and service health"
|
msgid "Track uptime, response times, and service health"
|
||||||
@@ -2282,6 +2315,7 @@ msgstr "Pokreće se kada se status sustava promijeni"
|
|||||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||||
msgstr "Pokreće se kada iskorištenost bilo kojeg diska premaši prag"
|
msgstr "Pokreće se kada iskorištenost bilo kojeg diska premaši prag"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/routes/settings/quiet-hours.tsx
|
#: src/components/routes/settings/quiet-hours.tsx
|
||||||
#: src/components/routes/settings/quiet-hours.tsx
|
#: src/components/routes/settings/quiet-hours.tsx
|
||||||
@@ -2416,7 +2450,6 @@ msgstr "Iskorištenost"
|
|||||||
msgid "Value"
|
msgid "Value"
|
||||||
msgstr "Vrijednost"
|
msgstr "Vrijednost"
|
||||||
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "View"
|
msgid "View"
|
||||||
msgstr "Prikaz"
|
msgstr "Prikaz"
|
||||||
@@ -2436,8 +2469,8 @@ msgid "View your 200 most recent alerts."
|
|||||||
msgstr "Pogledajte posljednjih 200 upozorenja."
|
msgstr "Pogledajte posljednjih 200 upozorenja."
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "Visible Fields"
|
#~ msgid "Visible Fields"
|
||||||
msgstr "Vidljiva polja"
|
#~ msgstr "Vidljiva polja"
|
||||||
|
|
||||||
#: src/components/routes/domain.tsx
|
#: src/components/routes/domain.tsx
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
@@ -2468,7 +2501,6 @@ msgstr "Pragovi upozorenja"
|
|||||||
msgid "Webhook / Push notifications"
|
msgid "Webhook / Push notifications"
|
||||||
msgstr "Webhook / Push obavijest"
|
msgstr "Webhook / Push obavijest"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Website & Service Monitoring"
|
msgid "Website & Service Monitoring"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|||||||
@@ -230,6 +230,15 @@ msgstr "Minden konténer"
|
|||||||
msgid "All Systems"
|
msgid "All Systems"
|
||||||
msgstr "Minden rendszer"
|
msgstr "Minden rendszer"
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "All Tags"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "All Types"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "App update"
|
msgid "App update"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -465,8 +474,8 @@ msgstr ""
|
|||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "Check Now"
|
#~ msgid "Check Now"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/heartbeat.tsx
|
#: src/components/routes/settings/heartbeat.tsx
|
||||||
msgid "Check your monitoring service"
|
msgid "Check your monitoring service"
|
||||||
@@ -503,8 +512,8 @@ msgid "Close"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
#~ msgid "Columns"
|
msgid "Columns"
|
||||||
#~ msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
@@ -784,6 +793,14 @@ msgctxt "Layout display options"
|
|||||||
msgid "Display"
|
msgid "Display"
|
||||||
msgstr "Megjelenítés"
|
msgstr "Megjelenítés"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "Display"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
msgid "Display Columns"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/domain.tsx
|
#: src/components/routes/domain.tsx
|
||||||
#~ msgid "DNS Records"
|
#~ msgid "DNS Records"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
@@ -814,7 +831,6 @@ msgid "Domain and SSL expiry calendar"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/domains-table/domains-table.tsx
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Domain Monitoring"
|
msgid "Domain Monitoring"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1407,7 +1423,6 @@ msgid "Monitor updated successfully"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
msgid "Monitor websites, APIs, and services"
|
msgid "Monitor websites, APIs, and services"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -1448,6 +1463,10 @@ msgstr ""
|
|||||||
msgid "Net"
|
msgid "Net"
|
||||||
msgstr "Hálózat"
|
msgstr "Hálózat"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
#~ msgid "Network (Grouped)"
|
||||||
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system/charts/network-charts.tsx
|
#: src/components/routes/system/charts/network-charts.tsx
|
||||||
msgid "Network traffic of docker containers"
|
msgid "Network traffic of docker containers"
|
||||||
msgstr "Docker konténerek hálózati forgalma"
|
msgstr "Docker konténerek hálózati forgalma"
|
||||||
@@ -1471,8 +1490,8 @@ msgid "No"
|
|||||||
msgstr "Nem"
|
msgstr "Nem"
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "No data available for selected time range"
|
#~ msgid "No data available for selected time range"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/incidents.tsx
|
#: src/components/routes/incidents.tsx
|
||||||
msgid "No incidents found."
|
msgid "No incidents found."
|
||||||
@@ -1553,6 +1572,11 @@ msgstr "Menü megnyitása"
|
|||||||
msgid "Optional description for this monitor"
|
msgid "Optional description for this monitor"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "Options"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/login/auth-form.tsx
|
#: src/components/login/auth-form.tsx
|
||||||
msgid "Or continue with"
|
msgid "Or continue with"
|
||||||
msgstr "Vagy folytasd ezzel"
|
msgstr "Vagy folytasd ezzel"
|
||||||
@@ -1626,6 +1650,8 @@ msgid "Paused"
|
|||||||
msgstr "Szüneteltetve"
|
msgstr "Szüneteltetve"
|
||||||
|
|
||||||
#. placeholder {0}: stats.paused
|
#. placeholder {0}: stats.paused
|
||||||
|
#. placeholder {0}: statusCounts.paused
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
msgid "Paused ({0})"
|
msgid "Paused ({0})"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1804,6 +1830,10 @@ msgstr "Megoldva"
|
|||||||
msgid "Response"
|
msgid "Response"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/routes/monitor.tsx
|
||||||
|
msgid "Response time over the selected period"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
#~ msgid "Response Times"
|
#~ msgid "Response Times"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
@@ -2004,7 +2034,6 @@ msgstr "Állapot"
|
|||||||
|
|
||||||
#: src/components/containers-table/containers-table-columns.tsx
|
#: src/components/containers-table/containers-table-columns.tsx
|
||||||
#: src/components/domains-table/domains-table.tsx
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/domains-table/domains-table.tsx
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
@@ -2016,12 +2045,15 @@ msgid "Status"
|
|||||||
msgstr "Állapot"
|
msgstr "Állapot"
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "Status and response time over the selected period"
|
#~ msgid "Status and response time over the selected period"
|
||||||
|
#~ msgstr ""
|
||||||
|
|
||||||
|
#: src/components/routes/status-pages.tsx
|
||||||
|
msgid "Status Page Manager"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
#: src/components/routes/status-pages.tsx
|
|
||||||
msgid "Status Pages"
|
msgid "Status Pages"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -2065,8 +2097,8 @@ msgid "System load averages over time"
|
|||||||
msgstr "Rendszer terhelési átlaga"
|
msgstr "Rendszer terhelési átlaga"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
msgid "System Monitoring"
|
#~ msgid "System Monitoring"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/systemd-table/systemd-table.tsx
|
#: src/components/systemd-table/systemd-table.tsx
|
||||||
msgid "Systemd Services"
|
msgid "Systemd Services"
|
||||||
@@ -2091,6 +2123,8 @@ msgctxt "Tabs system layout option"
|
|||||||
msgid "Tabs"
|
msgid "Tabs"
|
||||||
msgstr "Lapok"
|
msgstr "Lapok"
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2213,7 +2247,6 @@ msgstr ""
|
|||||||
msgid "Total: {0}"
|
msgid "Total: {0}"
|
||||||
msgstr "Összesen: {0}"
|
msgstr "Összesen: {0}"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Track domain expiry dates and DNS status"
|
msgid "Track domain expiry dates and DNS status"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2223,8 +2256,8 @@ msgid "Track domain expiry dates and watch domains for purchase"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
msgid "Track system resources, containers, and health"
|
#~ msgid "Track system resources, containers, and health"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Track uptime, response times, and service health"
|
msgid "Track uptime, response times, and service health"
|
||||||
@@ -2282,6 +2315,7 @@ msgstr "Riaszt, amikor a rendszer online állapota változik"
|
|||||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||||
msgstr "Riaszt, ha a lemezhasználat túllép egy küszöbértéket"
|
msgstr "Riaszt, ha a lemezhasználat túllép egy küszöbértéket"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/routes/settings/quiet-hours.tsx
|
#: src/components/routes/settings/quiet-hours.tsx
|
||||||
#: src/components/routes/settings/quiet-hours.tsx
|
#: src/components/routes/settings/quiet-hours.tsx
|
||||||
@@ -2416,7 +2450,6 @@ msgstr "Kihasználtság"
|
|||||||
msgid "Value"
|
msgid "Value"
|
||||||
msgstr "Érték"
|
msgstr "Érték"
|
||||||
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "View"
|
msgid "View"
|
||||||
msgstr "Nézet"
|
msgstr "Nézet"
|
||||||
@@ -2436,8 +2469,8 @@ msgid "View your 200 most recent alerts."
|
|||||||
msgstr "Legfrissebb 200 riasztásod áttekintése."
|
msgstr "Legfrissebb 200 riasztásod áttekintése."
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "Visible Fields"
|
#~ msgid "Visible Fields"
|
||||||
msgstr "Látható mezők"
|
#~ msgstr "Látható mezők"
|
||||||
|
|
||||||
#: src/components/routes/domain.tsx
|
#: src/components/routes/domain.tsx
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
@@ -2468,7 +2501,6 @@ msgstr "Figyelmeztetési küszöbértékek"
|
|||||||
msgid "Webhook / Push notifications"
|
msgid "Webhook / Push notifications"
|
||||||
msgstr "Webhook / Push értesítések"
|
msgstr "Webhook / Push értesítések"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Website & Service Monitoring"
|
msgid "Website & Service Monitoring"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|||||||
@@ -230,6 +230,15 @@ msgstr "Semua Container"
|
|||||||
msgid "All Systems"
|
msgid "All Systems"
|
||||||
msgstr "Semua Sistem"
|
msgstr "Semua Sistem"
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "All Tags"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "All Types"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "App update"
|
msgid "App update"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -465,8 +474,8 @@ msgstr ""
|
|||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "Check Now"
|
#~ msgid "Check Now"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/heartbeat.tsx
|
#: src/components/routes/settings/heartbeat.tsx
|
||||||
msgid "Check your monitoring service"
|
msgid "Check your monitoring service"
|
||||||
@@ -503,8 +512,8 @@ msgid "Close"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
#~ msgid "Columns"
|
msgid "Columns"
|
||||||
#~ msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
@@ -784,6 +793,14 @@ msgctxt "Layout display options"
|
|||||||
msgid "Display"
|
msgid "Display"
|
||||||
msgstr "Tampilan"
|
msgstr "Tampilan"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "Display"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
msgid "Display Columns"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/domain.tsx
|
#: src/components/routes/domain.tsx
|
||||||
#~ msgid "DNS Records"
|
#~ msgid "DNS Records"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
@@ -814,7 +831,6 @@ msgid "Domain and SSL expiry calendar"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/domains-table/domains-table.tsx
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Domain Monitoring"
|
msgid "Domain Monitoring"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1407,7 +1423,6 @@ msgid "Monitor updated successfully"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
msgid "Monitor websites, APIs, and services"
|
msgid "Monitor websites, APIs, and services"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -1448,6 +1463,10 @@ msgstr ""
|
|||||||
msgid "Net"
|
msgid "Net"
|
||||||
msgstr "Jaringan"
|
msgstr "Jaringan"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
#~ msgid "Network (Grouped)"
|
||||||
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system/charts/network-charts.tsx
|
#: src/components/routes/system/charts/network-charts.tsx
|
||||||
msgid "Network traffic of docker containers"
|
msgid "Network traffic of docker containers"
|
||||||
msgstr "Trafik jaringan kontainer docker"
|
msgstr "Trafik jaringan kontainer docker"
|
||||||
@@ -1471,8 +1490,8 @@ msgid "No"
|
|||||||
msgstr "Tidak"
|
msgstr "Tidak"
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "No data available for selected time range"
|
#~ msgid "No data available for selected time range"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/incidents.tsx
|
#: src/components/routes/incidents.tsx
|
||||||
msgid "No incidents found."
|
msgid "No incidents found."
|
||||||
@@ -1553,6 +1572,11 @@ msgstr "Buka menu"
|
|||||||
msgid "Optional description for this monitor"
|
msgid "Optional description for this monitor"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "Options"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/login/auth-form.tsx
|
#: src/components/login/auth-form.tsx
|
||||||
msgid "Or continue with"
|
msgid "Or continue with"
|
||||||
msgstr "Atau lanjutkan dengan"
|
msgstr "Atau lanjutkan dengan"
|
||||||
@@ -1626,6 +1650,8 @@ msgid "Paused"
|
|||||||
msgstr "Dijeda"
|
msgstr "Dijeda"
|
||||||
|
|
||||||
#. placeholder {0}: stats.paused
|
#. placeholder {0}: stats.paused
|
||||||
|
#. placeholder {0}: statusCounts.paused
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
msgid "Paused ({0})"
|
msgid "Paused ({0})"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1804,6 +1830,10 @@ msgstr "Diselesaikan"
|
|||||||
msgid "Response"
|
msgid "Response"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/routes/monitor.tsx
|
||||||
|
msgid "Response time over the selected period"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
#~ msgid "Response Times"
|
#~ msgid "Response Times"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
@@ -2004,7 +2034,6 @@ msgstr "Status"
|
|||||||
|
|
||||||
#: src/components/containers-table/containers-table-columns.tsx
|
#: src/components/containers-table/containers-table-columns.tsx
|
||||||
#: src/components/domains-table/domains-table.tsx
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/domains-table/domains-table.tsx
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
@@ -2016,12 +2045,15 @@ msgid "Status"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "Status and response time over the selected period"
|
#~ msgid "Status and response time over the selected period"
|
||||||
|
#~ msgstr ""
|
||||||
|
|
||||||
|
#: src/components/routes/status-pages.tsx
|
||||||
|
msgid "Status Page Manager"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
#: src/components/routes/status-pages.tsx
|
|
||||||
msgid "Status Pages"
|
msgid "Status Pages"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -2065,8 +2097,8 @@ msgid "System load averages over time"
|
|||||||
msgstr "Rata-rata beban sistem dari waktu ke waktu"
|
msgstr "Rata-rata beban sistem dari waktu ke waktu"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
msgid "System Monitoring"
|
#~ msgid "System Monitoring"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/systemd-table/systemd-table.tsx
|
#: src/components/systemd-table/systemd-table.tsx
|
||||||
msgid "Systemd Services"
|
msgid "Systemd Services"
|
||||||
@@ -2091,6 +2123,8 @@ msgctxt "Tabs system layout option"
|
|||||||
msgid "Tabs"
|
msgid "Tabs"
|
||||||
msgstr "Tab"
|
msgstr "Tab"
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2213,7 +2247,6 @@ msgstr ""
|
|||||||
msgid "Total: {0}"
|
msgid "Total: {0}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Track domain expiry dates and DNS status"
|
msgid "Track domain expiry dates and DNS status"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2223,8 +2256,8 @@ msgid "Track domain expiry dates and watch domains for purchase"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
msgid "Track system resources, containers, and health"
|
#~ msgid "Track system resources, containers, and health"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Track uptime, response times, and service health"
|
msgid "Track uptime, response times, and service health"
|
||||||
@@ -2282,6 +2315,7 @@ msgstr "Dipicu ketika status beralih antara up dan down"
|
|||||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||||
msgstr "Dipicu ketika penggunaan disk apa pun melebihi ambang batas"
|
msgstr "Dipicu ketika penggunaan disk apa pun melebihi ambang batas"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/routes/settings/quiet-hours.tsx
|
#: src/components/routes/settings/quiet-hours.tsx
|
||||||
#: src/components/routes/settings/quiet-hours.tsx
|
#: src/components/routes/settings/quiet-hours.tsx
|
||||||
@@ -2416,7 +2450,6 @@ msgstr "Utilisasi"
|
|||||||
msgid "Value"
|
msgid "Value"
|
||||||
msgstr "Nilai"
|
msgstr "Nilai"
|
||||||
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "View"
|
msgid "View"
|
||||||
msgstr "Lihat"
|
msgstr "Lihat"
|
||||||
@@ -2436,8 +2469,8 @@ msgid "View your 200 most recent alerts."
|
|||||||
msgstr "Lihat 200 peringatan terbaru anda."
|
msgstr "Lihat 200 peringatan terbaru anda."
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "Visible Fields"
|
#~ msgid "Visible Fields"
|
||||||
msgstr "Metrik yang Terlihat"
|
#~ msgstr "Metrik yang Terlihat"
|
||||||
|
|
||||||
#: src/components/routes/domain.tsx
|
#: src/components/routes/domain.tsx
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
@@ -2468,7 +2501,6 @@ msgstr "Ambang peringatan"
|
|||||||
msgid "Webhook / Push notifications"
|
msgid "Webhook / Push notifications"
|
||||||
msgstr "Webhook / Push notifikasi"
|
msgstr "Webhook / Push notifikasi"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Website & Service Monitoring"
|
msgid "Website & Service Monitoring"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|||||||
@@ -230,6 +230,15 @@ msgstr "Tutti i contenitori"
|
|||||||
msgid "All Systems"
|
msgid "All Systems"
|
||||||
msgstr "Tutti i Sistemi"
|
msgstr "Tutti i Sistemi"
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "All Tags"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "All Types"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "App update"
|
msgid "App update"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -465,8 +474,8 @@ msgstr ""
|
|||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "Check Now"
|
#~ msgid "Check Now"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/heartbeat.tsx
|
#: src/components/routes/settings/heartbeat.tsx
|
||||||
msgid "Check your monitoring service"
|
msgid "Check your monitoring service"
|
||||||
@@ -503,8 +512,8 @@ msgid "Close"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
#~ msgid "Columns"
|
msgid "Columns"
|
||||||
#~ msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
@@ -784,6 +793,14 @@ msgctxt "Layout display options"
|
|||||||
msgid "Display"
|
msgid "Display"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "Display"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
msgid "Display Columns"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/domain.tsx
|
#: src/components/routes/domain.tsx
|
||||||
#~ msgid "DNS Records"
|
#~ msgid "DNS Records"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
@@ -814,7 +831,6 @@ msgid "Domain and SSL expiry calendar"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/domains-table/domains-table.tsx
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Domain Monitoring"
|
msgid "Domain Monitoring"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1407,7 +1423,6 @@ msgid "Monitor updated successfully"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
msgid "Monitor websites, APIs, and services"
|
msgid "Monitor websites, APIs, and services"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -1448,6 +1463,10 @@ msgstr ""
|
|||||||
msgid "Net"
|
msgid "Net"
|
||||||
msgstr "Rete"
|
msgstr "Rete"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
#~ msgid "Network (Grouped)"
|
||||||
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system/charts/network-charts.tsx
|
#: src/components/routes/system/charts/network-charts.tsx
|
||||||
msgid "Network traffic of docker containers"
|
msgid "Network traffic of docker containers"
|
||||||
msgstr "Traffico di rete dei container Docker"
|
msgstr "Traffico di rete dei container Docker"
|
||||||
@@ -1471,8 +1490,8 @@ msgid "No"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "No data available for selected time range"
|
#~ msgid "No data available for selected time range"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/incidents.tsx
|
#: src/components/routes/incidents.tsx
|
||||||
msgid "No incidents found."
|
msgid "No incidents found."
|
||||||
@@ -1553,6 +1572,11 @@ msgstr "Apri menu"
|
|||||||
msgid "Optional description for this monitor"
|
msgid "Optional description for this monitor"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "Options"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/login/auth-form.tsx
|
#: src/components/login/auth-form.tsx
|
||||||
msgid "Or continue with"
|
msgid "Or continue with"
|
||||||
msgstr "Oppure continua con"
|
msgstr "Oppure continua con"
|
||||||
@@ -1626,6 +1650,8 @@ msgid "Paused"
|
|||||||
msgstr "In pausa"
|
msgstr "In pausa"
|
||||||
|
|
||||||
#. placeholder {0}: stats.paused
|
#. placeholder {0}: stats.paused
|
||||||
|
#. placeholder {0}: statusCounts.paused
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
msgid "Paused ({0})"
|
msgid "Paused ({0})"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1804,6 +1830,10 @@ msgstr "Risolto"
|
|||||||
msgid "Response"
|
msgid "Response"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/routes/monitor.tsx
|
||||||
|
msgid "Response time over the selected period"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
#~ msgid "Response Times"
|
#~ msgid "Response Times"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
@@ -2004,7 +2034,6 @@ msgstr "Stato"
|
|||||||
|
|
||||||
#: src/components/containers-table/containers-table-columns.tsx
|
#: src/components/containers-table/containers-table-columns.tsx
|
||||||
#: src/components/domains-table/domains-table.tsx
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/domains-table/domains-table.tsx
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
@@ -2016,12 +2045,15 @@ msgid "Status"
|
|||||||
msgstr "Stato"
|
msgstr "Stato"
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "Status and response time over the selected period"
|
#~ msgid "Status and response time over the selected period"
|
||||||
|
#~ msgstr ""
|
||||||
|
|
||||||
|
#: src/components/routes/status-pages.tsx
|
||||||
|
msgid "Status Page Manager"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
#: src/components/routes/status-pages.tsx
|
|
||||||
msgid "Status Pages"
|
msgid "Status Pages"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -2065,8 +2097,8 @@ msgid "System load averages over time"
|
|||||||
msgstr "Medie di carico del sistema nel tempo"
|
msgstr "Medie di carico del sistema nel tempo"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
msgid "System Monitoring"
|
#~ msgid "System Monitoring"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/systemd-table/systemd-table.tsx
|
#: src/components/systemd-table/systemd-table.tsx
|
||||||
msgid "Systemd Services"
|
msgid "Systemd Services"
|
||||||
@@ -2091,6 +2123,8 @@ msgctxt "Tabs system layout option"
|
|||||||
msgid "Tabs"
|
msgid "Tabs"
|
||||||
msgstr "Schede"
|
msgstr "Schede"
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2213,7 +2247,6 @@ msgstr ""
|
|||||||
msgid "Total: {0}"
|
msgid "Total: {0}"
|
||||||
msgstr "Totale: {0}"
|
msgstr "Totale: {0}"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Track domain expiry dates and DNS status"
|
msgid "Track domain expiry dates and DNS status"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2223,8 +2256,8 @@ msgid "Track domain expiry dates and watch domains for purchase"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
msgid "Track system resources, containers, and health"
|
#~ msgid "Track system resources, containers, and health"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Track uptime, response times, and service health"
|
msgid "Track uptime, response times, and service health"
|
||||||
@@ -2282,6 +2315,7 @@ msgstr "Attiva quando lo stato passa tra up e down"
|
|||||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||||
msgstr "Attiva quando l'utilizzo di un disco supera una soglia"
|
msgstr "Attiva quando l'utilizzo di un disco supera una soglia"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/routes/settings/quiet-hours.tsx
|
#: src/components/routes/settings/quiet-hours.tsx
|
||||||
#: src/components/routes/settings/quiet-hours.tsx
|
#: src/components/routes/settings/quiet-hours.tsx
|
||||||
@@ -2416,7 +2450,6 @@ msgstr "Utilizzo"
|
|||||||
msgid "Value"
|
msgid "Value"
|
||||||
msgstr "Valore"
|
msgstr "Valore"
|
||||||
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "View"
|
msgid "View"
|
||||||
msgstr "Vista"
|
msgstr "Vista"
|
||||||
@@ -2436,8 +2469,8 @@ msgid "View your 200 most recent alerts."
|
|||||||
msgstr "Visualizza i tuoi 200 avvisi più recenti."
|
msgstr "Visualizza i tuoi 200 avvisi più recenti."
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "Visible Fields"
|
#~ msgid "Visible Fields"
|
||||||
msgstr "Colonne visibili"
|
#~ msgstr "Colonne visibili"
|
||||||
|
|
||||||
#: src/components/routes/domain.tsx
|
#: src/components/routes/domain.tsx
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
@@ -2468,7 +2501,6 @@ msgstr "Soglie di avviso"
|
|||||||
msgid "Webhook / Push notifications"
|
msgid "Webhook / Push notifications"
|
||||||
msgstr "Notifiche Webhook / Push"
|
msgstr "Notifiche Webhook / Push"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Website & Service Monitoring"
|
msgid "Website & Service Monitoring"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|||||||
@@ -230,6 +230,15 @@ msgstr "すべてのコンテナ"
|
|||||||
msgid "All Systems"
|
msgid "All Systems"
|
||||||
msgstr "すべてのシステム"
|
msgstr "すべてのシステム"
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "All Tags"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "All Types"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "App update"
|
msgid "App update"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -465,8 +474,8 @@ msgstr ""
|
|||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "Check Now"
|
#~ msgid "Check Now"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/heartbeat.tsx
|
#: src/components/routes/settings/heartbeat.tsx
|
||||||
msgid "Check your monitoring service"
|
msgid "Check your monitoring service"
|
||||||
@@ -503,8 +512,8 @@ msgid "Close"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
#~ msgid "Columns"
|
msgid "Columns"
|
||||||
#~ msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
@@ -784,6 +793,14 @@ msgctxt "Layout display options"
|
|||||||
msgid "Display"
|
msgid "Display"
|
||||||
msgstr "表示"
|
msgstr "表示"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "Display"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
msgid "Display Columns"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/domain.tsx
|
#: src/components/routes/domain.tsx
|
||||||
#~ msgid "DNS Records"
|
#~ msgid "DNS Records"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
@@ -814,7 +831,6 @@ msgid "Domain and SSL expiry calendar"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/domains-table/domains-table.tsx
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Domain Monitoring"
|
msgid "Domain Monitoring"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1407,7 +1423,6 @@ msgid "Monitor updated successfully"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
msgid "Monitor websites, APIs, and services"
|
msgid "Monitor websites, APIs, and services"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -1448,6 +1463,10 @@ msgstr ""
|
|||||||
msgid "Net"
|
msgid "Net"
|
||||||
msgstr "帯域"
|
msgstr "帯域"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
#~ msgid "Network (Grouped)"
|
||||||
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system/charts/network-charts.tsx
|
#: src/components/routes/system/charts/network-charts.tsx
|
||||||
msgid "Network traffic of docker containers"
|
msgid "Network traffic of docker containers"
|
||||||
msgstr "Dockerコンテナのネットワークトラフィック"
|
msgstr "Dockerコンテナのネットワークトラフィック"
|
||||||
@@ -1471,8 +1490,8 @@ msgid "No"
|
|||||||
msgstr "いいえ"
|
msgstr "いいえ"
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "No data available for selected time range"
|
#~ msgid "No data available for selected time range"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/incidents.tsx
|
#: src/components/routes/incidents.tsx
|
||||||
msgid "No incidents found."
|
msgid "No incidents found."
|
||||||
@@ -1553,6 +1572,11 @@ msgstr "メニューを開く"
|
|||||||
msgid "Optional description for this monitor"
|
msgid "Optional description for this monitor"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "Options"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/login/auth-form.tsx
|
#: src/components/login/auth-form.tsx
|
||||||
msgid "Or continue with"
|
msgid "Or continue with"
|
||||||
msgstr "または、以下の方法でログイン"
|
msgstr "または、以下の方法でログイン"
|
||||||
@@ -1626,6 +1650,8 @@ msgid "Paused"
|
|||||||
msgstr "一時停止中"
|
msgstr "一時停止中"
|
||||||
|
|
||||||
#. placeholder {0}: stats.paused
|
#. placeholder {0}: stats.paused
|
||||||
|
#. placeholder {0}: statusCounts.paused
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
msgid "Paused ({0})"
|
msgid "Paused ({0})"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1804,6 +1830,10 @@ msgstr "解決済み"
|
|||||||
msgid "Response"
|
msgid "Response"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/routes/monitor.tsx
|
||||||
|
msgid "Response time over the selected period"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
#~ msgid "Response Times"
|
#~ msgid "Response Times"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
@@ -2004,7 +2034,6 @@ msgstr "状態"
|
|||||||
|
|
||||||
#: src/components/containers-table/containers-table-columns.tsx
|
#: src/components/containers-table/containers-table-columns.tsx
|
||||||
#: src/components/domains-table/domains-table.tsx
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/domains-table/domains-table.tsx
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
@@ -2016,12 +2045,15 @@ msgid "Status"
|
|||||||
msgstr "ステータス"
|
msgstr "ステータス"
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "Status and response time over the selected period"
|
#~ msgid "Status and response time over the selected period"
|
||||||
|
#~ msgstr ""
|
||||||
|
|
||||||
|
#: src/components/routes/status-pages.tsx
|
||||||
|
msgid "Status Page Manager"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
#: src/components/routes/status-pages.tsx
|
|
||||||
msgid "Status Pages"
|
msgid "Status Pages"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -2065,8 +2097,8 @@ msgid "System load averages over time"
|
|||||||
msgstr "システムの負荷平均の推移"
|
msgstr "システムの負荷平均の推移"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
msgid "System Monitoring"
|
#~ msgid "System Monitoring"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/systemd-table/systemd-table.tsx
|
#: src/components/systemd-table/systemd-table.tsx
|
||||||
msgid "Systemd Services"
|
msgid "Systemd Services"
|
||||||
@@ -2091,6 +2123,8 @@ msgctxt "Tabs system layout option"
|
|||||||
msgid "Tabs"
|
msgid "Tabs"
|
||||||
msgstr "タブ"
|
msgstr "タブ"
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2213,7 +2247,6 @@ msgstr ""
|
|||||||
msgid "Total: {0}"
|
msgid "Total: {0}"
|
||||||
msgstr "合計: {0}"
|
msgstr "合計: {0}"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Track domain expiry dates and DNS status"
|
msgid "Track domain expiry dates and DNS status"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2223,8 +2256,8 @@ msgid "Track domain expiry dates and watch domains for purchase"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
msgid "Track system resources, containers, and health"
|
#~ msgid "Track system resources, containers, and health"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Track uptime, response times, and service health"
|
msgid "Track uptime, response times, and service health"
|
||||||
@@ -2282,6 +2315,7 @@ msgstr "ステータスが上から下に切り替わるときにトリガーさ
|
|||||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||||
msgstr "ディスクの使用量がしきい値を超えたときにトリガーされます"
|
msgstr "ディスクの使用量がしきい値を超えたときにトリガーされます"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/routes/settings/quiet-hours.tsx
|
#: src/components/routes/settings/quiet-hours.tsx
|
||||||
#: src/components/routes/settings/quiet-hours.tsx
|
#: src/components/routes/settings/quiet-hours.tsx
|
||||||
@@ -2416,7 +2450,6 @@ msgstr "利用率"
|
|||||||
msgid "Value"
|
msgid "Value"
|
||||||
msgstr "値"
|
msgstr "値"
|
||||||
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "View"
|
msgid "View"
|
||||||
msgstr "表示"
|
msgstr "表示"
|
||||||
@@ -2436,8 +2469,8 @@ msgid "View your 200 most recent alerts."
|
|||||||
msgstr "直近200件のアラートを表示します。"
|
msgstr "直近200件のアラートを表示します。"
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "Visible Fields"
|
#~ msgid "Visible Fields"
|
||||||
msgstr "表示列"
|
#~ msgstr "表示列"
|
||||||
|
|
||||||
#: src/components/routes/domain.tsx
|
#: src/components/routes/domain.tsx
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
@@ -2468,7 +2501,6 @@ msgstr "警告のしきい値"
|
|||||||
msgid "Webhook / Push notifications"
|
msgid "Webhook / Push notifications"
|
||||||
msgstr "Webhook / プッシュ通知"
|
msgstr "Webhook / プッシュ通知"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Website & Service Monitoring"
|
msgid "Website & Service Monitoring"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|||||||
@@ -230,6 +230,15 @@ msgstr "모든 컨테이너"
|
|||||||
msgid "All Systems"
|
msgid "All Systems"
|
||||||
msgstr "모든 시스템"
|
msgstr "모든 시스템"
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "All Tags"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "All Types"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "App update"
|
msgid "App update"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -465,8 +474,8 @@ msgstr ""
|
|||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "Check Now"
|
#~ msgid "Check Now"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/heartbeat.tsx
|
#: src/components/routes/settings/heartbeat.tsx
|
||||||
msgid "Check your monitoring service"
|
msgid "Check your monitoring service"
|
||||||
@@ -503,8 +512,8 @@ msgid "Close"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
#~ msgid "Columns"
|
msgid "Columns"
|
||||||
#~ msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
@@ -784,6 +793,14 @@ msgctxt "Layout display options"
|
|||||||
msgid "Display"
|
msgid "Display"
|
||||||
msgstr "표시"
|
msgstr "표시"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "Display"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
msgid "Display Columns"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/domain.tsx
|
#: src/components/routes/domain.tsx
|
||||||
#~ msgid "DNS Records"
|
#~ msgid "DNS Records"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
@@ -814,7 +831,6 @@ msgid "Domain and SSL expiry calendar"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/domains-table/domains-table.tsx
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Domain Monitoring"
|
msgid "Domain Monitoring"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1407,7 +1423,6 @@ msgid "Monitor updated successfully"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
msgid "Monitor websites, APIs, and services"
|
msgid "Monitor websites, APIs, and services"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -1448,6 +1463,10 @@ msgstr ""
|
|||||||
msgid "Net"
|
msgid "Net"
|
||||||
msgstr "네트워크"
|
msgstr "네트워크"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
#~ msgid "Network (Grouped)"
|
||||||
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system/charts/network-charts.tsx
|
#: src/components/routes/system/charts/network-charts.tsx
|
||||||
msgid "Network traffic of docker containers"
|
msgid "Network traffic of docker containers"
|
||||||
msgstr "Docker 컨테이너의 네트워크 트래픽"
|
msgstr "Docker 컨테이너의 네트워크 트래픽"
|
||||||
@@ -1471,8 +1490,8 @@ msgid "No"
|
|||||||
msgstr "아니오"
|
msgstr "아니오"
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "No data available for selected time range"
|
#~ msgid "No data available for selected time range"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/incidents.tsx
|
#: src/components/routes/incidents.tsx
|
||||||
msgid "No incidents found."
|
msgid "No incidents found."
|
||||||
@@ -1553,6 +1572,11 @@ msgstr "메뉴 열기"
|
|||||||
msgid "Optional description for this monitor"
|
msgid "Optional description for this monitor"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "Options"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/login/auth-form.tsx
|
#: src/components/login/auth-form.tsx
|
||||||
msgid "Or continue with"
|
msgid "Or continue with"
|
||||||
msgstr "또는 아래 항목으로 진행하기"
|
msgstr "또는 아래 항목으로 진행하기"
|
||||||
@@ -1626,6 +1650,8 @@ msgid "Paused"
|
|||||||
msgstr "일시 정지됨"
|
msgstr "일시 정지됨"
|
||||||
|
|
||||||
#. placeholder {0}: stats.paused
|
#. placeholder {0}: stats.paused
|
||||||
|
#. placeholder {0}: statusCounts.paused
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
msgid "Paused ({0})"
|
msgid "Paused ({0})"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1804,6 +1830,10 @@ msgstr "해결됨"
|
|||||||
msgid "Response"
|
msgid "Response"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/routes/monitor.tsx
|
||||||
|
msgid "Response time over the selected period"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
#~ msgid "Response Times"
|
#~ msgid "Response Times"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
@@ -2004,7 +2034,6 @@ msgstr "상태"
|
|||||||
|
|
||||||
#: src/components/containers-table/containers-table-columns.tsx
|
#: src/components/containers-table/containers-table-columns.tsx
|
||||||
#: src/components/domains-table/domains-table.tsx
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/domains-table/domains-table.tsx
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
@@ -2016,12 +2045,15 @@ msgid "Status"
|
|||||||
msgstr "상태"
|
msgstr "상태"
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "Status and response time over the selected period"
|
#~ msgid "Status and response time over the selected period"
|
||||||
|
#~ msgstr ""
|
||||||
|
|
||||||
|
#: src/components/routes/status-pages.tsx
|
||||||
|
msgid "Status Page Manager"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
#: src/components/routes/status-pages.tsx
|
|
||||||
msgid "Status Pages"
|
msgid "Status Pages"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -2065,8 +2097,8 @@ msgid "System load averages over time"
|
|||||||
msgstr "시간에 따른 시스템 부하 평균"
|
msgstr "시간에 따른 시스템 부하 평균"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
msgid "System Monitoring"
|
#~ msgid "System Monitoring"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/systemd-table/systemd-table.tsx
|
#: src/components/systemd-table/systemd-table.tsx
|
||||||
msgid "Systemd Services"
|
msgid "Systemd Services"
|
||||||
@@ -2091,6 +2123,8 @@ msgctxt "Tabs system layout option"
|
|||||||
msgid "Tabs"
|
msgid "Tabs"
|
||||||
msgstr "탭"
|
msgstr "탭"
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2213,7 +2247,6 @@ msgstr ""
|
|||||||
msgid "Total: {0}"
|
msgid "Total: {0}"
|
||||||
msgstr "총: {0}"
|
msgstr "총: {0}"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Track domain expiry dates and DNS status"
|
msgid "Track domain expiry dates and DNS status"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2223,8 +2256,8 @@ msgid "Track domain expiry dates and watch domains for purchase"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
msgid "Track system resources, containers, and health"
|
#~ msgid "Track system resources, containers, and health"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Track uptime, response times, and service health"
|
msgid "Track uptime, response times, and service health"
|
||||||
@@ -2282,6 +2315,7 @@ msgstr "시스템의 전원이 켜지거나 꺼질때 트리거됩니다."
|
|||||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||||
msgstr "디스크 사용량이 임계값을 초과할 때 트리거됩니다."
|
msgstr "디스크 사용량이 임계값을 초과할 때 트리거됩니다."
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/routes/settings/quiet-hours.tsx
|
#: src/components/routes/settings/quiet-hours.tsx
|
||||||
#: src/components/routes/settings/quiet-hours.tsx
|
#: src/components/routes/settings/quiet-hours.tsx
|
||||||
@@ -2416,7 +2450,6 @@ msgstr "사용률"
|
|||||||
msgid "Value"
|
msgid "Value"
|
||||||
msgstr "값"
|
msgstr "값"
|
||||||
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "View"
|
msgid "View"
|
||||||
msgstr "보기"
|
msgstr "보기"
|
||||||
@@ -2436,8 +2469,8 @@ msgid "View your 200 most recent alerts."
|
|||||||
msgstr "최근 200개의 알림을 봅니다."
|
msgstr "최근 200개의 알림을 봅니다."
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "Visible Fields"
|
#~ msgid "Visible Fields"
|
||||||
msgstr "표시할 열"
|
#~ msgstr "표시할 열"
|
||||||
|
|
||||||
#: src/components/routes/domain.tsx
|
#: src/components/routes/domain.tsx
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
@@ -2468,7 +2501,6 @@ msgstr "경고 임계값"
|
|||||||
msgid "Webhook / Push notifications"
|
msgid "Webhook / Push notifications"
|
||||||
msgstr "Webhook / 푸시 알림"
|
msgstr "Webhook / 푸시 알림"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Website & Service Monitoring"
|
msgid "Website & Service Monitoring"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|||||||
@@ -230,6 +230,15 @@ msgstr "Alle containers"
|
|||||||
msgid "All Systems"
|
msgid "All Systems"
|
||||||
msgstr "Alle systemen"
|
msgstr "Alle systemen"
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "All Tags"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "All Types"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "App update"
|
msgid "App update"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -465,8 +474,8 @@ msgstr ""
|
|||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "Check Now"
|
#~ msgid "Check Now"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/heartbeat.tsx
|
#: src/components/routes/settings/heartbeat.tsx
|
||||||
msgid "Check your monitoring service"
|
msgid "Check your monitoring service"
|
||||||
@@ -503,8 +512,8 @@ msgid "Close"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
#~ msgid "Columns"
|
msgid "Columns"
|
||||||
#~ msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
@@ -784,6 +793,14 @@ msgctxt "Layout display options"
|
|||||||
msgid "Display"
|
msgid "Display"
|
||||||
msgstr "Weergave"
|
msgstr "Weergave"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "Display"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
msgid "Display Columns"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/domain.tsx
|
#: src/components/routes/domain.tsx
|
||||||
#~ msgid "DNS Records"
|
#~ msgid "DNS Records"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
@@ -814,7 +831,6 @@ msgid "Domain and SSL expiry calendar"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/domains-table/domains-table.tsx
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Domain Monitoring"
|
msgid "Domain Monitoring"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1407,7 +1423,6 @@ msgid "Monitor updated successfully"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
msgid "Monitor websites, APIs, and services"
|
msgid "Monitor websites, APIs, and services"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -1448,6 +1463,10 @@ msgstr ""
|
|||||||
msgid "Net"
|
msgid "Net"
|
||||||
msgstr "Netwerk"
|
msgstr "Netwerk"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
#~ msgid "Network (Grouped)"
|
||||||
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system/charts/network-charts.tsx
|
#: src/components/routes/system/charts/network-charts.tsx
|
||||||
msgid "Network traffic of docker containers"
|
msgid "Network traffic of docker containers"
|
||||||
msgstr "Netwerkverkeer van docker containers"
|
msgstr "Netwerkverkeer van docker containers"
|
||||||
@@ -1471,8 +1490,8 @@ msgid "No"
|
|||||||
msgstr "Nee"
|
msgstr "Nee"
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "No data available for selected time range"
|
#~ msgid "No data available for selected time range"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/incidents.tsx
|
#: src/components/routes/incidents.tsx
|
||||||
msgid "No incidents found."
|
msgid "No incidents found."
|
||||||
@@ -1553,6 +1572,11 @@ msgstr "Menu openen"
|
|||||||
msgid "Optional description for this monitor"
|
msgid "Optional description for this monitor"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "Options"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/login/auth-form.tsx
|
#: src/components/login/auth-form.tsx
|
||||||
msgid "Or continue with"
|
msgid "Or continue with"
|
||||||
msgstr "Of ga verder met"
|
msgstr "Of ga verder met"
|
||||||
@@ -1626,6 +1650,8 @@ msgid "Paused"
|
|||||||
msgstr "Gepauzeerd"
|
msgstr "Gepauzeerd"
|
||||||
|
|
||||||
#. placeholder {0}: stats.paused
|
#. placeholder {0}: stats.paused
|
||||||
|
#. placeholder {0}: statusCounts.paused
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
msgid "Paused ({0})"
|
msgid "Paused ({0})"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1804,6 +1830,10 @@ msgstr "Opgelost"
|
|||||||
msgid "Response"
|
msgid "Response"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/routes/monitor.tsx
|
||||||
|
msgid "Response time over the selected period"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
#~ msgid "Response Times"
|
#~ msgid "Response Times"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
@@ -2004,7 +2034,6 @@ msgstr "Status"
|
|||||||
|
|
||||||
#: src/components/containers-table/containers-table-columns.tsx
|
#: src/components/containers-table/containers-table-columns.tsx
|
||||||
#: src/components/domains-table/domains-table.tsx
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/domains-table/domains-table.tsx
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
@@ -2016,12 +2045,15 @@ msgid "Status"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "Status and response time over the selected period"
|
#~ msgid "Status and response time over the selected period"
|
||||||
|
#~ msgstr ""
|
||||||
|
|
||||||
|
#: src/components/routes/status-pages.tsx
|
||||||
|
msgid "Status Page Manager"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
#: src/components/routes/status-pages.tsx
|
|
||||||
msgid "Status Pages"
|
msgid "Status Pages"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -2065,8 +2097,8 @@ msgid "System load averages over time"
|
|||||||
msgstr "Gemiddelde systeembelasting na verloop van tijd"
|
msgstr "Gemiddelde systeembelasting na verloop van tijd"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
msgid "System Monitoring"
|
#~ msgid "System Monitoring"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/systemd-table/systemd-table.tsx
|
#: src/components/systemd-table/systemd-table.tsx
|
||||||
msgid "Systemd Services"
|
msgid "Systemd Services"
|
||||||
@@ -2091,6 +2123,8 @@ msgctxt "Tabs system layout option"
|
|||||||
msgid "Tabs"
|
msgid "Tabs"
|
||||||
msgstr "Tabbladen"
|
msgstr "Tabbladen"
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2213,7 +2247,6 @@ msgstr ""
|
|||||||
msgid "Total: {0}"
|
msgid "Total: {0}"
|
||||||
msgstr "Totaal: {0}"
|
msgstr "Totaal: {0}"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Track domain expiry dates and DNS status"
|
msgid "Track domain expiry dates and DNS status"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2223,8 +2256,8 @@ msgid "Track domain expiry dates and watch domains for purchase"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
msgid "Track system resources, containers, and health"
|
#~ msgid "Track system resources, containers, and health"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Track uptime, response times, and service health"
|
msgid "Track uptime, response times, and service health"
|
||||||
@@ -2282,6 +2315,7 @@ msgstr "Triggert wanneer de status schakelt tussen up en down"
|
|||||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||||
msgstr "Triggert wanneer het gebruik van een schijf een drempelwaarde overschrijdt"
|
msgstr "Triggert wanneer het gebruik van een schijf een drempelwaarde overschrijdt"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/routes/settings/quiet-hours.tsx
|
#: src/components/routes/settings/quiet-hours.tsx
|
||||||
#: src/components/routes/settings/quiet-hours.tsx
|
#: src/components/routes/settings/quiet-hours.tsx
|
||||||
@@ -2416,7 +2450,6 @@ msgstr "Gebruik"
|
|||||||
msgid "Value"
|
msgid "Value"
|
||||||
msgstr "Waarde"
|
msgstr "Waarde"
|
||||||
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "View"
|
msgid "View"
|
||||||
msgstr "Weergave"
|
msgstr "Weergave"
|
||||||
@@ -2436,8 +2469,8 @@ msgid "View your 200 most recent alerts."
|
|||||||
msgstr "Bekijk je 200 meest recente meldingen."
|
msgstr "Bekijk je 200 meest recente meldingen."
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "Visible Fields"
|
#~ msgid "Visible Fields"
|
||||||
msgstr "Zichtbare kolommen"
|
#~ msgstr "Zichtbare kolommen"
|
||||||
|
|
||||||
#: src/components/routes/domain.tsx
|
#: src/components/routes/domain.tsx
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
@@ -2468,7 +2501,6 @@ msgstr "Waarschuwingsdrempels"
|
|||||||
msgid "Webhook / Push notifications"
|
msgid "Webhook / Push notifications"
|
||||||
msgstr "Webhook / Pushmeldingen"
|
msgstr "Webhook / Pushmeldingen"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Website & Service Monitoring"
|
msgid "Website & Service Monitoring"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|||||||
@@ -230,6 +230,15 @@ msgstr "Alle containere"
|
|||||||
msgid "All Systems"
|
msgid "All Systems"
|
||||||
msgstr "Alle Systemer"
|
msgstr "Alle Systemer"
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "All Tags"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "All Types"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "App update"
|
msgid "App update"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -465,8 +474,8 @@ msgstr ""
|
|||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "Check Now"
|
#~ msgid "Check Now"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/heartbeat.tsx
|
#: src/components/routes/settings/heartbeat.tsx
|
||||||
msgid "Check your monitoring service"
|
msgid "Check your monitoring service"
|
||||||
@@ -503,8 +512,8 @@ msgid "Close"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
#~ msgid "Columns"
|
msgid "Columns"
|
||||||
#~ msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
@@ -784,6 +793,14 @@ msgctxt "Layout display options"
|
|||||||
msgid "Display"
|
msgid "Display"
|
||||||
msgstr "Vis"
|
msgstr "Vis"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "Display"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
msgid "Display Columns"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/domain.tsx
|
#: src/components/routes/domain.tsx
|
||||||
#~ msgid "DNS Records"
|
#~ msgid "DNS Records"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
@@ -814,7 +831,6 @@ msgid "Domain and SSL expiry calendar"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/domains-table/domains-table.tsx
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Domain Monitoring"
|
msgid "Domain Monitoring"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1407,7 +1423,6 @@ msgid "Monitor updated successfully"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
msgid "Monitor websites, APIs, and services"
|
msgid "Monitor websites, APIs, and services"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -1448,6 +1463,10 @@ msgstr ""
|
|||||||
msgid "Net"
|
msgid "Net"
|
||||||
msgstr "Nett"
|
msgstr "Nett"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
#~ msgid "Network (Grouped)"
|
||||||
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system/charts/network-charts.tsx
|
#: src/components/routes/system/charts/network-charts.tsx
|
||||||
msgid "Network traffic of docker containers"
|
msgid "Network traffic of docker containers"
|
||||||
msgstr "Nettverkstrafikk av docker-konteinere"
|
msgstr "Nettverkstrafikk av docker-konteinere"
|
||||||
@@ -1471,8 +1490,8 @@ msgid "No"
|
|||||||
msgstr "Nei"
|
msgstr "Nei"
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "No data available for selected time range"
|
#~ msgid "No data available for selected time range"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/incidents.tsx
|
#: src/components/routes/incidents.tsx
|
||||||
msgid "No incidents found."
|
msgid "No incidents found."
|
||||||
@@ -1553,6 +1572,11 @@ msgstr "Åpne meny"
|
|||||||
msgid "Optional description for this monitor"
|
msgid "Optional description for this monitor"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "Options"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/login/auth-form.tsx
|
#: src/components/login/auth-form.tsx
|
||||||
msgid "Or continue with"
|
msgid "Or continue with"
|
||||||
msgstr "Eller fortsett med"
|
msgstr "Eller fortsett med"
|
||||||
@@ -1626,6 +1650,8 @@ msgid "Paused"
|
|||||||
msgstr "Satt på Pause"
|
msgstr "Satt på Pause"
|
||||||
|
|
||||||
#. placeholder {0}: stats.paused
|
#. placeholder {0}: stats.paused
|
||||||
|
#. placeholder {0}: statusCounts.paused
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
msgid "Paused ({0})"
|
msgid "Paused ({0})"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1804,6 +1830,10 @@ msgstr "Løst"
|
|||||||
msgid "Response"
|
msgid "Response"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/routes/monitor.tsx
|
||||||
|
msgid "Response time over the selected period"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
#~ msgid "Response Times"
|
#~ msgid "Response Times"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
@@ -2004,7 +2034,6 @@ msgstr "Tilstand"
|
|||||||
|
|
||||||
#: src/components/containers-table/containers-table-columns.tsx
|
#: src/components/containers-table/containers-table-columns.tsx
|
||||||
#: src/components/domains-table/domains-table.tsx
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/domains-table/domains-table.tsx
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
@@ -2016,12 +2045,15 @@ msgid "Status"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "Status and response time over the selected period"
|
#~ msgid "Status and response time over the selected period"
|
||||||
|
#~ msgstr ""
|
||||||
|
|
||||||
|
#: src/components/routes/status-pages.tsx
|
||||||
|
msgid "Status Page Manager"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
#: src/components/routes/status-pages.tsx
|
|
||||||
msgid "Status Pages"
|
msgid "Status Pages"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -2065,8 +2097,8 @@ msgid "System load averages over time"
|
|||||||
msgstr "Systembelastning gjennomsnitt over tid"
|
msgstr "Systembelastning gjennomsnitt over tid"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
msgid "System Monitoring"
|
#~ msgid "System Monitoring"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/systemd-table/systemd-table.tsx
|
#: src/components/systemd-table/systemd-table.tsx
|
||||||
msgid "Systemd Services"
|
msgid "Systemd Services"
|
||||||
@@ -2091,6 +2123,8 @@ msgctxt "Tabs system layout option"
|
|||||||
msgid "Tabs"
|
msgid "Tabs"
|
||||||
msgstr "Faner"
|
msgstr "Faner"
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2213,7 +2247,6 @@ msgstr ""
|
|||||||
msgid "Total: {0}"
|
msgid "Total: {0}"
|
||||||
msgstr "Totalt: {0}"
|
msgstr "Totalt: {0}"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Track domain expiry dates and DNS status"
|
msgid "Track domain expiry dates and DNS status"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2223,8 +2256,8 @@ msgid "Track domain expiry dates and watch domains for purchase"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
msgid "Track system resources, containers, and health"
|
#~ msgid "Track system resources, containers, and health"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Track uptime, response times, and service health"
|
msgid "Track uptime, response times, and service health"
|
||||||
@@ -2282,6 +2315,7 @@ msgstr "Slår inn når statusen veksler mellom oppe og nede"
|
|||||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||||
msgstr "Slår inn når forbruk av hvilken som helst disk overstiger en grenseverdi"
|
msgstr "Slår inn når forbruk av hvilken som helst disk overstiger en grenseverdi"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/routes/settings/quiet-hours.tsx
|
#: src/components/routes/settings/quiet-hours.tsx
|
||||||
#: src/components/routes/settings/quiet-hours.tsx
|
#: src/components/routes/settings/quiet-hours.tsx
|
||||||
@@ -2416,7 +2450,6 @@ msgstr "Utnyttelse"
|
|||||||
msgid "Value"
|
msgid "Value"
|
||||||
msgstr "Verdi"
|
msgstr "Verdi"
|
||||||
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "View"
|
msgid "View"
|
||||||
msgstr "Visning"
|
msgstr "Visning"
|
||||||
@@ -2436,8 +2469,8 @@ msgid "View your 200 most recent alerts."
|
|||||||
msgstr "Vis de 200 siste varslene."
|
msgstr "Vis de 200 siste varslene."
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "Visible Fields"
|
#~ msgid "Visible Fields"
|
||||||
msgstr "Synlige Felter"
|
#~ msgstr "Synlige Felter"
|
||||||
|
|
||||||
#: src/components/routes/domain.tsx
|
#: src/components/routes/domain.tsx
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
@@ -2468,7 +2501,6 @@ msgstr "Advarselsterskler"
|
|||||||
msgid "Webhook / Push notifications"
|
msgid "Webhook / Push notifications"
|
||||||
msgstr "Webhook / Push-varslinger"
|
msgstr "Webhook / Push-varslinger"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Website & Service Monitoring"
|
msgid "Website & Service Monitoring"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|||||||
@@ -230,6 +230,15 @@ msgstr "Wszystkie kontenery"
|
|||||||
msgid "All Systems"
|
msgid "All Systems"
|
||||||
msgstr "Wszystkie systemy"
|
msgstr "Wszystkie systemy"
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "All Tags"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "All Types"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "App update"
|
msgid "App update"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -465,8 +474,8 @@ msgstr ""
|
|||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "Check Now"
|
#~ msgid "Check Now"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/heartbeat.tsx
|
#: src/components/routes/settings/heartbeat.tsx
|
||||||
msgid "Check your monitoring service"
|
msgid "Check your monitoring service"
|
||||||
@@ -503,8 +512,8 @@ msgid "Close"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
#~ msgid "Columns"
|
msgid "Columns"
|
||||||
#~ msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
@@ -784,6 +793,14 @@ msgctxt "Layout display options"
|
|||||||
msgid "Display"
|
msgid "Display"
|
||||||
msgstr "Widok"
|
msgstr "Widok"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "Display"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
msgid "Display Columns"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/domain.tsx
|
#: src/components/routes/domain.tsx
|
||||||
#~ msgid "DNS Records"
|
#~ msgid "DNS Records"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
@@ -814,7 +831,6 @@ msgid "Domain and SSL expiry calendar"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/domains-table/domains-table.tsx
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Domain Monitoring"
|
msgid "Domain Monitoring"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1407,7 +1423,6 @@ msgid "Monitor updated successfully"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
msgid "Monitor websites, APIs, and services"
|
msgid "Monitor websites, APIs, and services"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -1448,6 +1463,10 @@ msgstr ""
|
|||||||
msgid "Net"
|
msgid "Net"
|
||||||
msgstr "Sieć"
|
msgstr "Sieć"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
#~ msgid "Network (Grouped)"
|
||||||
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system/charts/network-charts.tsx
|
#: src/components/routes/system/charts/network-charts.tsx
|
||||||
msgid "Network traffic of docker containers"
|
msgid "Network traffic of docker containers"
|
||||||
msgstr "Ruch sieciowy kontenerów Docker."
|
msgstr "Ruch sieciowy kontenerów Docker."
|
||||||
@@ -1471,8 +1490,8 @@ msgid "No"
|
|||||||
msgstr "Nie"
|
msgstr "Nie"
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "No data available for selected time range"
|
#~ msgid "No data available for selected time range"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/incidents.tsx
|
#: src/components/routes/incidents.tsx
|
||||||
msgid "No incidents found."
|
msgid "No incidents found."
|
||||||
@@ -1553,6 +1572,11 @@ msgstr "Otwórz menu"
|
|||||||
msgid "Optional description for this monitor"
|
msgid "Optional description for this monitor"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "Options"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/login/auth-form.tsx
|
#: src/components/login/auth-form.tsx
|
||||||
msgid "Or continue with"
|
msgid "Or continue with"
|
||||||
msgstr "Lub kontynuuj z"
|
msgstr "Lub kontynuuj z"
|
||||||
@@ -1626,6 +1650,8 @@ msgid "Paused"
|
|||||||
msgstr "Wstrzymane"
|
msgstr "Wstrzymane"
|
||||||
|
|
||||||
#. placeholder {0}: stats.paused
|
#. placeholder {0}: stats.paused
|
||||||
|
#. placeholder {0}: statusCounts.paused
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
msgid "Paused ({0})"
|
msgid "Paused ({0})"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1804,6 +1830,10 @@ msgstr "Rozwiązany"
|
|||||||
msgid "Response"
|
msgid "Response"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/routes/monitor.tsx
|
||||||
|
msgid "Response time over the selected period"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
#~ msgid "Response Times"
|
#~ msgid "Response Times"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
@@ -2004,7 +2034,6 @@ msgstr "Stan"
|
|||||||
|
|
||||||
#: src/components/containers-table/containers-table-columns.tsx
|
#: src/components/containers-table/containers-table-columns.tsx
|
||||||
#: src/components/domains-table/domains-table.tsx
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/domains-table/domains-table.tsx
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
@@ -2016,12 +2045,15 @@ msgid "Status"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "Status and response time over the selected period"
|
#~ msgid "Status and response time over the selected period"
|
||||||
|
#~ msgstr ""
|
||||||
|
|
||||||
|
#: src/components/routes/status-pages.tsx
|
||||||
|
msgid "Status Page Manager"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
#: src/components/routes/status-pages.tsx
|
|
||||||
msgid "Status Pages"
|
msgid "Status Pages"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -2065,8 +2097,8 @@ msgid "System load averages over time"
|
|||||||
msgstr "Średnie obciążenie systemu w czasie"
|
msgstr "Średnie obciążenie systemu w czasie"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
msgid "System Monitoring"
|
#~ msgid "System Monitoring"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/systemd-table/systemd-table.tsx
|
#: src/components/systemd-table/systemd-table.tsx
|
||||||
msgid "Systemd Services"
|
msgid "Systemd Services"
|
||||||
@@ -2091,6 +2123,8 @@ msgctxt "Tabs system layout option"
|
|||||||
msgid "Tabs"
|
msgid "Tabs"
|
||||||
msgstr "Karty"
|
msgstr "Karty"
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2213,7 +2247,6 @@ msgstr ""
|
|||||||
msgid "Total: {0}"
|
msgid "Total: {0}"
|
||||||
msgstr "Łącznie: {0}"
|
msgstr "Łącznie: {0}"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Track domain expiry dates and DNS status"
|
msgid "Track domain expiry dates and DNS status"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2223,8 +2256,8 @@ msgid "Track domain expiry dates and watch domains for purchase"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
msgid "Track system resources, containers, and health"
|
#~ msgid "Track system resources, containers, and health"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Track uptime, response times, and service health"
|
msgid "Track uptime, response times, and service health"
|
||||||
@@ -2282,6 +2315,7 @@ msgstr "Wyzwalane, gdy status przełącza się między stanem aktywnym a nieakty
|
|||||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||||
msgstr "Wyzwalane, gdy wykorzystanie któregokolwiek dysku przekroczy ustalony próg"
|
msgstr "Wyzwalane, gdy wykorzystanie któregokolwiek dysku przekroczy ustalony próg"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/routes/settings/quiet-hours.tsx
|
#: src/components/routes/settings/quiet-hours.tsx
|
||||||
#: src/components/routes/settings/quiet-hours.tsx
|
#: src/components/routes/settings/quiet-hours.tsx
|
||||||
@@ -2416,7 +2450,6 @@ msgstr "Użycie"
|
|||||||
msgid "Value"
|
msgid "Value"
|
||||||
msgstr "Wartość"
|
msgstr "Wartość"
|
||||||
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "View"
|
msgid "View"
|
||||||
msgstr "Widok"
|
msgstr "Widok"
|
||||||
@@ -2436,8 +2469,8 @@ msgid "View your 200 most recent alerts."
|
|||||||
msgstr "Wyświetl 200 ostatnich alertów."
|
msgstr "Wyświetl 200 ostatnich alertów."
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "Visible Fields"
|
#~ msgid "Visible Fields"
|
||||||
msgstr "Widoczne kolumny"
|
#~ msgstr "Widoczne kolumny"
|
||||||
|
|
||||||
#: src/components/routes/domain.tsx
|
#: src/components/routes/domain.tsx
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
@@ -2468,7 +2501,6 @@ msgstr "Progi ostrzegawcze"
|
|||||||
msgid "Webhook / Push notifications"
|
msgid "Webhook / Push notifications"
|
||||||
msgstr "Webhook / Powiadomienia push"
|
msgstr "Webhook / Powiadomienia push"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Website & Service Monitoring"
|
msgid "Website & Service Monitoring"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|||||||
@@ -230,6 +230,15 @@ msgstr "Todos os Contêineres"
|
|||||||
msgid "All Systems"
|
msgid "All Systems"
|
||||||
msgstr "Todos os Sistemas"
|
msgstr "Todos os Sistemas"
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "All Tags"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "All Types"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "App update"
|
msgid "App update"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -465,8 +474,8 @@ msgstr ""
|
|||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "Check Now"
|
#~ msgid "Check Now"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/heartbeat.tsx
|
#: src/components/routes/settings/heartbeat.tsx
|
||||||
msgid "Check your monitoring service"
|
msgid "Check your monitoring service"
|
||||||
@@ -503,8 +512,8 @@ msgid "Close"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
#~ msgid "Columns"
|
msgid "Columns"
|
||||||
#~ msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
@@ -784,6 +793,14 @@ msgctxt "Layout display options"
|
|||||||
msgid "Display"
|
msgid "Display"
|
||||||
msgstr "Ecrã"
|
msgstr "Ecrã"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "Display"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
msgid "Display Columns"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/domain.tsx
|
#: src/components/routes/domain.tsx
|
||||||
#~ msgid "DNS Records"
|
#~ msgid "DNS Records"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
@@ -814,7 +831,6 @@ msgid "Domain and SSL expiry calendar"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/domains-table/domains-table.tsx
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Domain Monitoring"
|
msgid "Domain Monitoring"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1407,7 +1423,6 @@ msgid "Monitor updated successfully"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
msgid "Monitor websites, APIs, and services"
|
msgid "Monitor websites, APIs, and services"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -1448,6 +1463,10 @@ msgstr ""
|
|||||||
msgid "Net"
|
msgid "Net"
|
||||||
msgstr "Rede"
|
msgstr "Rede"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
#~ msgid "Network (Grouped)"
|
||||||
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system/charts/network-charts.tsx
|
#: src/components/routes/system/charts/network-charts.tsx
|
||||||
msgid "Network traffic of docker containers"
|
msgid "Network traffic of docker containers"
|
||||||
msgstr "Tráfego de rede dos contêineres Docker"
|
msgstr "Tráfego de rede dos contêineres Docker"
|
||||||
@@ -1471,8 +1490,8 @@ msgid "No"
|
|||||||
msgstr "Não"
|
msgstr "Não"
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "No data available for selected time range"
|
#~ msgid "No data available for selected time range"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/incidents.tsx
|
#: src/components/routes/incidents.tsx
|
||||||
msgid "No incidents found."
|
msgid "No incidents found."
|
||||||
@@ -1553,6 +1572,11 @@ msgstr "Abrir menu"
|
|||||||
msgid "Optional description for this monitor"
|
msgid "Optional description for this monitor"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "Options"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/login/auth-form.tsx
|
#: src/components/login/auth-form.tsx
|
||||||
msgid "Or continue with"
|
msgid "Or continue with"
|
||||||
msgstr "Ou continue com"
|
msgstr "Ou continue com"
|
||||||
@@ -1626,6 +1650,8 @@ msgid "Paused"
|
|||||||
msgstr "Pausado"
|
msgstr "Pausado"
|
||||||
|
|
||||||
#. placeholder {0}: stats.paused
|
#. placeholder {0}: stats.paused
|
||||||
|
#. placeholder {0}: statusCounts.paused
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
msgid "Paused ({0})"
|
msgid "Paused ({0})"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1804,6 +1830,10 @@ msgstr "Resolvido"
|
|||||||
msgid "Response"
|
msgid "Response"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/routes/monitor.tsx
|
||||||
|
msgid "Response time over the selected period"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
#~ msgid "Response Times"
|
#~ msgid "Response Times"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
@@ -2004,7 +2034,6 @@ msgstr "Estado"
|
|||||||
|
|
||||||
#: src/components/containers-table/containers-table-columns.tsx
|
#: src/components/containers-table/containers-table-columns.tsx
|
||||||
#: src/components/domains-table/domains-table.tsx
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/domains-table/domains-table.tsx
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
@@ -2016,12 +2045,15 @@ msgid "Status"
|
|||||||
msgstr "Estado"
|
msgstr "Estado"
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "Status and response time over the selected period"
|
#~ msgid "Status and response time over the selected period"
|
||||||
|
#~ msgstr ""
|
||||||
|
|
||||||
|
#: src/components/routes/status-pages.tsx
|
||||||
|
msgid "Status Page Manager"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
#: src/components/routes/status-pages.tsx
|
|
||||||
msgid "Status Pages"
|
msgid "Status Pages"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -2065,8 +2097,8 @@ msgid "System load averages over time"
|
|||||||
msgstr "Médias de carga do sistema ao longo do tempo"
|
msgstr "Médias de carga do sistema ao longo do tempo"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
msgid "System Monitoring"
|
#~ msgid "System Monitoring"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/systemd-table/systemd-table.tsx
|
#: src/components/systemd-table/systemd-table.tsx
|
||||||
msgid "Systemd Services"
|
msgid "Systemd Services"
|
||||||
@@ -2091,6 +2123,8 @@ msgctxt "Tabs system layout option"
|
|||||||
msgid "Tabs"
|
msgid "Tabs"
|
||||||
msgstr "Separadores"
|
msgstr "Separadores"
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2213,7 +2247,6 @@ msgstr ""
|
|||||||
msgid "Total: {0}"
|
msgid "Total: {0}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Track domain expiry dates and DNS status"
|
msgid "Track domain expiry dates and DNS status"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2223,8 +2256,8 @@ msgid "Track domain expiry dates and watch domains for purchase"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
msgid "Track system resources, containers, and health"
|
#~ msgid "Track system resources, containers, and health"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Track uptime, response times, and service health"
|
msgid "Track uptime, response times, and service health"
|
||||||
@@ -2282,6 +2315,7 @@ msgstr "Dispara quando o status alterna entre ativo e inativo"
|
|||||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||||
msgstr "Dispara quando o uso de qualquer disco excede um limite"
|
msgstr "Dispara quando o uso de qualquer disco excede um limite"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/routes/settings/quiet-hours.tsx
|
#: src/components/routes/settings/quiet-hours.tsx
|
||||||
#: src/components/routes/settings/quiet-hours.tsx
|
#: src/components/routes/settings/quiet-hours.tsx
|
||||||
@@ -2416,7 +2450,6 @@ msgstr "Utilização"
|
|||||||
msgid "Value"
|
msgid "Value"
|
||||||
msgstr "Valor"
|
msgstr "Valor"
|
||||||
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "View"
|
msgid "View"
|
||||||
msgstr "Visual"
|
msgstr "Visual"
|
||||||
@@ -2436,8 +2469,8 @@ msgid "View your 200 most recent alerts."
|
|||||||
msgstr "Veja os seus 200 alertas mais recentes."
|
msgstr "Veja os seus 200 alertas mais recentes."
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "Visible Fields"
|
#~ msgid "Visible Fields"
|
||||||
msgstr "Campos Visíveis"
|
#~ msgstr "Campos Visíveis"
|
||||||
|
|
||||||
#: src/components/routes/domain.tsx
|
#: src/components/routes/domain.tsx
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
@@ -2468,7 +2501,6 @@ msgstr "Limites de aviso"
|
|||||||
msgid "Webhook / Push notifications"
|
msgid "Webhook / Push notifications"
|
||||||
msgstr "Notificações Webhook / Push"
|
msgstr "Notificações Webhook / Push"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Website & Service Monitoring"
|
msgid "Website & Service Monitoring"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|||||||
@@ -230,6 +230,15 @@ msgstr "Все контейнеры"
|
|||||||
msgid "All Systems"
|
msgid "All Systems"
|
||||||
msgstr "Все системы"
|
msgstr "Все системы"
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "All Tags"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "All Types"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "App update"
|
msgid "App update"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -465,8 +474,8 @@ msgstr ""
|
|||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "Check Now"
|
#~ msgid "Check Now"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/heartbeat.tsx
|
#: src/components/routes/settings/heartbeat.tsx
|
||||||
msgid "Check your monitoring service"
|
msgid "Check your monitoring service"
|
||||||
@@ -503,8 +512,8 @@ msgid "Close"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
#~ msgid "Columns"
|
msgid "Columns"
|
||||||
#~ msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
@@ -784,6 +793,14 @@ msgctxt "Layout display options"
|
|||||||
msgid "Display"
|
msgid "Display"
|
||||||
msgstr "Отображение"
|
msgstr "Отображение"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "Display"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
msgid "Display Columns"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/domain.tsx
|
#: src/components/routes/domain.tsx
|
||||||
#~ msgid "DNS Records"
|
#~ msgid "DNS Records"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
@@ -814,7 +831,6 @@ msgid "Domain and SSL expiry calendar"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/domains-table/domains-table.tsx
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Domain Monitoring"
|
msgid "Domain Monitoring"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1407,7 +1423,6 @@ msgid "Monitor updated successfully"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
msgid "Monitor websites, APIs, and services"
|
msgid "Monitor websites, APIs, and services"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -1448,6 +1463,10 @@ msgstr ""
|
|||||||
msgid "Net"
|
msgid "Net"
|
||||||
msgstr "Сеть"
|
msgstr "Сеть"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
#~ msgid "Network (Grouped)"
|
||||||
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system/charts/network-charts.tsx
|
#: src/components/routes/system/charts/network-charts.tsx
|
||||||
msgid "Network traffic of docker containers"
|
msgid "Network traffic of docker containers"
|
||||||
msgstr "Сетевой трафик контейнеров Docker"
|
msgstr "Сетевой трафик контейнеров Docker"
|
||||||
@@ -1471,8 +1490,8 @@ msgid "No"
|
|||||||
msgstr "Нет"
|
msgstr "Нет"
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "No data available for selected time range"
|
#~ msgid "No data available for selected time range"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/incidents.tsx
|
#: src/components/routes/incidents.tsx
|
||||||
msgid "No incidents found."
|
msgid "No incidents found."
|
||||||
@@ -1553,6 +1572,11 @@ msgstr "Открыть меню"
|
|||||||
msgid "Optional description for this monitor"
|
msgid "Optional description for this monitor"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "Options"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/login/auth-form.tsx
|
#: src/components/login/auth-form.tsx
|
||||||
msgid "Or continue with"
|
msgid "Or continue with"
|
||||||
msgstr "Или продолжить с"
|
msgstr "Или продолжить с"
|
||||||
@@ -1626,6 +1650,8 @@ msgid "Paused"
|
|||||||
msgstr "Пауза"
|
msgstr "Пауза"
|
||||||
|
|
||||||
#. placeholder {0}: stats.paused
|
#. placeholder {0}: stats.paused
|
||||||
|
#. placeholder {0}: statusCounts.paused
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
msgid "Paused ({0})"
|
msgid "Paused ({0})"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1804,6 +1830,10 @@ msgstr "Завершено"
|
|||||||
msgid "Response"
|
msgid "Response"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/routes/monitor.tsx
|
||||||
|
msgid "Response time over the selected period"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
#~ msgid "Response Times"
|
#~ msgid "Response Times"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
@@ -2004,7 +2034,6 @@ msgstr "Состояние"
|
|||||||
|
|
||||||
#: src/components/containers-table/containers-table-columns.tsx
|
#: src/components/containers-table/containers-table-columns.tsx
|
||||||
#: src/components/domains-table/domains-table.tsx
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/domains-table/domains-table.tsx
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
@@ -2016,12 +2045,15 @@ msgid "Status"
|
|||||||
msgstr "Статус"
|
msgstr "Статус"
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "Status and response time over the selected period"
|
#~ msgid "Status and response time over the selected period"
|
||||||
|
#~ msgstr ""
|
||||||
|
|
||||||
|
#: src/components/routes/status-pages.tsx
|
||||||
|
msgid "Status Page Manager"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
#: src/components/routes/status-pages.tsx
|
|
||||||
msgid "Status Pages"
|
msgid "Status Pages"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -2065,8 +2097,8 @@ msgid "System load averages over time"
|
|||||||
msgstr "Средняя загрузка системы за время"
|
msgstr "Средняя загрузка системы за время"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
msgid "System Monitoring"
|
#~ msgid "System Monitoring"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/systemd-table/systemd-table.tsx
|
#: src/components/systemd-table/systemd-table.tsx
|
||||||
msgid "Systemd Services"
|
msgid "Systemd Services"
|
||||||
@@ -2091,6 +2123,8 @@ msgctxt "Tabs system layout option"
|
|||||||
msgid "Tabs"
|
msgid "Tabs"
|
||||||
msgstr "Вкладки"
|
msgstr "Вкладки"
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2213,7 +2247,6 @@ msgstr ""
|
|||||||
msgid "Total: {0}"
|
msgid "Total: {0}"
|
||||||
msgstr "Всего: {0}"
|
msgstr "Всего: {0}"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Track domain expiry dates and DNS status"
|
msgid "Track domain expiry dates and DNS status"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2223,8 +2256,8 @@ msgid "Track domain expiry dates and watch domains for purchase"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
msgid "Track system resources, containers, and health"
|
#~ msgid "Track system resources, containers, and health"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Track uptime, response times, and service health"
|
msgid "Track uptime, response times, and service health"
|
||||||
@@ -2282,6 +2315,7 @@ msgstr "Срабатывает, когда статус переключаетс
|
|||||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||||
msgstr "Срабатывает, когда использование любого диска превышает порог"
|
msgstr "Срабатывает, когда использование любого диска превышает порог"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/routes/settings/quiet-hours.tsx
|
#: src/components/routes/settings/quiet-hours.tsx
|
||||||
#: src/components/routes/settings/quiet-hours.tsx
|
#: src/components/routes/settings/quiet-hours.tsx
|
||||||
@@ -2416,7 +2450,6 @@ msgstr "Использование"
|
|||||||
msgid "Value"
|
msgid "Value"
|
||||||
msgstr "Значение"
|
msgstr "Значение"
|
||||||
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "View"
|
msgid "View"
|
||||||
msgstr "Вид"
|
msgstr "Вид"
|
||||||
@@ -2436,8 +2469,8 @@ msgid "View your 200 most recent alerts."
|
|||||||
msgstr "Просмотреть 200 последних оповещений."
|
msgstr "Просмотреть 200 последних оповещений."
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "Visible Fields"
|
#~ msgid "Visible Fields"
|
||||||
msgstr "Видимые столбцы"
|
#~ msgstr "Видимые столбцы"
|
||||||
|
|
||||||
#: src/components/routes/domain.tsx
|
#: src/components/routes/domain.tsx
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
@@ -2468,7 +2501,6 @@ msgstr "Пороги предупреждения"
|
|||||||
msgid "Webhook / Push notifications"
|
msgid "Webhook / Push notifications"
|
||||||
msgstr "Webhook / Push уведомления"
|
msgstr "Webhook / Push уведомления"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Website & Service Monitoring"
|
msgid "Website & Service Monitoring"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|||||||
@@ -230,6 +230,15 @@ msgstr "Vsi kontejnerji"
|
|||||||
msgid "All Systems"
|
msgid "All Systems"
|
||||||
msgstr "Vsi sistemi"
|
msgstr "Vsi sistemi"
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "All Tags"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "All Types"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "App update"
|
msgid "App update"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -465,8 +474,8 @@ msgstr ""
|
|||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "Check Now"
|
#~ msgid "Check Now"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/heartbeat.tsx
|
#: src/components/routes/settings/heartbeat.tsx
|
||||||
msgid "Check your monitoring service"
|
msgid "Check your monitoring service"
|
||||||
@@ -503,8 +512,8 @@ msgid "Close"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
#~ msgid "Columns"
|
msgid "Columns"
|
||||||
#~ msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
@@ -784,6 +793,14 @@ msgctxt "Layout display options"
|
|||||||
msgid "Display"
|
msgid "Display"
|
||||||
msgstr "Zaslon"
|
msgstr "Zaslon"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "Display"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
msgid "Display Columns"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/domain.tsx
|
#: src/components/routes/domain.tsx
|
||||||
#~ msgid "DNS Records"
|
#~ msgid "DNS Records"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
@@ -814,7 +831,6 @@ msgid "Domain and SSL expiry calendar"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/domains-table/domains-table.tsx
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Domain Monitoring"
|
msgid "Domain Monitoring"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1407,7 +1423,6 @@ msgid "Monitor updated successfully"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
msgid "Monitor websites, APIs, and services"
|
msgid "Monitor websites, APIs, and services"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -1448,6 +1463,10 @@ msgstr ""
|
|||||||
msgid "Net"
|
msgid "Net"
|
||||||
msgstr "Mreža"
|
msgstr "Mreža"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
#~ msgid "Network (Grouped)"
|
||||||
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system/charts/network-charts.tsx
|
#: src/components/routes/system/charts/network-charts.tsx
|
||||||
msgid "Network traffic of docker containers"
|
msgid "Network traffic of docker containers"
|
||||||
msgstr "Omrežni promet docker kontejnerjev"
|
msgstr "Omrežni promet docker kontejnerjev"
|
||||||
@@ -1471,8 +1490,8 @@ msgid "No"
|
|||||||
msgstr "Ne"
|
msgstr "Ne"
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "No data available for selected time range"
|
#~ msgid "No data available for selected time range"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/incidents.tsx
|
#: src/components/routes/incidents.tsx
|
||||||
msgid "No incidents found."
|
msgid "No incidents found."
|
||||||
@@ -1553,6 +1572,11 @@ msgstr "Odpri menu"
|
|||||||
msgid "Optional description for this monitor"
|
msgid "Optional description for this monitor"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "Options"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/login/auth-form.tsx
|
#: src/components/login/auth-form.tsx
|
||||||
msgid "Or continue with"
|
msgid "Or continue with"
|
||||||
msgstr "Ali nadaljuj z"
|
msgstr "Ali nadaljuj z"
|
||||||
@@ -1626,6 +1650,8 @@ msgid "Paused"
|
|||||||
msgstr "Zaustavljeno"
|
msgstr "Zaustavljeno"
|
||||||
|
|
||||||
#. placeholder {0}: stats.paused
|
#. placeholder {0}: stats.paused
|
||||||
|
#. placeholder {0}: statusCounts.paused
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
msgid "Paused ({0})"
|
msgid "Paused ({0})"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1804,6 +1830,10 @@ msgstr "Rešeno"
|
|||||||
msgid "Response"
|
msgid "Response"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/routes/monitor.tsx
|
||||||
|
msgid "Response time over the selected period"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
#~ msgid "Response Times"
|
#~ msgid "Response Times"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
@@ -2004,7 +2034,6 @@ msgstr "Stanje"
|
|||||||
|
|
||||||
#: src/components/containers-table/containers-table-columns.tsx
|
#: src/components/containers-table/containers-table-columns.tsx
|
||||||
#: src/components/domains-table/domains-table.tsx
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/domains-table/domains-table.tsx
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
@@ -2016,12 +2045,15 @@ msgid "Status"
|
|||||||
msgstr "Stanje"
|
msgstr "Stanje"
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "Status and response time over the selected period"
|
#~ msgid "Status and response time over the selected period"
|
||||||
|
#~ msgstr ""
|
||||||
|
|
||||||
|
#: src/components/routes/status-pages.tsx
|
||||||
|
msgid "Status Page Manager"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
#: src/components/routes/status-pages.tsx
|
|
||||||
msgid "Status Pages"
|
msgid "Status Pages"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -2065,8 +2097,8 @@ msgid "System load averages over time"
|
|||||||
msgstr "Sistemske povprečne obremenitve skozi čas"
|
msgstr "Sistemske povprečne obremenitve skozi čas"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
msgid "System Monitoring"
|
#~ msgid "System Monitoring"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/systemd-table/systemd-table.tsx
|
#: src/components/systemd-table/systemd-table.tsx
|
||||||
msgid "Systemd Services"
|
msgid "Systemd Services"
|
||||||
@@ -2091,6 +2123,8 @@ msgctxt "Tabs system layout option"
|
|||||||
msgid "Tabs"
|
msgid "Tabs"
|
||||||
msgstr "Zavihki"
|
msgstr "Zavihki"
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2213,7 +2247,6 @@ msgstr ""
|
|||||||
msgid "Total: {0}"
|
msgid "Total: {0}"
|
||||||
msgstr "Skupaj: {0}"
|
msgstr "Skupaj: {0}"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Track domain expiry dates and DNS status"
|
msgid "Track domain expiry dates and DNS status"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2223,8 +2256,8 @@ msgid "Track domain expiry dates and watch domains for purchase"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
msgid "Track system resources, containers, and health"
|
#~ msgid "Track system resources, containers, and health"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Track uptime, response times, and service health"
|
msgid "Track uptime, response times, and service health"
|
||||||
@@ -2282,6 +2315,7 @@ msgstr "Sproži se, ko se stanje preklaplja med gor in dol"
|
|||||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||||
msgstr "Sproži se, ko uporaba katerega koli diska preseže prag"
|
msgstr "Sproži se, ko uporaba katerega koli diska preseže prag"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/routes/settings/quiet-hours.tsx
|
#: src/components/routes/settings/quiet-hours.tsx
|
||||||
#: src/components/routes/settings/quiet-hours.tsx
|
#: src/components/routes/settings/quiet-hours.tsx
|
||||||
@@ -2416,7 +2450,6 @@ msgstr "Izkoriščenost"
|
|||||||
msgid "Value"
|
msgid "Value"
|
||||||
msgstr "Vrednost"
|
msgstr "Vrednost"
|
||||||
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "View"
|
msgid "View"
|
||||||
msgstr "Pogled"
|
msgstr "Pogled"
|
||||||
@@ -2436,8 +2469,8 @@ msgid "View your 200 most recent alerts."
|
|||||||
msgstr "Oglejte si svojih 200 najnovejših opozoril."
|
msgstr "Oglejte si svojih 200 najnovejših opozoril."
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "Visible Fields"
|
#~ msgid "Visible Fields"
|
||||||
msgstr "Vidna polja"
|
#~ msgstr "Vidna polja"
|
||||||
|
|
||||||
#: src/components/routes/domain.tsx
|
#: src/components/routes/domain.tsx
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
@@ -2468,7 +2501,6 @@ msgstr "Pragovi za opozorila"
|
|||||||
msgid "Webhook / Push notifications"
|
msgid "Webhook / Push notifications"
|
||||||
msgstr "Webhook / potisna obvestila"
|
msgstr "Webhook / potisna obvestila"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Website & Service Monitoring"
|
msgid "Website & Service Monitoring"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|||||||
@@ -230,6 +230,15 @@ msgstr "Сви контејнери"
|
|||||||
msgid "All Systems"
|
msgid "All Systems"
|
||||||
msgstr "Сви системи"
|
msgstr "Сви системи"
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "All Tags"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "All Types"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "App update"
|
msgid "App update"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -465,8 +474,8 @@ msgstr ""
|
|||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "Check Now"
|
#~ msgid "Check Now"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/heartbeat.tsx
|
#: src/components/routes/settings/heartbeat.tsx
|
||||||
msgid "Check your monitoring service"
|
msgid "Check your monitoring service"
|
||||||
@@ -503,8 +512,8 @@ msgid "Close"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
#~ msgid "Columns"
|
msgid "Columns"
|
||||||
#~ msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
@@ -784,6 +793,14 @@ msgctxt "Layout display options"
|
|||||||
msgid "Display"
|
msgid "Display"
|
||||||
msgstr "Приказ"
|
msgstr "Приказ"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "Display"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
msgid "Display Columns"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/domain.tsx
|
#: src/components/routes/domain.tsx
|
||||||
#~ msgid "DNS Records"
|
#~ msgid "DNS Records"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
@@ -814,7 +831,6 @@ msgid "Domain and SSL expiry calendar"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/domains-table/domains-table.tsx
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Domain Monitoring"
|
msgid "Domain Monitoring"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1407,7 +1423,6 @@ msgid "Monitor updated successfully"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
msgid "Monitor websites, APIs, and services"
|
msgid "Monitor websites, APIs, and services"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -1448,6 +1463,10 @@ msgstr ""
|
|||||||
msgid "Net"
|
msgid "Net"
|
||||||
msgstr "Мрежа"
|
msgstr "Мрежа"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
#~ msgid "Network (Grouped)"
|
||||||
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system/charts/network-charts.tsx
|
#: src/components/routes/system/charts/network-charts.tsx
|
||||||
msgid "Network traffic of docker containers"
|
msgid "Network traffic of docker containers"
|
||||||
msgstr "Мрежни саобраћај docker контејнера"
|
msgstr "Мрежни саобраћај docker контејнера"
|
||||||
@@ -1471,8 +1490,8 @@ msgid "No"
|
|||||||
msgstr "Не"
|
msgstr "Не"
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "No data available for selected time range"
|
#~ msgid "No data available for selected time range"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/incidents.tsx
|
#: src/components/routes/incidents.tsx
|
||||||
msgid "No incidents found."
|
msgid "No incidents found."
|
||||||
@@ -1553,6 +1572,11 @@ msgstr "Отвори мени"
|
|||||||
msgid "Optional description for this monitor"
|
msgid "Optional description for this monitor"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "Options"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/login/auth-form.tsx
|
#: src/components/login/auth-form.tsx
|
||||||
msgid "Or continue with"
|
msgid "Or continue with"
|
||||||
msgstr "Или наставите са"
|
msgstr "Или наставите са"
|
||||||
@@ -1626,6 +1650,8 @@ msgid "Paused"
|
|||||||
msgstr "Паузирано"
|
msgstr "Паузирано"
|
||||||
|
|
||||||
#. placeholder {0}: stats.paused
|
#. placeholder {0}: stats.paused
|
||||||
|
#. placeholder {0}: statusCounts.paused
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
msgid "Paused ({0})"
|
msgid "Paused ({0})"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1804,6 +1830,10 @@ msgstr "Решено"
|
|||||||
msgid "Response"
|
msgid "Response"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/routes/monitor.tsx
|
||||||
|
msgid "Response time over the selected period"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
#~ msgid "Response Times"
|
#~ msgid "Response Times"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
@@ -2004,7 +2034,6 @@ msgstr "Стање"
|
|||||||
|
|
||||||
#: src/components/containers-table/containers-table-columns.tsx
|
#: src/components/containers-table/containers-table-columns.tsx
|
||||||
#: src/components/domains-table/domains-table.tsx
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/domains-table/domains-table.tsx
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
@@ -2016,12 +2045,15 @@ msgid "Status"
|
|||||||
msgstr "Статус"
|
msgstr "Статус"
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "Status and response time over the selected period"
|
#~ msgid "Status and response time over the selected period"
|
||||||
|
#~ msgstr ""
|
||||||
|
|
||||||
|
#: src/components/routes/status-pages.tsx
|
||||||
|
msgid "Status Page Manager"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
#: src/components/routes/status-pages.tsx
|
|
||||||
msgid "Status Pages"
|
msgid "Status Pages"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -2065,8 +2097,8 @@ msgid "System load averages over time"
|
|||||||
msgstr "Просечна оптерећења система током времена"
|
msgstr "Просечна оптерећења система током времена"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
msgid "System Monitoring"
|
#~ msgid "System Monitoring"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/systemd-table/systemd-table.tsx
|
#: src/components/systemd-table/systemd-table.tsx
|
||||||
msgid "Systemd Services"
|
msgid "Systemd Services"
|
||||||
@@ -2091,6 +2123,8 @@ msgctxt "Tabs system layout option"
|
|||||||
msgid "Tabs"
|
msgid "Tabs"
|
||||||
msgstr "Картице"
|
msgstr "Картице"
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2213,7 +2247,6 @@ msgstr ""
|
|||||||
msgid "Total: {0}"
|
msgid "Total: {0}"
|
||||||
msgstr "Укупно: {0}"
|
msgstr "Укупно: {0}"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Track domain expiry dates and DNS status"
|
msgid "Track domain expiry dates and DNS status"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2223,8 +2256,8 @@ msgid "Track domain expiry dates and watch domains for purchase"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
msgid "Track system resources, containers, and health"
|
#~ msgid "Track system resources, containers, and health"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Track uptime, response times, and service health"
|
msgid "Track uptime, response times, and service health"
|
||||||
@@ -2282,6 +2315,7 @@ msgstr "Окида се када статус прелази између укљ
|
|||||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||||
msgstr "Окида се када употреба било ког диска премаши праг"
|
msgstr "Окида се када употреба било ког диска премаши праг"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/routes/settings/quiet-hours.tsx
|
#: src/components/routes/settings/quiet-hours.tsx
|
||||||
#: src/components/routes/settings/quiet-hours.tsx
|
#: src/components/routes/settings/quiet-hours.tsx
|
||||||
@@ -2416,7 +2450,6 @@ msgstr "Искоришћеност"
|
|||||||
msgid "Value"
|
msgid "Value"
|
||||||
msgstr "Вредност"
|
msgstr "Вредност"
|
||||||
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "View"
|
msgid "View"
|
||||||
msgstr "Погледај"
|
msgstr "Погледај"
|
||||||
@@ -2436,8 +2469,8 @@ msgid "View your 200 most recent alerts."
|
|||||||
msgstr "Погледајте ваших 200 најновијих упозорења."
|
msgstr "Погледајте ваших 200 најновијих упозорења."
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "Visible Fields"
|
#~ msgid "Visible Fields"
|
||||||
msgstr "Видљива поља"
|
#~ msgstr "Видљива поља"
|
||||||
|
|
||||||
#: src/components/routes/domain.tsx
|
#: src/components/routes/domain.tsx
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
@@ -2468,7 +2501,6 @@ msgstr "Прагове упозорења"
|
|||||||
msgid "Webhook / Push notifications"
|
msgid "Webhook / Push notifications"
|
||||||
msgstr "Webhook / Push обавештења"
|
msgstr "Webhook / Push обавештења"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Website & Service Monitoring"
|
msgid "Website & Service Monitoring"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|||||||
@@ -230,6 +230,15 @@ msgstr "Alla behållare"
|
|||||||
msgid "All Systems"
|
msgid "All Systems"
|
||||||
msgstr "Alla system"
|
msgstr "Alla system"
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "All Tags"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "All Types"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "App update"
|
msgid "App update"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -465,8 +474,8 @@ msgstr ""
|
|||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "Check Now"
|
#~ msgid "Check Now"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/heartbeat.tsx
|
#: src/components/routes/settings/heartbeat.tsx
|
||||||
msgid "Check your monitoring service"
|
msgid "Check your monitoring service"
|
||||||
@@ -503,8 +512,8 @@ msgid "Close"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
#~ msgid "Columns"
|
msgid "Columns"
|
||||||
#~ msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
@@ -784,6 +793,14 @@ msgctxt "Layout display options"
|
|||||||
msgid "Display"
|
msgid "Display"
|
||||||
msgstr "Visa"
|
msgstr "Visa"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "Display"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
msgid "Display Columns"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/domain.tsx
|
#: src/components/routes/domain.tsx
|
||||||
#~ msgid "DNS Records"
|
#~ msgid "DNS Records"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
@@ -814,7 +831,6 @@ msgid "Domain and SSL expiry calendar"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/domains-table/domains-table.tsx
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Domain Monitoring"
|
msgid "Domain Monitoring"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1407,7 +1423,6 @@ msgid "Monitor updated successfully"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
msgid "Monitor websites, APIs, and services"
|
msgid "Monitor websites, APIs, and services"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -1448,6 +1463,10 @@ msgstr ""
|
|||||||
msgid "Net"
|
msgid "Net"
|
||||||
msgstr "Nät"
|
msgstr "Nät"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
#~ msgid "Network (Grouped)"
|
||||||
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system/charts/network-charts.tsx
|
#: src/components/routes/system/charts/network-charts.tsx
|
||||||
msgid "Network traffic of docker containers"
|
msgid "Network traffic of docker containers"
|
||||||
msgstr "Nätverkstrafik för dockercontainrar"
|
msgstr "Nätverkstrafik för dockercontainrar"
|
||||||
@@ -1471,8 +1490,8 @@ msgid "No"
|
|||||||
msgstr "Nej"
|
msgstr "Nej"
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "No data available for selected time range"
|
#~ msgid "No data available for selected time range"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/incidents.tsx
|
#: src/components/routes/incidents.tsx
|
||||||
msgid "No incidents found."
|
msgid "No incidents found."
|
||||||
@@ -1553,6 +1572,11 @@ msgstr "Öppna menyn"
|
|||||||
msgid "Optional description for this monitor"
|
msgid "Optional description for this monitor"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "Options"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/login/auth-form.tsx
|
#: src/components/login/auth-form.tsx
|
||||||
msgid "Or continue with"
|
msgid "Or continue with"
|
||||||
msgstr "Eller fortsätt med"
|
msgstr "Eller fortsätt med"
|
||||||
@@ -1626,6 +1650,8 @@ msgid "Paused"
|
|||||||
msgstr "Pausad"
|
msgstr "Pausad"
|
||||||
|
|
||||||
#. placeholder {0}: stats.paused
|
#. placeholder {0}: stats.paused
|
||||||
|
#. placeholder {0}: statusCounts.paused
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
msgid "Paused ({0})"
|
msgid "Paused ({0})"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1804,6 +1830,10 @@ msgstr "Löst"
|
|||||||
msgid "Response"
|
msgid "Response"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/routes/monitor.tsx
|
||||||
|
msgid "Response time over the selected period"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
#~ msgid "Response Times"
|
#~ msgid "Response Times"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
@@ -2004,7 +2034,6 @@ msgstr "Tillstånd"
|
|||||||
|
|
||||||
#: src/components/containers-table/containers-table-columns.tsx
|
#: src/components/containers-table/containers-table-columns.tsx
|
||||||
#: src/components/domains-table/domains-table.tsx
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/domains-table/domains-table.tsx
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
@@ -2016,12 +2045,15 @@ msgid "Status"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "Status and response time over the selected period"
|
#~ msgid "Status and response time over the selected period"
|
||||||
|
#~ msgstr ""
|
||||||
|
|
||||||
|
#: src/components/routes/status-pages.tsx
|
||||||
|
msgid "Status Page Manager"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
#: src/components/routes/status-pages.tsx
|
|
||||||
msgid "Status Pages"
|
msgid "Status Pages"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -2065,8 +2097,8 @@ msgid "System load averages over time"
|
|||||||
msgstr "Systembelastning över tid"
|
msgstr "Systembelastning över tid"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
msgid "System Monitoring"
|
#~ msgid "System Monitoring"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/systemd-table/systemd-table.tsx
|
#: src/components/systemd-table/systemd-table.tsx
|
||||||
msgid "Systemd Services"
|
msgid "Systemd Services"
|
||||||
@@ -2091,6 +2123,8 @@ msgctxt "Tabs system layout option"
|
|||||||
msgid "Tabs"
|
msgid "Tabs"
|
||||||
msgstr "Flikar"
|
msgstr "Flikar"
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2213,7 +2247,6 @@ msgstr ""
|
|||||||
msgid "Total: {0}"
|
msgid "Total: {0}"
|
||||||
msgstr "Totalt: {0}"
|
msgstr "Totalt: {0}"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Track domain expiry dates and DNS status"
|
msgid "Track domain expiry dates and DNS status"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2223,8 +2256,8 @@ msgid "Track domain expiry dates and watch domains for purchase"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
msgid "Track system resources, containers, and health"
|
#~ msgid "Track system resources, containers, and health"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Track uptime, response times, and service health"
|
msgid "Track uptime, response times, and service health"
|
||||||
@@ -2282,6 +2315,7 @@ msgstr "Utlöses när status växlar mellan upp och ner"
|
|||||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||||
msgstr "Utlöses när användningen av någon disk överskrider ett tröskelvärde"
|
msgstr "Utlöses när användningen av någon disk överskrider ett tröskelvärde"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/routes/settings/quiet-hours.tsx
|
#: src/components/routes/settings/quiet-hours.tsx
|
||||||
#: src/components/routes/settings/quiet-hours.tsx
|
#: src/components/routes/settings/quiet-hours.tsx
|
||||||
@@ -2416,7 +2450,6 @@ msgstr "Användning"
|
|||||||
msgid "Value"
|
msgid "Value"
|
||||||
msgstr "Värde"
|
msgstr "Värde"
|
||||||
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "View"
|
msgid "View"
|
||||||
msgstr "Visa"
|
msgstr "Visa"
|
||||||
@@ -2436,8 +2469,8 @@ msgid "View your 200 most recent alerts."
|
|||||||
msgstr "Visa dina 200 senaste larm."
|
msgstr "Visa dina 200 senaste larm."
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "Visible Fields"
|
#~ msgid "Visible Fields"
|
||||||
msgstr "Synliga fält"
|
#~ msgstr "Synliga fält"
|
||||||
|
|
||||||
#: src/components/routes/domain.tsx
|
#: src/components/routes/domain.tsx
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
@@ -2468,7 +2501,6 @@ msgstr "Varningströsklar"
|
|||||||
msgid "Webhook / Push notifications"
|
msgid "Webhook / Push notifications"
|
||||||
msgstr "Webhook / Push-aviseringar"
|
msgstr "Webhook / Push-aviseringar"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Website & Service Monitoring"
|
msgid "Website & Service Monitoring"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|||||||
@@ -230,6 +230,15 @@ msgstr "Tüm Konteynerler"
|
|||||||
msgid "All Systems"
|
msgid "All Systems"
|
||||||
msgstr "Tüm Sistemler"
|
msgstr "Tüm Sistemler"
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "All Tags"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "All Types"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "App update"
|
msgid "App update"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -465,8 +474,8 @@ msgstr ""
|
|||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "Check Now"
|
#~ msgid "Check Now"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/heartbeat.tsx
|
#: src/components/routes/settings/heartbeat.tsx
|
||||||
msgid "Check your monitoring service"
|
msgid "Check your monitoring service"
|
||||||
@@ -503,8 +512,8 @@ msgid "Close"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
#~ msgid "Columns"
|
msgid "Columns"
|
||||||
#~ msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
@@ -784,6 +793,14 @@ msgctxt "Layout display options"
|
|||||||
msgid "Display"
|
msgid "Display"
|
||||||
msgstr "Görünüm"
|
msgstr "Görünüm"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "Display"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
msgid "Display Columns"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/domain.tsx
|
#: src/components/routes/domain.tsx
|
||||||
#~ msgid "DNS Records"
|
#~ msgid "DNS Records"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
@@ -814,7 +831,6 @@ msgid "Domain and SSL expiry calendar"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/domains-table/domains-table.tsx
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Domain Monitoring"
|
msgid "Domain Monitoring"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1407,7 +1423,6 @@ msgid "Monitor updated successfully"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
msgid "Monitor websites, APIs, and services"
|
msgid "Monitor websites, APIs, and services"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -1448,6 +1463,10 @@ msgstr ""
|
|||||||
msgid "Net"
|
msgid "Net"
|
||||||
msgstr "Ağ"
|
msgstr "Ağ"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
#~ msgid "Network (Grouped)"
|
||||||
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system/charts/network-charts.tsx
|
#: src/components/routes/system/charts/network-charts.tsx
|
||||||
msgid "Network traffic of docker containers"
|
msgid "Network traffic of docker containers"
|
||||||
msgstr "Docker konteynerlerinin ağ trafiği"
|
msgstr "Docker konteynerlerinin ağ trafiği"
|
||||||
@@ -1471,8 +1490,8 @@ msgid "No"
|
|||||||
msgstr "Hayır"
|
msgstr "Hayır"
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "No data available for selected time range"
|
#~ msgid "No data available for selected time range"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/incidents.tsx
|
#: src/components/routes/incidents.tsx
|
||||||
msgid "No incidents found."
|
msgid "No incidents found."
|
||||||
@@ -1553,6 +1572,11 @@ msgstr "Menüyü aç"
|
|||||||
msgid "Optional description for this monitor"
|
msgid "Optional description for this monitor"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "Options"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/login/auth-form.tsx
|
#: src/components/login/auth-form.tsx
|
||||||
msgid "Or continue with"
|
msgid "Or continue with"
|
||||||
msgstr "Veya devam et"
|
msgstr "Veya devam et"
|
||||||
@@ -1626,6 +1650,8 @@ msgid "Paused"
|
|||||||
msgstr "Duraklatıldı"
|
msgstr "Duraklatıldı"
|
||||||
|
|
||||||
#. placeholder {0}: stats.paused
|
#. placeholder {0}: stats.paused
|
||||||
|
#. placeholder {0}: statusCounts.paused
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
msgid "Paused ({0})"
|
msgid "Paused ({0})"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1804,6 +1830,10 @@ msgstr "Çözüldü"
|
|||||||
msgid "Response"
|
msgid "Response"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/routes/monitor.tsx
|
||||||
|
msgid "Response time over the selected period"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
#~ msgid "Response Times"
|
#~ msgid "Response Times"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
@@ -2004,7 +2034,6 @@ msgstr "Durum"
|
|||||||
|
|
||||||
#: src/components/containers-table/containers-table-columns.tsx
|
#: src/components/containers-table/containers-table-columns.tsx
|
||||||
#: src/components/domains-table/domains-table.tsx
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/domains-table/domains-table.tsx
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
@@ -2016,12 +2045,15 @@ msgid "Status"
|
|||||||
msgstr "Durum"
|
msgstr "Durum"
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "Status and response time over the selected period"
|
#~ msgid "Status and response time over the selected period"
|
||||||
|
#~ msgstr ""
|
||||||
|
|
||||||
|
#: src/components/routes/status-pages.tsx
|
||||||
|
msgid "Status Page Manager"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
#: src/components/routes/status-pages.tsx
|
|
||||||
msgid "Status Pages"
|
msgid "Status Pages"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -2065,8 +2097,8 @@ msgid "System load averages over time"
|
|||||||
msgstr "Zaman içindeki sistem yükü ortalamaları"
|
msgstr "Zaman içindeki sistem yükü ortalamaları"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
msgid "System Monitoring"
|
#~ msgid "System Monitoring"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/systemd-table/systemd-table.tsx
|
#: src/components/systemd-table/systemd-table.tsx
|
||||||
msgid "Systemd Services"
|
msgid "Systemd Services"
|
||||||
@@ -2091,6 +2123,8 @@ msgctxt "Tabs system layout option"
|
|||||||
msgid "Tabs"
|
msgid "Tabs"
|
||||||
msgstr "Sekmeler"
|
msgstr "Sekmeler"
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2213,7 +2247,6 @@ msgstr ""
|
|||||||
msgid "Total: {0}"
|
msgid "Total: {0}"
|
||||||
msgstr "Toplam: {0}"
|
msgstr "Toplam: {0}"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Track domain expiry dates and DNS status"
|
msgid "Track domain expiry dates and DNS status"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2223,8 +2256,8 @@ msgid "Track domain expiry dates and watch domains for purchase"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
msgid "Track system resources, containers, and health"
|
#~ msgid "Track system resources, containers, and health"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Track uptime, response times, and service health"
|
msgid "Track uptime, response times, and service health"
|
||||||
@@ -2282,6 +2315,7 @@ msgstr "Durum yukarı ve aşağı arasında değiştiğinde tetiklenir"
|
|||||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||||
msgstr "Herhangi bir diskin kullanımı bir eşiği aştığında tetiklenir"
|
msgstr "Herhangi bir diskin kullanımı bir eşiği aştığında tetiklenir"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/routes/settings/quiet-hours.tsx
|
#: src/components/routes/settings/quiet-hours.tsx
|
||||||
#: src/components/routes/settings/quiet-hours.tsx
|
#: src/components/routes/settings/quiet-hours.tsx
|
||||||
@@ -2416,7 +2450,6 @@ msgstr "Kullanım"
|
|||||||
msgid "Value"
|
msgid "Value"
|
||||||
msgstr "Değer"
|
msgstr "Değer"
|
||||||
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "View"
|
msgid "View"
|
||||||
msgstr "Görüntüle"
|
msgstr "Görüntüle"
|
||||||
@@ -2436,8 +2469,8 @@ msgid "View your 200 most recent alerts."
|
|||||||
msgstr "En son 200 uyarınızı görüntüleyin."
|
msgstr "En son 200 uyarınızı görüntüleyin."
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "Visible Fields"
|
#~ msgid "Visible Fields"
|
||||||
msgstr "Görünür Alanlar"
|
#~ msgstr "Görünür Alanlar"
|
||||||
|
|
||||||
#: src/components/routes/domain.tsx
|
#: src/components/routes/domain.tsx
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
@@ -2468,7 +2501,6 @@ msgstr "Uyarı eşikleri"
|
|||||||
msgid "Webhook / Push notifications"
|
msgid "Webhook / Push notifications"
|
||||||
msgstr "Webhook / Anlık bildirimler"
|
msgstr "Webhook / Anlık bildirimler"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Website & Service Monitoring"
|
msgid "Website & Service Monitoring"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|||||||
@@ -230,6 +230,15 @@ msgstr "Всі контейнери"
|
|||||||
msgid "All Systems"
|
msgid "All Systems"
|
||||||
msgstr "Всі системи"
|
msgstr "Всі системи"
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "All Tags"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "All Types"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "App update"
|
msgid "App update"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -465,8 +474,8 @@ msgstr ""
|
|||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "Check Now"
|
#~ msgid "Check Now"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/heartbeat.tsx
|
#: src/components/routes/settings/heartbeat.tsx
|
||||||
msgid "Check your monitoring service"
|
msgid "Check your monitoring service"
|
||||||
@@ -503,8 +512,8 @@ msgid "Close"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
#~ msgid "Columns"
|
msgid "Columns"
|
||||||
#~ msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
@@ -784,6 +793,14 @@ msgctxt "Layout display options"
|
|||||||
msgid "Display"
|
msgid "Display"
|
||||||
msgstr "Відображення"
|
msgstr "Відображення"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "Display"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
msgid "Display Columns"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/domain.tsx
|
#: src/components/routes/domain.tsx
|
||||||
#~ msgid "DNS Records"
|
#~ msgid "DNS Records"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
@@ -814,7 +831,6 @@ msgid "Domain and SSL expiry calendar"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/domains-table/domains-table.tsx
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Domain Monitoring"
|
msgid "Domain Monitoring"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1407,7 +1423,6 @@ msgid "Monitor updated successfully"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
msgid "Monitor websites, APIs, and services"
|
msgid "Monitor websites, APIs, and services"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -1448,6 +1463,10 @@ msgstr ""
|
|||||||
msgid "Net"
|
msgid "Net"
|
||||||
msgstr "Мережа"
|
msgstr "Мережа"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
#~ msgid "Network (Grouped)"
|
||||||
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system/charts/network-charts.tsx
|
#: src/components/routes/system/charts/network-charts.tsx
|
||||||
msgid "Network traffic of docker containers"
|
msgid "Network traffic of docker containers"
|
||||||
msgstr "Мережевий трафік контейнерів Docker"
|
msgstr "Мережевий трафік контейнерів Docker"
|
||||||
@@ -1471,8 +1490,8 @@ msgid "No"
|
|||||||
msgstr "Ні"
|
msgstr "Ні"
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "No data available for selected time range"
|
#~ msgid "No data available for selected time range"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/incidents.tsx
|
#: src/components/routes/incidents.tsx
|
||||||
msgid "No incidents found."
|
msgid "No incidents found."
|
||||||
@@ -1553,6 +1572,11 @@ msgstr "Відкрити меню"
|
|||||||
msgid "Optional description for this monitor"
|
msgid "Optional description for this monitor"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "Options"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/login/auth-form.tsx
|
#: src/components/login/auth-form.tsx
|
||||||
msgid "Or continue with"
|
msgid "Or continue with"
|
||||||
msgstr "Або продовжити з"
|
msgstr "Або продовжити з"
|
||||||
@@ -1626,6 +1650,8 @@ msgid "Paused"
|
|||||||
msgstr "Призупинено"
|
msgstr "Призупинено"
|
||||||
|
|
||||||
#. placeholder {0}: stats.paused
|
#. placeholder {0}: stats.paused
|
||||||
|
#. placeholder {0}: statusCounts.paused
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
msgid "Paused ({0})"
|
msgid "Paused ({0})"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1804,6 +1830,10 @@ msgstr "Вирішено"
|
|||||||
msgid "Response"
|
msgid "Response"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/routes/monitor.tsx
|
||||||
|
msgid "Response time over the selected period"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
#~ msgid "Response Times"
|
#~ msgid "Response Times"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
@@ -2004,7 +2034,6 @@ msgstr "Стан"
|
|||||||
|
|
||||||
#: src/components/containers-table/containers-table-columns.tsx
|
#: src/components/containers-table/containers-table-columns.tsx
|
||||||
#: src/components/domains-table/domains-table.tsx
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/domains-table/domains-table.tsx
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
@@ -2016,12 +2045,15 @@ msgid "Status"
|
|||||||
msgstr "Статус"
|
msgstr "Статус"
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "Status and response time over the selected period"
|
#~ msgid "Status and response time over the selected period"
|
||||||
|
#~ msgstr ""
|
||||||
|
|
||||||
|
#: src/components/routes/status-pages.tsx
|
||||||
|
msgid "Status Page Manager"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
#: src/components/routes/status-pages.tsx
|
|
||||||
msgid "Status Pages"
|
msgid "Status Pages"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -2065,8 +2097,8 @@ msgid "System load averages over time"
|
|||||||
msgstr "Середнє навантаження системи з часом"
|
msgstr "Середнє навантаження системи з часом"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
msgid "System Monitoring"
|
#~ msgid "System Monitoring"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/systemd-table/systemd-table.tsx
|
#: src/components/systemd-table/systemd-table.tsx
|
||||||
msgid "Systemd Services"
|
msgid "Systemd Services"
|
||||||
@@ -2091,6 +2123,8 @@ msgctxt "Tabs system layout option"
|
|||||||
msgid "Tabs"
|
msgid "Tabs"
|
||||||
msgstr "Вкладки"
|
msgstr "Вкладки"
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2213,7 +2247,6 @@ msgstr ""
|
|||||||
msgid "Total: {0}"
|
msgid "Total: {0}"
|
||||||
msgstr "Всього: {0}"
|
msgstr "Всього: {0}"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Track domain expiry dates and DNS status"
|
msgid "Track domain expiry dates and DNS status"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2223,8 +2256,8 @@ msgid "Track domain expiry dates and watch domains for purchase"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
msgid "Track system resources, containers, and health"
|
#~ msgid "Track system resources, containers, and health"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Track uptime, response times, and service health"
|
msgid "Track uptime, response times, and service health"
|
||||||
@@ -2282,6 +2315,7 @@ msgstr "Спрацьовує, коли статус перемикається
|
|||||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||||
msgstr "Спрацьовує, коли використання будь-якого диска перевищує поріг"
|
msgstr "Спрацьовує, коли використання будь-якого диска перевищує поріг"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/routes/settings/quiet-hours.tsx
|
#: src/components/routes/settings/quiet-hours.tsx
|
||||||
#: src/components/routes/settings/quiet-hours.tsx
|
#: src/components/routes/settings/quiet-hours.tsx
|
||||||
@@ -2416,7 +2450,6 @@ msgstr "Використання"
|
|||||||
msgid "Value"
|
msgid "Value"
|
||||||
msgstr "Значення"
|
msgstr "Значення"
|
||||||
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "View"
|
msgid "View"
|
||||||
msgstr "Вигляд"
|
msgstr "Вигляд"
|
||||||
@@ -2436,8 +2469,8 @@ msgid "View your 200 most recent alerts."
|
|||||||
msgstr "Переглянути 200 останніх сповіщень."
|
msgstr "Переглянути 200 останніх сповіщень."
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "Visible Fields"
|
#~ msgid "Visible Fields"
|
||||||
msgstr "Видимі стовпці"
|
#~ msgstr "Видимі стовпці"
|
||||||
|
|
||||||
#: src/components/routes/domain.tsx
|
#: src/components/routes/domain.tsx
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
@@ -2468,7 +2501,6 @@ msgstr "Пороги попередження"
|
|||||||
msgid "Webhook / Push notifications"
|
msgid "Webhook / Push notifications"
|
||||||
msgstr "Webhook / Push сповіщення"
|
msgstr "Webhook / Push сповіщення"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Website & Service Monitoring"
|
msgid "Website & Service Monitoring"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|||||||
@@ -230,6 +230,15 @@ msgstr "Tất cả container"
|
|||||||
msgid "All Systems"
|
msgid "All Systems"
|
||||||
msgstr "Tất cả Hệ thống"
|
msgstr "Tất cả Hệ thống"
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "All Tags"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "All Types"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "App update"
|
msgid "App update"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -465,8 +474,8 @@ msgstr ""
|
|||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "Check Now"
|
#~ msgid "Check Now"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/heartbeat.tsx
|
#: src/components/routes/settings/heartbeat.tsx
|
||||||
msgid "Check your monitoring service"
|
msgid "Check your monitoring service"
|
||||||
@@ -503,8 +512,8 @@ msgid "Close"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
#~ msgid "Columns"
|
msgid "Columns"
|
||||||
#~ msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
@@ -784,6 +793,14 @@ msgctxt "Layout display options"
|
|||||||
msgid "Display"
|
msgid "Display"
|
||||||
msgstr "Hiển thị"
|
msgstr "Hiển thị"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "Display"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
msgid "Display Columns"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/domain.tsx
|
#: src/components/routes/domain.tsx
|
||||||
#~ msgid "DNS Records"
|
#~ msgid "DNS Records"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
@@ -814,7 +831,6 @@ msgid "Domain and SSL expiry calendar"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/domains-table/domains-table.tsx
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Domain Monitoring"
|
msgid "Domain Monitoring"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1407,7 +1423,6 @@ msgid "Monitor updated successfully"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
msgid "Monitor websites, APIs, and services"
|
msgid "Monitor websites, APIs, and services"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -1448,6 +1463,10 @@ msgstr ""
|
|||||||
msgid "Net"
|
msgid "Net"
|
||||||
msgstr "Mạng"
|
msgstr "Mạng"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
#~ msgid "Network (Grouped)"
|
||||||
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system/charts/network-charts.tsx
|
#: src/components/routes/system/charts/network-charts.tsx
|
||||||
msgid "Network traffic of docker containers"
|
msgid "Network traffic of docker containers"
|
||||||
msgstr "Lưu lượng mạng của các container Docker"
|
msgstr "Lưu lượng mạng của các container Docker"
|
||||||
@@ -1471,8 +1490,8 @@ msgid "No"
|
|||||||
msgstr "Không"
|
msgstr "Không"
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "No data available for selected time range"
|
#~ msgid "No data available for selected time range"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/incidents.tsx
|
#: src/components/routes/incidents.tsx
|
||||||
msgid "No incidents found."
|
msgid "No incidents found."
|
||||||
@@ -1553,6 +1572,11 @@ msgstr "Mở menu"
|
|||||||
msgid "Optional description for this monitor"
|
msgid "Optional description for this monitor"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "Options"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/login/auth-form.tsx
|
#: src/components/login/auth-form.tsx
|
||||||
msgid "Or continue with"
|
msgid "Or continue with"
|
||||||
msgstr "Hoặc tiếp tục với"
|
msgstr "Hoặc tiếp tục với"
|
||||||
@@ -1626,6 +1650,8 @@ msgid "Paused"
|
|||||||
msgstr "Đã tạm dừng"
|
msgstr "Đã tạm dừng"
|
||||||
|
|
||||||
#. placeholder {0}: stats.paused
|
#. placeholder {0}: stats.paused
|
||||||
|
#. placeholder {0}: statusCounts.paused
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
msgid "Paused ({0})"
|
msgid "Paused ({0})"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1804,6 +1830,10 @@ msgstr "Đã giải quyết"
|
|||||||
msgid "Response"
|
msgid "Response"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/routes/monitor.tsx
|
||||||
|
msgid "Response time over the selected period"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
#~ msgid "Response Times"
|
#~ msgid "Response Times"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
@@ -2004,7 +2034,6 @@ msgstr "Trạng thái"
|
|||||||
|
|
||||||
#: src/components/containers-table/containers-table-columns.tsx
|
#: src/components/containers-table/containers-table-columns.tsx
|
||||||
#: src/components/domains-table/domains-table.tsx
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/domains-table/domains-table.tsx
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
@@ -2016,12 +2045,15 @@ msgid "Status"
|
|||||||
msgstr "Trạng thái"
|
msgstr "Trạng thái"
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "Status and response time over the selected period"
|
#~ msgid "Status and response time over the selected period"
|
||||||
|
#~ msgstr ""
|
||||||
|
|
||||||
|
#: src/components/routes/status-pages.tsx
|
||||||
|
msgid "Status Page Manager"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
#: src/components/routes/status-pages.tsx
|
|
||||||
msgid "Status Pages"
|
msgid "Status Pages"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -2065,8 +2097,8 @@ msgid "System load averages over time"
|
|||||||
msgstr "Tải trung bình của hệ thống theo thời gian"
|
msgstr "Tải trung bình của hệ thống theo thời gian"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
msgid "System Monitoring"
|
#~ msgid "System Monitoring"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/systemd-table/systemd-table.tsx
|
#: src/components/systemd-table/systemd-table.tsx
|
||||||
msgid "Systemd Services"
|
msgid "Systemd Services"
|
||||||
@@ -2091,6 +2123,8 @@ msgctxt "Tabs system layout option"
|
|||||||
msgid "Tabs"
|
msgid "Tabs"
|
||||||
msgstr "Tab"
|
msgstr "Tab"
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2213,7 +2247,6 @@ msgstr ""
|
|||||||
msgid "Total: {0}"
|
msgid "Total: {0}"
|
||||||
msgstr "Tổng: {0}"
|
msgstr "Tổng: {0}"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Track domain expiry dates and DNS status"
|
msgid "Track domain expiry dates and DNS status"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2223,8 +2256,8 @@ msgid "Track domain expiry dates and watch domains for purchase"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
msgid "Track system resources, containers, and health"
|
#~ msgid "Track system resources, containers, and health"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Track uptime, response times, and service health"
|
msgid "Track uptime, response times, and service health"
|
||||||
@@ -2282,6 +2315,7 @@ msgstr "Kích hoạt khi trạng thái chuyển đổi giữa lên và xuống"
|
|||||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||||
msgstr "Kích hoạt khi sử dụng bất kỳ đĩa nào vượt quá ngưỡng"
|
msgstr "Kích hoạt khi sử dụng bất kỳ đĩa nào vượt quá ngưỡng"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/routes/settings/quiet-hours.tsx
|
#: src/components/routes/settings/quiet-hours.tsx
|
||||||
#: src/components/routes/settings/quiet-hours.tsx
|
#: src/components/routes/settings/quiet-hours.tsx
|
||||||
@@ -2416,7 +2450,6 @@ msgstr "Sử dụng"
|
|||||||
msgid "Value"
|
msgid "Value"
|
||||||
msgstr "Giá trị"
|
msgstr "Giá trị"
|
||||||
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "View"
|
msgid "View"
|
||||||
msgstr "Xem"
|
msgstr "Xem"
|
||||||
@@ -2436,8 +2469,8 @@ msgid "View your 200 most recent alerts."
|
|||||||
msgstr "Xem 200 cảnh báo gần đây nhất của bạn."
|
msgstr "Xem 200 cảnh báo gần đây nhất của bạn."
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "Visible Fields"
|
#~ msgid "Visible Fields"
|
||||||
msgstr "Các cột hiển thị"
|
#~ msgstr "Các cột hiển thị"
|
||||||
|
|
||||||
#: src/components/routes/domain.tsx
|
#: src/components/routes/domain.tsx
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
@@ -2468,7 +2501,6 @@ msgstr "Ngưỡng cảnh báo"
|
|||||||
msgid "Webhook / Push notifications"
|
msgid "Webhook / Push notifications"
|
||||||
msgstr "Thông báo Webhook / Push"
|
msgstr "Thông báo Webhook / Push"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Website & Service Monitoring"
|
msgid "Website & Service Monitoring"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|||||||
@@ -230,6 +230,15 @@ msgstr "所有容器"
|
|||||||
msgid "All Systems"
|
msgid "All Systems"
|
||||||
msgstr "所有客户端"
|
msgstr "所有客户端"
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "All Tags"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "All Types"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "App update"
|
msgid "App update"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -465,8 +474,8 @@ msgstr ""
|
|||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "Check Now"
|
#~ msgid "Check Now"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/heartbeat.tsx
|
#: src/components/routes/settings/heartbeat.tsx
|
||||||
msgid "Check your monitoring service"
|
msgid "Check your monitoring service"
|
||||||
@@ -503,8 +512,8 @@ msgid "Close"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
#~ msgid "Columns"
|
msgid "Columns"
|
||||||
#~ msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
@@ -784,6 +793,14 @@ msgctxt "Layout display options"
|
|||||||
msgid "Display"
|
msgid "Display"
|
||||||
msgstr "显示"
|
msgstr "显示"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "Display"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
msgid "Display Columns"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/domain.tsx
|
#: src/components/routes/domain.tsx
|
||||||
#~ msgid "DNS Records"
|
#~ msgid "DNS Records"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
@@ -814,7 +831,6 @@ msgid "Domain and SSL expiry calendar"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/domains-table/domains-table.tsx
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Domain Monitoring"
|
msgid "Domain Monitoring"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1407,7 +1423,6 @@ msgid "Monitor updated successfully"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
msgid "Monitor websites, APIs, and services"
|
msgid "Monitor websites, APIs, and services"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -1448,6 +1463,10 @@ msgstr ""
|
|||||||
msgid "Net"
|
msgid "Net"
|
||||||
msgstr "网络"
|
msgstr "网络"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
#~ msgid "Network (Grouped)"
|
||||||
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system/charts/network-charts.tsx
|
#: src/components/routes/system/charts/network-charts.tsx
|
||||||
msgid "Network traffic of docker containers"
|
msgid "Network traffic of docker containers"
|
||||||
msgstr "Docker 容器的网络流量"
|
msgstr "Docker 容器的网络流量"
|
||||||
@@ -1471,8 +1490,8 @@ msgid "No"
|
|||||||
msgstr "否"
|
msgstr "否"
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "No data available for selected time range"
|
#~ msgid "No data available for selected time range"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/incidents.tsx
|
#: src/components/routes/incidents.tsx
|
||||||
msgid "No incidents found."
|
msgid "No incidents found."
|
||||||
@@ -1553,6 +1572,11 @@ msgstr "打开菜单"
|
|||||||
msgid "Optional description for this monitor"
|
msgid "Optional description for this monitor"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "Options"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/login/auth-form.tsx
|
#: src/components/login/auth-form.tsx
|
||||||
msgid "Or continue with"
|
msgid "Or continue with"
|
||||||
msgstr "或使用以下方式登录"
|
msgstr "或使用以下方式登录"
|
||||||
@@ -1626,6 +1650,8 @@ msgid "Paused"
|
|||||||
msgstr "已暂停"
|
msgstr "已暂停"
|
||||||
|
|
||||||
#. placeholder {0}: stats.paused
|
#. placeholder {0}: stats.paused
|
||||||
|
#. placeholder {0}: statusCounts.paused
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
msgid "Paused ({0})"
|
msgid "Paused ({0})"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1804,6 +1830,10 @@ msgstr "已解决"
|
|||||||
msgid "Response"
|
msgid "Response"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/routes/monitor.tsx
|
||||||
|
msgid "Response time over the selected period"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
#~ msgid "Response Times"
|
#~ msgid "Response Times"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
@@ -2004,7 +2034,6 @@ msgstr "状态"
|
|||||||
|
|
||||||
#: src/components/containers-table/containers-table-columns.tsx
|
#: src/components/containers-table/containers-table-columns.tsx
|
||||||
#: src/components/domains-table/domains-table.tsx
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/domains-table/domains-table.tsx
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
@@ -2016,12 +2045,15 @@ msgid "Status"
|
|||||||
msgstr "状态"
|
msgstr "状态"
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "Status and response time over the selected period"
|
#~ msgid "Status and response time over the selected period"
|
||||||
|
#~ msgstr ""
|
||||||
|
|
||||||
|
#: src/components/routes/status-pages.tsx
|
||||||
|
msgid "Status Page Manager"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
#: src/components/routes/status-pages.tsx
|
|
||||||
msgid "Status Pages"
|
msgid "Status Pages"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -2065,8 +2097,8 @@ msgid "System load averages over time"
|
|||||||
msgstr "系统负载平均值随时间变化"
|
msgstr "系统负载平均值随时间变化"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
msgid "System Monitoring"
|
#~ msgid "System Monitoring"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/systemd-table/systemd-table.tsx
|
#: src/components/systemd-table/systemd-table.tsx
|
||||||
msgid "Systemd Services"
|
msgid "Systemd Services"
|
||||||
@@ -2091,6 +2123,8 @@ msgctxt "Tabs system layout option"
|
|||||||
msgid "Tabs"
|
msgid "Tabs"
|
||||||
msgstr "标签页"
|
msgstr "标签页"
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2213,7 +2247,6 @@ msgstr ""
|
|||||||
msgid "Total: {0}"
|
msgid "Total: {0}"
|
||||||
msgstr "总计: {0}"
|
msgstr "总计: {0}"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Track domain expiry dates and DNS status"
|
msgid "Track domain expiry dates and DNS status"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2223,8 +2256,8 @@ msgid "Track domain expiry dates and watch domains for purchase"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
msgid "Track system resources, containers, and health"
|
#~ msgid "Track system resources, containers, and health"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Track uptime, response times, and service health"
|
msgid "Track uptime, response times, and service health"
|
||||||
@@ -2282,6 +2315,7 @@ msgstr "当状态在上线与掉线之间切换时触发"
|
|||||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||||
msgstr "当任何磁盘的使用率超过阈值时触发"
|
msgstr "当任何磁盘的使用率超过阈值时触发"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/routes/settings/quiet-hours.tsx
|
#: src/components/routes/settings/quiet-hours.tsx
|
||||||
#: src/components/routes/settings/quiet-hours.tsx
|
#: src/components/routes/settings/quiet-hours.tsx
|
||||||
@@ -2416,7 +2450,6 @@ msgstr "利用率"
|
|||||||
msgid "Value"
|
msgid "Value"
|
||||||
msgstr "值"
|
msgstr "值"
|
||||||
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "View"
|
msgid "View"
|
||||||
msgstr "视图"
|
msgstr "视图"
|
||||||
@@ -2436,8 +2469,8 @@ msgid "View your 200 most recent alerts."
|
|||||||
msgstr "查看您最近的200个警报。"
|
msgstr "查看您最近的200个警报。"
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "Visible Fields"
|
#~ msgid "Visible Fields"
|
||||||
msgstr "可见列"
|
#~ msgstr "可见列"
|
||||||
|
|
||||||
#: src/components/routes/domain.tsx
|
#: src/components/routes/domain.tsx
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
@@ -2468,7 +2501,6 @@ msgstr "警告阈值"
|
|||||||
msgid "Webhook / Push notifications"
|
msgid "Webhook / Push notifications"
|
||||||
msgstr "Webhook / 推送通知"
|
msgstr "Webhook / 推送通知"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Website & Service Monitoring"
|
msgid "Website & Service Monitoring"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|||||||
@@ -230,6 +230,15 @@ msgstr "所有容器"
|
|||||||
msgid "All Systems"
|
msgid "All Systems"
|
||||||
msgstr "所有系統"
|
msgstr "所有系統"
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "All Tags"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "All Types"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "App update"
|
msgid "App update"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -465,8 +474,8 @@ msgstr ""
|
|||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "Check Now"
|
#~ msgid "Check Now"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/heartbeat.tsx
|
#: src/components/routes/settings/heartbeat.tsx
|
||||||
msgid "Check your monitoring service"
|
msgid "Check your monitoring service"
|
||||||
@@ -503,8 +512,8 @@ msgid "Close"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
#~ msgid "Columns"
|
msgid "Columns"
|
||||||
#~ msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
@@ -784,6 +793,14 @@ msgctxt "Layout display options"
|
|||||||
msgid "Display"
|
msgid "Display"
|
||||||
msgstr "顯示"
|
msgstr "顯示"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "Display"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
msgid "Display Columns"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/domain.tsx
|
#: src/components/routes/domain.tsx
|
||||||
#~ msgid "DNS Records"
|
#~ msgid "DNS Records"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
@@ -814,7 +831,6 @@ msgid "Domain and SSL expiry calendar"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/domains-table/domains-table.tsx
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Domain Monitoring"
|
msgid "Domain Monitoring"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1407,7 +1423,6 @@ msgid "Monitor updated successfully"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
msgid "Monitor websites, APIs, and services"
|
msgid "Monitor websites, APIs, and services"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -1448,6 +1463,10 @@ msgstr ""
|
|||||||
msgid "Net"
|
msgid "Net"
|
||||||
msgstr "網絡"
|
msgstr "網絡"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
#~ msgid "Network (Grouped)"
|
||||||
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system/charts/network-charts.tsx
|
#: src/components/routes/system/charts/network-charts.tsx
|
||||||
msgid "Network traffic of docker containers"
|
msgid "Network traffic of docker containers"
|
||||||
msgstr "Docker 容器的網絡流量"
|
msgstr "Docker 容器的網絡流量"
|
||||||
@@ -1471,8 +1490,8 @@ msgid "No"
|
|||||||
msgstr "否"
|
msgstr "否"
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "No data available for selected time range"
|
#~ msgid "No data available for selected time range"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/incidents.tsx
|
#: src/components/routes/incidents.tsx
|
||||||
msgid "No incidents found."
|
msgid "No incidents found."
|
||||||
@@ -1553,6 +1572,11 @@ msgstr "開啟選單"
|
|||||||
msgid "Optional description for this monitor"
|
msgid "Optional description for this monitor"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "Options"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/login/auth-form.tsx
|
#: src/components/login/auth-form.tsx
|
||||||
msgid "Or continue with"
|
msgid "Or continue with"
|
||||||
msgstr "或繼續使用"
|
msgstr "或繼續使用"
|
||||||
@@ -1626,6 +1650,8 @@ msgid "Paused"
|
|||||||
msgstr "已暫停"
|
msgstr "已暫停"
|
||||||
|
|
||||||
#. placeholder {0}: stats.paused
|
#. placeholder {0}: stats.paused
|
||||||
|
#. placeholder {0}: statusCounts.paused
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
msgid "Paused ({0})"
|
msgid "Paused ({0})"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1804,6 +1830,10 @@ msgstr "已解決"
|
|||||||
msgid "Response"
|
msgid "Response"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/routes/monitor.tsx
|
||||||
|
msgid "Response time over the selected period"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
#~ msgid "Response Times"
|
#~ msgid "Response Times"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
@@ -2004,7 +2034,6 @@ msgstr "狀態"
|
|||||||
|
|
||||||
#: src/components/containers-table/containers-table-columns.tsx
|
#: src/components/containers-table/containers-table-columns.tsx
|
||||||
#: src/components/domains-table/domains-table.tsx
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/domains-table/domains-table.tsx
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
@@ -2016,12 +2045,15 @@ msgid "Status"
|
|||||||
msgstr "狀態"
|
msgstr "狀態"
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "Status and response time over the selected period"
|
#~ msgid "Status and response time over the selected period"
|
||||||
|
#~ msgstr ""
|
||||||
|
|
||||||
|
#: src/components/routes/status-pages.tsx
|
||||||
|
msgid "Status Page Manager"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
#: src/components/routes/status-pages.tsx
|
|
||||||
msgid "Status Pages"
|
msgid "Status Pages"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -2065,8 +2097,8 @@ msgid "System load averages over time"
|
|||||||
msgstr "系統平均負載隨時間變化"
|
msgstr "系統平均負載隨時間變化"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
msgid "System Monitoring"
|
#~ msgid "System Monitoring"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/systemd-table/systemd-table.tsx
|
#: src/components/systemd-table/systemd-table.tsx
|
||||||
msgid "Systemd Services"
|
msgid "Systemd Services"
|
||||||
@@ -2091,6 +2123,8 @@ msgctxt "Tabs system layout option"
|
|||||||
msgid "Tabs"
|
msgid "Tabs"
|
||||||
msgstr "分頁"
|
msgstr "分頁"
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2213,7 +2247,6 @@ msgstr ""
|
|||||||
msgid "Total: {0}"
|
msgid "Total: {0}"
|
||||||
msgstr "總計: {0}"
|
msgstr "總計: {0}"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Track domain expiry dates and DNS status"
|
msgid "Track domain expiry dates and DNS status"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2223,8 +2256,8 @@ msgid "Track domain expiry dates and watch domains for purchase"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
msgid "Track system resources, containers, and health"
|
#~ msgid "Track system resources, containers, and health"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Track uptime, response times, and service health"
|
msgid "Track uptime, response times, and service health"
|
||||||
@@ -2282,6 +2315,7 @@ msgstr "當狀態在上和下之間切換時觸發"
|
|||||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||||
msgstr "當任何磁碟的使用超過閾值時觸發"
|
msgstr "當任何磁碟的使用超過閾值時觸發"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/routes/settings/quiet-hours.tsx
|
#: src/components/routes/settings/quiet-hours.tsx
|
||||||
#: src/components/routes/settings/quiet-hours.tsx
|
#: src/components/routes/settings/quiet-hours.tsx
|
||||||
@@ -2416,7 +2450,6 @@ msgstr "利用率"
|
|||||||
msgid "Value"
|
msgid "Value"
|
||||||
msgstr "值"
|
msgstr "值"
|
||||||
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "View"
|
msgid "View"
|
||||||
msgstr "檢視"
|
msgstr "檢視"
|
||||||
@@ -2436,8 +2469,8 @@ msgid "View your 200 most recent alerts."
|
|||||||
msgstr "檢視最近 200 則警報。"
|
msgstr "檢視最近 200 則警報。"
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "Visible Fields"
|
#~ msgid "Visible Fields"
|
||||||
msgstr "可見欄位"
|
#~ msgstr "可見欄位"
|
||||||
|
|
||||||
#: src/components/routes/domain.tsx
|
#: src/components/routes/domain.tsx
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
@@ -2468,7 +2501,6 @@ msgstr "警告閾值"
|
|||||||
msgid "Webhook / Push notifications"
|
msgid "Webhook / Push notifications"
|
||||||
msgstr "Webhook / 推送通知"
|
msgstr "Webhook / 推送通知"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Website & Service Monitoring"
|
msgid "Website & Service Monitoring"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|||||||
@@ -230,6 +230,15 @@ msgstr "所有容器"
|
|||||||
msgid "All Systems"
|
msgid "All Systems"
|
||||||
msgstr "所有系統"
|
msgstr "所有系統"
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "All Tags"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "All Types"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "App update"
|
msgid "App update"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -465,8 +474,8 @@ msgstr ""
|
|||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "Check Now"
|
#~ msgid "Check Now"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/heartbeat.tsx
|
#: src/components/routes/settings/heartbeat.tsx
|
||||||
msgid "Check your monitoring service"
|
msgid "Check your monitoring service"
|
||||||
@@ -503,8 +512,8 @@ msgid "Close"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
#~ msgid "Columns"
|
msgid "Columns"
|
||||||
#~ msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
@@ -784,6 +793,14 @@ msgctxt "Layout display options"
|
|||||||
msgid "Display"
|
msgid "Display"
|
||||||
msgstr "顯示"
|
msgstr "顯示"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "Display"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
msgid "Display Columns"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/domain.tsx
|
#: src/components/routes/domain.tsx
|
||||||
#~ msgid "DNS Records"
|
#~ msgid "DNS Records"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
@@ -814,7 +831,6 @@ msgid "Domain and SSL expiry calendar"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/domains-table/domains-table.tsx
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Domain Monitoring"
|
msgid "Domain Monitoring"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1407,7 +1423,6 @@ msgid "Monitor updated successfully"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
msgid "Monitor websites, APIs, and services"
|
msgid "Monitor websites, APIs, and services"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -1448,6 +1463,10 @@ msgstr ""
|
|||||||
msgid "Net"
|
msgid "Net"
|
||||||
msgstr "網路"
|
msgstr "網路"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
#~ msgid "Network (Grouped)"
|
||||||
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system/charts/network-charts.tsx
|
#: src/components/routes/system/charts/network-charts.tsx
|
||||||
msgid "Network traffic of docker containers"
|
msgid "Network traffic of docker containers"
|
||||||
msgstr "Docker 容器的網路流量"
|
msgstr "Docker 容器的網路流量"
|
||||||
@@ -1471,8 +1490,8 @@ msgid "No"
|
|||||||
msgstr "否"
|
msgstr "否"
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "No data available for selected time range"
|
#~ msgid "No data available for selected time range"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/incidents.tsx
|
#: src/components/routes/incidents.tsx
|
||||||
msgid "No incidents found."
|
msgid "No incidents found."
|
||||||
@@ -1553,6 +1572,11 @@ msgstr "開啟選單"
|
|||||||
msgid "Optional description for this monitor"
|
msgid "Optional description for this monitor"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
|
msgid "Options"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/login/auth-form.tsx
|
#: src/components/login/auth-form.tsx
|
||||||
msgid "Or continue with"
|
msgid "Or continue with"
|
||||||
msgstr "或繼續使用"
|
msgstr "或繼續使用"
|
||||||
@@ -1626,6 +1650,8 @@ msgid "Paused"
|
|||||||
msgstr "已暫停"
|
msgstr "已暫停"
|
||||||
|
|
||||||
#. placeholder {0}: stats.paused
|
#. placeholder {0}: stats.paused
|
||||||
|
#. placeholder {0}: statusCounts.paused
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
msgid "Paused ({0})"
|
msgid "Paused ({0})"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1804,6 +1830,10 @@ msgstr "已解決"
|
|||||||
msgid "Response"
|
msgid "Response"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/routes/monitor.tsx
|
||||||
|
msgid "Response time over the selected period"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
#~ msgid "Response Times"
|
#~ msgid "Response Times"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
@@ -2004,7 +2034,6 @@ msgstr "狀態"
|
|||||||
|
|
||||||
#: src/components/containers-table/containers-table-columns.tsx
|
#: src/components/containers-table/containers-table-columns.tsx
|
||||||
#: src/components/domains-table/domains-table.tsx
|
#: src/components/domains-table/domains-table.tsx
|
||||||
#: src/components/domains-table/domains-table.tsx
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
@@ -2016,12 +2045,15 @@ msgid "Status"
|
|||||||
msgstr "狀態"
|
msgstr "狀態"
|
||||||
|
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
msgid "Status and response time over the selected period"
|
#~ msgid "Status and response time over the selected period"
|
||||||
|
#~ msgstr ""
|
||||||
|
|
||||||
|
#: src/components/routes/status-pages.tsx
|
||||||
|
msgid "Status Page Manager"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
#: src/components/routes/status-pages.tsx
|
|
||||||
msgid "Status Pages"
|
msgid "Status Pages"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -2065,8 +2097,8 @@ msgid "System load averages over time"
|
|||||||
msgstr "系統平均負載隨時間變化"
|
msgstr "系統平均負載隨時間變化"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
msgid "System Monitoring"
|
#~ msgid "System Monitoring"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/systemd-table/systemd-table.tsx
|
#: src/components/systemd-table/systemd-table.tsx
|
||||||
msgid "Systemd Services"
|
msgid "Systemd Services"
|
||||||
@@ -2091,6 +2123,8 @@ msgctxt "Tabs system layout option"
|
|||||||
msgid "Tabs"
|
msgid "Tabs"
|
||||||
msgstr "分頁"
|
msgstr "分頁"
|
||||||
|
|
||||||
|
#: src/components/domains-table/domains-table.tsx
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2213,7 +2247,6 @@ msgstr ""
|
|||||||
msgid "Total: {0}"
|
msgid "Total: {0}"
|
||||||
msgstr "總計:{0}"
|
msgstr "總計:{0}"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Track domain expiry dates and DNS status"
|
msgid "Track domain expiry dates and DNS status"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2223,8 +2256,8 @@ msgid "Track domain expiry dates and watch domains for purchase"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
msgid "Track system resources, containers, and health"
|
#~ msgid "Track system resources, containers, and health"
|
||||||
msgstr ""
|
#~ msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Track uptime, response times, and service health"
|
msgid "Track uptime, response times, and service health"
|
||||||
@@ -2282,6 +2315,7 @@ msgstr "當連線和離線時觸發"
|
|||||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||||
msgstr "當任何磁碟使用率超過閾值時觸發"
|
msgstr "當任何磁碟使用率超過閾值時觸發"
|
||||||
|
|
||||||
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
#: src/components/monitors-table/monitors-table.tsx
|
||||||
#: src/components/routes/settings/quiet-hours.tsx
|
#: src/components/routes/settings/quiet-hours.tsx
|
||||||
#: src/components/routes/settings/quiet-hours.tsx
|
#: src/components/routes/settings/quiet-hours.tsx
|
||||||
@@ -2416,7 +2450,6 @@ msgstr "利用率"
|
|||||||
msgid "Value"
|
msgid "Value"
|
||||||
msgstr "值"
|
msgstr "值"
|
||||||
|
|
||||||
#: src/components/monitors-table/monitors-table.tsx
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "View"
|
msgid "View"
|
||||||
msgstr "檢視"
|
msgstr "檢視"
|
||||||
@@ -2436,8 +2469,8 @@ msgid "View your 200 most recent alerts."
|
|||||||
msgstr "檢視最近 200 則警報。"
|
msgstr "檢視最近 200 則警報。"
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "Visible Fields"
|
#~ msgid "Visible Fields"
|
||||||
msgstr "顯示欄位"
|
#~ msgstr "顯示欄位"
|
||||||
|
|
||||||
#: src/components/routes/domain.tsx
|
#: src/components/routes/domain.tsx
|
||||||
#: src/components/routes/monitor.tsx
|
#: src/components/routes/monitor.tsx
|
||||||
@@ -2468,7 +2501,6 @@ msgstr "警告閾值"
|
|||||||
msgid "Webhook / Push notifications"
|
msgid "Webhook / Push notifications"
|
||||||
msgstr "Webhook / 推送通知"
|
msgstr "Webhook / 推送通知"
|
||||||
|
|
||||||
#: src/components/routes/home.tsx
|
|
||||||
#: src/components/routes/monitoring.tsx
|
#: src/components/routes/monitoring.tsx
|
||||||
msgid "Website & Service Monitoring"
|
msgid "Website & Service Monitoring"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|||||||
Submodule reference/domain-locker deleted from 9c2962de33
Submodule reference/uptime-kuma deleted from 2f45b46315
@@ -0,0 +1,160 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func tryAlternativeWHOISService(ctx context.Context, serviceName, url, domainName string) (string, error) {
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to create request for %s: %w", serviceName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36")
|
||||||
|
req.Header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")
|
||||||
|
|
||||||
|
client := &http.Client{Timeout: 15 * time.Second}
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to fetch %s: %w", serviceName, err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != 200 {
|
||||||
|
return "", fmt.Errorf("%s returned status %d", serviceName, resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
body, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to read %s response: %w", serviceName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(body), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseAlternativeWHOISHTML(html, domainName, serviceName string) {
|
||||||
|
fmt.Printf("HTML length from %s: %d characters\n", serviceName, len(html))
|
||||||
|
|
||||||
|
// Look for expiry date patterns (common across WHOIS services)
|
||||||
|
expiryPatterns := []string{
|
||||||
|
`Expiry Date:\s*</[^>]*>\s*([^<\n]+)`,
|
||||||
|
`Expiry Date:</[^>]*>\s*([^<\n]+)`,
|
||||||
|
`Expires on:\s*</[^>]*>\s*([^<\n]+)`,
|
||||||
|
`Expires:\s*</[^>]*>\s*([^<\n]+)`,
|
||||||
|
`"expiry":"([^"]+)"`,
|
||||||
|
`"expires":"([^"]+)"`,
|
||||||
|
`data-expiry="([^"]+)"`,
|
||||||
|
`expiry_date["\s]*:\s*"([^"]+)"`,
|
||||||
|
`expires["\s]*:\s*"([^"]+)"`,
|
||||||
|
`\d{4}-\d{2}-\d{2}`, // ISO date pattern
|
||||||
|
`\d{2}/\d{2}/\d{4}`, // DD/MM/YYYY pattern
|
||||||
|
`\d{2}-\d{2}-\d{4}`, // MM-DD-YYYY pattern
|
||||||
|
`202\d-\d{2}-\d{2}`, // 202x-xx-xx pattern
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("\n=== Searching for expiry dates on %s ===\n", serviceName)
|
||||||
|
for _, pattern := range expiryPatterns {
|
||||||
|
re := regexp.MustCompile(pattern)
|
||||||
|
matches := re.FindAllStringSubmatch(html, -1)
|
||||||
|
if len(matches) > 0 {
|
||||||
|
fmt.Printf("Pattern '%s' found %d matches:\n", pattern, len(matches))
|
||||||
|
for i, match := range matches {
|
||||||
|
if len(match) > 1 {
|
||||||
|
fmt.Printf(" Match %d: %s\n", i+1, strings.TrimSpace(match[1]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look for registrar name
|
||||||
|
registrarPatterns := []string{
|
||||||
|
`Registrar:\s*</[^>]*>\s*([^<\n]+)`,
|
||||||
|
`Registrar:</[^>]*>\s*([^<\n]+)`,
|
||||||
|
`Registered through:\s*</[^>]*>\s*([^<\n]+)`,
|
||||||
|
`"registrar":"([^"]+)"`,
|
||||||
|
`data-registrar="([^"]+)"`,
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("\n=== Searching for registrar on %s ===\n", serviceName)
|
||||||
|
for _, pattern := range registrarPatterns {
|
||||||
|
re := regexp.MustCompile(pattern)
|
||||||
|
matches := re.FindAllStringSubmatch(html, -1)
|
||||||
|
if len(matches) > 0 {
|
||||||
|
fmt.Printf("Pattern '%s' found %d matches:\n", pattern, len(matches))
|
||||||
|
for i, match := range matches {
|
||||||
|
if len(match) > 1 {
|
||||||
|
fmt.Printf(" Match %d: %s\n", i+1, strings.TrimSpace(match[1]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look for status
|
||||||
|
statusPatterns := []string{
|
||||||
|
`Status:\s*</[^>]*>\s*([^<\n]+)`,
|
||||||
|
`Status:</[^>]*>\s*([^<\n]+)`,
|
||||||
|
`"status":"([^"]+)"`,
|
||||||
|
`data-status="([^"]+)"`,
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("\n=== Searching for status on %s ===\n", serviceName)
|
||||||
|
for _, pattern := range statusPatterns {
|
||||||
|
re := regexp.MustCompile(pattern)
|
||||||
|
matches := re.FindAllStringSubmatch(html, -1)
|
||||||
|
if len(matches) > 0 {
|
||||||
|
fmt.Printf("Pattern '%s' found %d matches:\n", pattern, len(matches))
|
||||||
|
for i, match := range matches {
|
||||||
|
if len(match) > 1 {
|
||||||
|
fmt.Printf(" Match %d: %s\n", i+1, strings.TrimSpace(match[1]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show sample HTML
|
||||||
|
fmt.Printf("\n=== Sample HTML from %s (first 1500 chars) ===\n", serviceName)
|
||||||
|
if len(html) > 1500 {
|
||||||
|
fmt.Printf("%s...\n", html[:1500])
|
||||||
|
} else {
|
||||||
|
fmt.Printf("%s\n", html)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
testDomains := []string{"bookra.eu", "sportcreative.eu"}
|
||||||
|
|
||||||
|
for _, domain := range testDomains {
|
||||||
|
fmt.Printf("Testing alternative WHOIS services for: %s\n", domain)
|
||||||
|
fmt.Println("========================================")
|
||||||
|
|
||||||
|
// Try multiple alternative WHOIS services
|
||||||
|
services := []struct {
|
||||||
|
name string
|
||||||
|
url string
|
||||||
|
}{
|
||||||
|
{"whois.com", fmt.Sprintf("https://www.whois.com/whois/%s", domain)},
|
||||||
|
{"who.is", fmt.Sprintf("https://who.is/whois/%s", domain)},
|
||||||
|
{"ip2location.com", fmt.Sprintf("https://www.ip2location.com/whois/%s", domain)},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, service := range services {
|
||||||
|
fmt.Printf("\n--- Testing %s ---\n", service.name)
|
||||||
|
|
||||||
|
html, err := tryAlternativeWHOISService(context.Background(), service.name, service.url, domain)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error: %v\n", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
parseAlternativeWHOISHTML(html, domain, service.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("\n========================================\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// parseFlexibleDate parses dates in various formats
|
||||||
|
func parseFlexibleDate(dateString string) string {
|
||||||
|
if dateString == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove common separators and normalize
|
||||||
|
normalized := regexp.MustCompile(`[./-]`).ReplaceAllString(dateString, "-")
|
||||||
|
normalized = regexp.MustCompile(`\s+`).ReplaceAllString(normalized, "")
|
||||||
|
|
||||||
|
// Try different date formats
|
||||||
|
formats := []string{
|
||||||
|
// DD.MM.YYYY, DD/MM/YYYY, DD-MM-YYYY
|
||||||
|
`^(\d{2})[-/.](\d{2})[-/.](\d{4})$`,
|
||||||
|
// YYYY-MM-DD, YYYY/MM/DD, YYYY.MM.DD
|
||||||
|
`^(\d{4})[-/.](\d{2})[-/.](\d{2})$`,
|
||||||
|
// MM-DD-YYYY, MM/DD/YYYY, MM.DD.YYYY
|
||||||
|
`^(\d{2})[-/.](\d{2})[-/.](\d{4})$`,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, format := range formats {
|
||||||
|
re := regexp.MustCompile(format)
|
||||||
|
match := re.FindStringSubmatch(normalized)
|
||||||
|
if match != nil {
|
||||||
|
part1, part2, part3 := match[1], match[2], match[3]
|
||||||
|
|
||||||
|
// Determine if it's DD.MM.YYYY or YYYY.MM.DD format
|
||||||
|
var year, month, day string
|
||||||
|
|
||||||
|
if len(part1) == 4 {
|
||||||
|
// YYYY.MM.DD format
|
||||||
|
year = part1
|
||||||
|
month = part2
|
||||||
|
day = part3
|
||||||
|
} else {
|
||||||
|
// DD.MM.YYYY format (most common)
|
||||||
|
day = part1
|
||||||
|
month = part2
|
||||||
|
year = part3
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate and format
|
||||||
|
yearNum, _ := strconv.Atoi(year)
|
||||||
|
monthNum, _ := strconv.Atoi(month)
|
||||||
|
dayNum, _ := strconv.Atoi(day)
|
||||||
|
|
||||||
|
if yearNum >= 2000 && yearNum <= 2100 && monthNum >= 1 && monthNum <= 12 && dayNum >= 1 && dayNum <= 31 {
|
||||||
|
return fmt.Sprintf("%s-%s-%s", year, fmt.Sprintf("%02s", month), fmt.Sprintf("%02s", day))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
testDates := []string{
|
||||||
|
"15.06.2026",
|
||||||
|
"13.11.2029",
|
||||||
|
"2026-06-15",
|
||||||
|
"15/06/2026",
|
||||||
|
"13-11-2029",
|
||||||
|
"2026.06.15",
|
||||||
|
"15 06 2026",
|
||||||
|
"invalid-date",
|
||||||
|
"32.13.2026", // Invalid day/month
|
||||||
|
"1999.12.31", // Too old
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("Testing Flexible Date Parsing")
|
||||||
|
fmt.Println("=============================")
|
||||||
|
|
||||||
|
for _, date := range testDates {
|
||||||
|
result := parseFlexibleDate(date)
|
||||||
|
status := "✅"
|
||||||
|
if result == "" {
|
||||||
|
status = "❌"
|
||||||
|
}
|
||||||
|
fmt.Printf("%s '%s' -> '%s'\n", status, date, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("\nExample Usage:")
|
||||||
|
fmt.Println("=============")
|
||||||
|
fmt.Println("User can paste: '15.06.2026, 13.11.2029'")
|
||||||
|
fmt.Println("System will parse: '2026-06-15', '2029-11-13'")
|
||||||
|
}
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
testDomains := []string{"bookra.eu", "sportcreative.eu"}
|
||||||
|
|
||||||
|
for _, domain := range testDomains {
|
||||||
|
fmt.Printf("Testing EURid web scraping for: %s\n", domain)
|
||||||
|
fmt.Println("----------------------------------------")
|
||||||
|
|
||||||
|
// EURid web WHOIS URL
|
||||||
|
url := fmt.Sprintf("https://www.eurid.eu/en/registrations/search/?domain=%s", domain)
|
||||||
|
|
||||||
|
req, err := http.NewRequestWithContext(context.Background(), "GET", url, nil)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to create request: %v\n", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36")
|
||||||
|
req.Header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")
|
||||||
|
|
||||||
|
client := &http.Client{Timeout: 15 * time.Second}
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to fetch EURid web page: %v\n", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != 200 {
|
||||||
|
fmt.Printf("EURid web page returned status %d\n", resp.StatusCode)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the HTML response
|
||||||
|
body, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to read EURid response: %v\n", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
html := string(body)
|
||||||
|
fmt.Printf("HTML length: %d characters\n", len(html))
|
||||||
|
|
||||||
|
// Look for expiry date patterns
|
||||||
|
expiryPatterns := []string{
|
||||||
|
`Expiry date:\s*</strong>\s*(\d{2}/\d{2}/\d{4})`,
|
||||||
|
`Expiry date:</strong>\s*(\d{2}/\d{2}/\d{4})`,
|
||||||
|
`Expiry date</strong>\s*(\d{2}/\d{2}/\d{4})`,
|
||||||
|
`"expiryDate":"([^"]+)"`,
|
||||||
|
`data-expiry="([^"]+)"`,
|
||||||
|
`\d{2}/\d{2}/\d{4}`, // Generic date pattern
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("\n=== Searching for expiry dates ===")
|
||||||
|
for _, pattern := range expiryPatterns {
|
||||||
|
re := regexp.MustCompile(pattern)
|
||||||
|
matches := re.FindAllStringSubmatch(html, -1)
|
||||||
|
if len(matches) > 0 {
|
||||||
|
fmt.Printf("Pattern '%s' found %d matches:\n", pattern, len(matches))
|
||||||
|
for i, match := range matches {
|
||||||
|
if len(match) > 1 {
|
||||||
|
fmt.Printf(" Match %d: %s\n", i+1, match[1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look for registrar patterns
|
||||||
|
registrarPatterns := []string{
|
||||||
|
`Registrar:\s*</strong>\s*([^<\n]+)`,
|
||||||
|
`Registrar:</strong>\s*([^<\n]+)`,
|
||||||
|
`Registrar</strong>\s*([^<\n]+)`,
|
||||||
|
`"registrar":"([^"]+)"`,
|
||||||
|
`data-registrar="([^"]+)"`,
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("\n=== Searching for registrar ===")
|
||||||
|
for _, pattern := range registrarPatterns {
|
||||||
|
re := regexp.MustCompile(pattern)
|
||||||
|
matches := re.FindAllStringSubmatch(html, -1)
|
||||||
|
if len(matches) > 0 {
|
||||||
|
fmt.Printf("Pattern '%s' found %d matches:\n", pattern, len(matches))
|
||||||
|
for i, match := range matches {
|
||||||
|
if len(match) > 1 {
|
||||||
|
fmt.Printf(" Match %d: %s\n", i+1, strings.TrimSpace(match[1]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show some sample HTML to understand the structure
|
||||||
|
fmt.Println("\n=== Sample HTML (first 1000 chars) ===")
|
||||||
|
if len(html) > 1000 {
|
||||||
|
fmt.Printf("%s...\n", html[:1000])
|
||||||
|
} else {
|
||||||
|
fmt.Printf("%s\n", html)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("\n========================================\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,163 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func tryEURidEndpoint(ctx context.Context, url, domainName string) (string, error) {
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to create request: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use more realistic browser headers
|
||||||
|
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36")
|
||||||
|
req.Header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7")
|
||||||
|
req.Header.Set("Accept-Language", "en-US,en;q=0.9")
|
||||||
|
req.Header.Set("Accept-Encoding", "gzip, deflate, br")
|
||||||
|
req.Header.Set("Cache-Control", "no-cache")
|
||||||
|
req.Header.Set("Pragma", "no-cache")
|
||||||
|
req.Header.Set("Sec-Ch-Ua", "\"Not_A Brand\";v=\"8\", \"Chromium\";v=\"120\", \"Google Chrome\";v=\"120\"")
|
||||||
|
req.Header.Set("Sec-Ch-Ua-Mobile", "?0")
|
||||||
|
req.Header.Set("Sec-Ch-Ua-Platform", "\"Windows\"")
|
||||||
|
req.Header.Set("Sec-Fetch-Dest", "document")
|
||||||
|
req.Header.Set("Sec-Fetch-Mode", "navigate")
|
||||||
|
req.Header.Set("Sec-Fetch-Site", "none")
|
||||||
|
req.Header.Set("Sec-Fetch-User", "?1")
|
||||||
|
req.Header.Set("Upgrade-Insecure-Requests", "1")
|
||||||
|
|
||||||
|
client := &http.Client{Timeout: 20 * time.Second}
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to fetch EURid web page: %w", err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != 200 {
|
||||||
|
return "", fmt.Errorf("EURid web page returned status %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the HTML response
|
||||||
|
body, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to read EURid response: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(body), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseEURidWebHTML(html, domainName string) {
|
||||||
|
fmt.Printf("HTML length: %d characters\n", len(html))
|
||||||
|
|
||||||
|
// Look for expiry date patterns
|
||||||
|
expiryPatterns := []string{
|
||||||
|
`Expiry date:\s*</strong>\s*(\d{2}/\d{2}/\d{4})`,
|
||||||
|
`Expiry date:</strong>\s*(\d{2}/\d{2}/\d{4})`,
|
||||||
|
`Expiry date</strong>\s*(\d{2}/\d{2}/\d{4})`,
|
||||||
|
`"expiryDate":"([^"]+)"`,
|
||||||
|
`data-expiry="([^"]+)"`,
|
||||||
|
`\d{2}/\d{2}/\d{4}`, // Generic date pattern
|
||||||
|
`\d{4}-\d{2}-\d{2}`, // ISO date pattern
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("\n=== Searching for expiry dates ===")
|
||||||
|
for _, pattern := range expiryPatterns {
|
||||||
|
re := regexp.MustCompile(pattern)
|
||||||
|
matches := re.FindAllStringSubmatch(html, -1)
|
||||||
|
if len(matches) > 0 {
|
||||||
|
fmt.Printf("Pattern '%s' found %d matches:\n", pattern, len(matches))
|
||||||
|
for i, match := range matches {
|
||||||
|
if len(match) > 1 {
|
||||||
|
fmt.Printf(" Match %d: %s\n", i+1, match[1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look for registrar patterns
|
||||||
|
registrarPatterns := []string{
|
||||||
|
`Registrar:\s*</strong>\s*([^<\n]+)`,
|
||||||
|
`Registrar:</strong>\s*([^<\n]+)`,
|
||||||
|
`Registrar</strong>\s*([^<\n]+)`,
|
||||||
|
`"registrar":"([^"]+)"`,
|
||||||
|
`data-registrar="([^"]+)"`,
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("\n=== Searching for registrar ===")
|
||||||
|
for _, pattern := range registrarPatterns {
|
||||||
|
re := regexp.MustCompile(pattern)
|
||||||
|
matches := re.FindAllStringSubmatch(html, -1)
|
||||||
|
if len(matches) > 0 {
|
||||||
|
fmt.Printf("Pattern '%s' found %d matches:\n", pattern, len(matches))
|
||||||
|
for i, match := range matches {
|
||||||
|
if len(match) > 1 {
|
||||||
|
fmt.Printf(" Match %d: %s\n", i+1, strings.TrimSpace(match[1]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look for any JSON data
|
||||||
|
jsonPatterns := []string{
|
||||||
|
`\{[^}]*"expiry[^}]*\}`,
|
||||||
|
`\{[^}]*"registrar[^}]*\}`,
|
||||||
|
`\{[^}]*"domain"[^}]*\}`,
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("\n=== Searching for JSON data ===")
|
||||||
|
for _, pattern := range jsonPatterns {
|
||||||
|
re := regexp.MustCompile(pattern)
|
||||||
|
matches := re.FindAllString(html, -1)
|
||||||
|
if len(matches) > 0 {
|
||||||
|
fmt.Printf("Pattern '%s' found %d matches:\n", pattern, len(matches))
|
||||||
|
for i, match := range matches {
|
||||||
|
fmt.Printf(" Match %d: %s\n", i+1, match)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show sample HTML
|
||||||
|
fmt.Println("\n=== Sample HTML (first 2000 chars) ===")
|
||||||
|
if len(html) > 2000 {
|
||||||
|
fmt.Printf("%s...\n", html[:2000])
|
||||||
|
} else {
|
||||||
|
fmt.Printf("%s\n", html)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
testDomains := []string{"bookra.eu", "sportcreative.eu"}
|
||||||
|
|
||||||
|
for _, domain := range testDomains {
|
||||||
|
fmt.Printf("Testing EURid web scraping for: %s\n", domain)
|
||||||
|
fmt.Println("========================================")
|
||||||
|
|
||||||
|
// Try multiple EURid endpoints
|
||||||
|
endpoints := []string{
|
||||||
|
fmt.Sprintf("https://whois.eurid.eu/en/?q=%s", domain),
|
||||||
|
fmt.Sprintf("https://whois.eurid.eu/en/search?q=%s", domain),
|
||||||
|
fmt.Sprintf("https://www.eurid.eu/en/whois/?domain=%s", domain),
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, url := range endpoints {
|
||||||
|
fmt.Printf("\n--- Attempt %d: %s ---\n", i+1, url)
|
||||||
|
|
||||||
|
html, err := tryEURidEndpoint(context.Background(), url, domain)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error: %v\n", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
parseEURidWebHTML(html, domain)
|
||||||
|
break // If we got HTML, don't try other endpoints
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("\n========================================\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Test TCP WHOIS for .eu domains
|
||||||
|
testDomains := []string{"bookra.eu", "sportcreative.eu"}
|
||||||
|
|
||||||
|
for _, domainName := range testDomains {
|
||||||
|
fmt.Printf("Testing TCP WHOIS for: %s\n", domainName)
|
||||||
|
fmt.Println("----------------------------------------")
|
||||||
|
|
||||||
|
// Extract TLD
|
||||||
|
parts := strings.Split(domainName, ".")
|
||||||
|
if len(parts) < 2 {
|
||||||
|
fmt.Printf("Invalid domain format: %s\n", domainName)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
tld := strings.ToLower(parts[len(parts)-1])
|
||||||
|
|
||||||
|
// WHOIS server for .eu
|
||||||
|
server := "whois.eu"
|
||||||
|
if tld != "eu" {
|
||||||
|
fmt.Printf("Skipping non-eu domain: %s\n", domainName)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
addr := net.JoinHostPort(server, "43")
|
||||||
|
fmt.Printf("Connecting to: %s\n", addr)
|
||||||
|
|
||||||
|
// Use longer timeout for .eu domains
|
||||||
|
timeout := 20 * time.Second
|
||||||
|
dialer := &net.Dialer{Timeout: timeout}
|
||||||
|
|
||||||
|
start := time.Now()
|
||||||
|
conn, err := dialer.Dial("tcp", addr)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("TCP WHOIS dial failed: %v\n", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
fmt.Printf("Connected in: %v\n", time.Since(start))
|
||||||
|
|
||||||
|
// Send query
|
||||||
|
query := domainName + "\r\n"
|
||||||
|
fmt.Printf("Sending query: %q\n", query)
|
||||||
|
|
||||||
|
writeStart := time.Now()
|
||||||
|
if _, err := conn.Write([]byte(query)); err != nil {
|
||||||
|
fmt.Printf("TCP WHOIS write failed: %v\n", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fmt.Printf("Write completed in: %v\n", time.Since(writeStart))
|
||||||
|
|
||||||
|
// Set read deadline
|
||||||
|
if err := conn.SetReadDeadline(time.Now().Add(timeout)); err != nil {
|
||||||
|
fmt.Printf("Failed to set read deadline: %v\n", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read response
|
||||||
|
var output strings.Builder
|
||||||
|
buf := make([]byte, 4096)
|
||||||
|
totalRead := 0
|
||||||
|
|
||||||
|
readStart := time.Now()
|
||||||
|
for {
|
||||||
|
n, err := conn.Read(buf)
|
||||||
|
if n > 0 {
|
||||||
|
output.Write(buf[:n])
|
||||||
|
totalRead += n
|
||||||
|
fmt.Printf("Read %d bytes (total: %d)\n", n, totalRead)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
if err.Error() != "EOF" {
|
||||||
|
fmt.Printf("Read error: %v\n", err)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// Prevent infinite loop
|
||||||
|
if totalRead > 10000 {
|
||||||
|
fmt.Printf("Stopping read after 10KB\n")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Read completed in: %v\n", time.Since(readStart))
|
||||||
|
fmt.Printf("Total bytes read: %d\n", totalRead)
|
||||||
|
|
||||||
|
response := output.String()
|
||||||
|
if len(response) > 0 {
|
||||||
|
fmt.Printf("✅ WHOIS Response (first 500 chars):\n")
|
||||||
|
if len(response) > 500 {
|
||||||
|
fmt.Printf("%s...\n", response[:500])
|
||||||
|
} else {
|
||||||
|
fmt.Printf("%s\n", response)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Printf("❌ No response received\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println()
|
||||||
|
fmt.Println("========================================")
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/henrygd/beszel/internal/hub/domains/whois"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Create a new lookup service with a dummy API key
|
||||||
|
lookupService := whois.NewLookupService("dummy-api-key")
|
||||||
|
|
||||||
|
// Test domains including .eu domain
|
||||||
|
testDomains := []string{
|
||||||
|
"google.com",
|
||||||
|
"bookra.eu", // Your .eu domain
|
||||||
|
"sportcreative.eu", // Another .eu domain
|
||||||
|
"github.com",
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
fmt.Println("=== WHOIS Lookup Test ===")
|
||||||
|
fmt.Println()
|
||||||
|
|
||||||
|
for _, domainName := range testDomains {
|
||||||
|
fmt.Printf("Testing domain: %s\n", domainName)
|
||||||
|
fmt.Println("----------------------------------------")
|
||||||
|
|
||||||
|
start := time.Now()
|
||||||
|
|
||||||
|
// Test WHOIS lookup
|
||||||
|
whoisData, _, err := lookupService.LookupWHOIS(ctx, domainName)
|
||||||
|
|
||||||
|
duration := time.Since(start)
|
||||||
|
fmt.Printf("Lookup duration: %v\n", duration)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("ERROR: %v\n", err)
|
||||||
|
} else if whoisData != nil {
|
||||||
|
fmt.Printf("✅ WHOIS Data Found:\n")
|
||||||
|
fmt.Printf(" Domain: %s\n", whoisData.DomainName)
|
||||||
|
fmt.Printf(" Status: %v\n", whoisData.Status)
|
||||||
|
fmt.Printf(" DNSSEC: %s\n", whoisData.DNSSEC)
|
||||||
|
|
||||||
|
if whoisData.Dates.ExpiryDate != nil && !whoisData.Dates.ExpiryDate.IsZero() {
|
||||||
|
fmt.Printf(" Expiry Date: %s\n", whoisData.Dates.ExpiryDate.Format("2006-01-02"))
|
||||||
|
}
|
||||||
|
if whoisData.Dates.CreationDate != nil && !whoisData.Dates.CreationDate.IsZero() {
|
||||||
|
fmt.Printf(" Creation Date: %s\n", whoisData.Dates.CreationDate.Format("2006-01-02"))
|
||||||
|
}
|
||||||
|
if whoisData.Dates.UpdatedDate != nil && !whoisData.Dates.UpdatedDate.IsZero() {
|
||||||
|
fmt.Printf(" Updated Date: %s\n", whoisData.Dates.UpdatedDate.Format("2006-01-02"))
|
||||||
|
}
|
||||||
|
|
||||||
|
if whoisData.Registrar.Name != "" {
|
||||||
|
fmt.Printf(" Registrar: %s\n", whoisData.Registrar.Name)
|
||||||
|
}
|
||||||
|
if whoisData.Registrar.ID != "" {
|
||||||
|
fmt.Printf(" Registrar ID: %s\n", whoisData.Registrar.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
if whoisData.Registrant.Name != "" || whoisData.Registrant.Organization != "" {
|
||||||
|
fmt.Printf(" Registrant: %s (%s)\n", whoisData.Registrant.Name, whoisData.Registrant.Organization)
|
||||||
|
}
|
||||||
|
if whoisData.Registrant.Country != "" {
|
||||||
|
fmt.Printf(" Registrant Country: %s\n", whoisData.Registrant.Country)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Printf("❌ No WHOIS data returned\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println()
|
||||||
|
fmt.Println("=== Full Domain Lookup Test ===")
|
||||||
|
|
||||||
|
// Test full domain lookup
|
||||||
|
fullDomain, err := lookupService.LookupDomain(ctx, domainName)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Full lookup ERROR: %v\n", err)
|
||||||
|
} else if fullDomain != nil {
|
||||||
|
fmt.Printf("✅ Full Domain Data:\n")
|
||||||
|
fmt.Printf(" Domain: %s\n", fullDomain.DomainName)
|
||||||
|
fmt.Printf(" Status: %s\n", fullDomain.Status)
|
||||||
|
fmt.Printf(" Active: %t\n", fullDomain.Active)
|
||||||
|
if fullDomain.ExpiryDate != nil {
|
||||||
|
fmt.Printf(" Expiry: %s\n", fullDomain.ExpiryDate.Format("2006-01-02"))
|
||||||
|
daysUntil := int(fullDomain.ExpiryDate.Sub(time.Now()).Hours() / 24)
|
||||||
|
fmt.Printf(" Days Until Expiry: %d\n", daysUntil)
|
||||||
|
}
|
||||||
|
fmt.Printf(" Registrar: %s\n", fullDomain.RegistrarName)
|
||||||
|
fmt.Printf(" DNSSEC: %s\n", fullDomain.DNSSEC)
|
||||||
|
|
||||||
|
if len(fullDomain.IPv4Addresses) > 0 {
|
||||||
|
fmt.Printf(" IPv4 Addresses: %v\n", fullDomain.IPv4Addresses)
|
||||||
|
}
|
||||||
|
if len(fullDomain.IPv6Addresses) > 0 {
|
||||||
|
fmt.Printf(" IPv6 Addresses: %v\n", fullDomain.IPv6Addresses)
|
||||||
|
}
|
||||||
|
if len(fullDomain.NameServers) > 0 {
|
||||||
|
fmt.Printf(" Name Servers: %v\n", fullDomain.NameServers)
|
||||||
|
}
|
||||||
|
if len(fullDomain.MXRecords) > 0 {
|
||||||
|
fmt.Printf(" MX Records: %v\n", fullDomain.MXRecords)
|
||||||
|
}
|
||||||
|
|
||||||
|
if fullDomain.SSLValidTo != nil {
|
||||||
|
fmt.Printf(" SSL Valid Until: %s\n", fullDomain.SSLValidTo.Format("2006-01-02"))
|
||||||
|
sslDaysUntil := int(fullDomain.SSLValidTo.Sub(time.Now()).Hours() / 24)
|
||||||
|
fmt.Printf(" SSL Days Until: %d\n", sslDaysUntil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println()
|
||||||
|
fmt.Println("========================================")
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
// Test WhoisXML API for .eu domains
|
||||||
|
const testWhoisXML = async (domain) => {
|
||||||
|
try {
|
||||||
|
// Note: This would require a real API key to test
|
||||||
|
console.log(`Testing WhoisXML API for: ${domain}`);
|
||||||
|
console.log('Note: WhoisXML API requires an API key to test properly');
|
||||||
|
|
||||||
|
// URL structure they use
|
||||||
|
const url = `https://www.whoisxmlapi.com/whoisserver/WhoisService?apiKey=YOUR_API_KEY&outputFormat=json&domainName=${domain}`;
|
||||||
|
console.log(`WhoisXML URL: ${url}`);
|
||||||
|
|
||||||
|
// Based on their code, WhoisXML should return expiry dates for .eu domains
|
||||||
|
console.log('WhoisXML API typically provides expiry dates for .eu domains that TCP WHOIS does not');
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error: ${error.message}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
testWhoisXML('bookra.eu');
|
||||||
|
testWhoisXML('sportcreative.eu');
|
||||||
Reference in New Issue
Block a user