Compare commits

...

10 Commits

Author SHA1 Message Date
UUBulb
f127ff8141
Merge a5dbc5693d into d835aeb486 2024-12-18 09:26:34 +00:00
uubulb
a5dbc5693d feat: user-specific connection secret 2024-12-18 17:26:24 +08:00
uubulb
4754d283c9 update 2024-12-18 15:29:46 +08:00
naiba
d835aeb486 feat: upgrade frontend
Some checks failed
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Contributors / contributors (push) Has been cancelled
Sync / sync-to-jihulab (push) Has been cancelled
Run Tests / tests (macos) (push) Has been cancelled
Run Tests / tests (ubuntu) (push) Has been cancelled
Run Tests / tests (windows) (push) Has been cancelled
2024-12-17 22:20:02 +08:00
naiba
c952ed0e38 feat: upgrade frontend 2024-12-17 22:05:41 +08:00
github-actions[bot]
35d137d4ea update contributors[no ci] 2024-12-17 21:46:43 +08:00
Weblate (bot)
c32dd4cd75
Translations update from Hosted Weblate (#580)
* Translated using Weblate (Spanish)

Currently translated at 100.0% (45 of 45 strings)

Translated using Weblate (German)

Currently translated at 100.0% (45 of 45 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (45 of 45 strings)

Added translation using Weblate (Spanish)

Translated using Weblate (German)

Currently translated at 100.0% (45 of 45 strings)

Added translation using Weblate (German)

Co-authored-by: Barium <peter.kuehfuss+weblate@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Kris <krishoodbyair@gmail.com>
Co-authored-by: UUBulb <uub@kuzu.uk>
Translate-URL: https://hosted.weblate.org/projects/nezha/nezha-dashboard/de/
Translate-URL: https://hosted.weblate.org/projects/nezha/nezha-dashboard/es/
Translation: Nezha/Nezha Dashboard

* feat: add german support

---------

Co-authored-by: Barium <peter.kuehfuss+weblate@gmail.com>
Co-authored-by: Kris <krishoodbyair@gmail.com>
Co-authored-by: UUBulb <uub@kuzu.uk>
2024-12-17 21:46:25 +08:00
naiba
a9c2abe71e feat: upgrade frontend
Some checks are pending
CodeQL / Analyze (go) (push) Waiting to run
CodeQL / Analyze (javascript) (push) Waiting to run
Contributors / contributors (push) Waiting to run
Sync / sync-to-jihulab (push) Waiting to run
2024-12-16 23:44:51 +08:00
naiba
65d7e4cb98 feat: upgrade frontend 2024-12-16 21:29:23 +08:00
naiba
c1fdd00cf0 feat: upgrade frontend 2024-12-16 21:10:10 +08:00
23 changed files with 572 additions and 49 deletions

View File

@ -67,11 +67,11 @@ add your theme to [service/singleton/frontend-templates.yaml](service/singleton/
<a href="https://github.com/iilemon" title="Sean"><img src="https://avatars.githubusercontent.com/u/33201711?v=4" width="50;" alt="Sean"/></a>
<a href="https://github.com/fscarmen" title="fscarmen"><img src="https://avatars.githubusercontent.com/u/62703343?v=4" width="50;" alt="fscarmen"/></a>
<a href="https://github.com/ch8o" title="no-name-now"><img src="https://avatars.githubusercontent.com/u/9103372?v=4" width="50;" alt="no-name-now"/></a>
<a href="https://github.com/weblate" title="Weblate (bot)"><img src="https://avatars.githubusercontent.com/u/1607653?v=4" width="50;" alt="Weblate (bot)"/></a>
<a href="https://github.com/HsukqiLee" title="HsukqiLee"><img src="https://avatars.githubusercontent.com/u/79034142?v=4" width="50;" alt="HsukqiLee"/></a>
<a href="https://github.com/DarcJC" title="Darc Z."><img src="https://avatars.githubusercontent.com/u/53445798?v=4" width="50;" alt="Darc Z."/></a>
<a href="https://github.com/Creling" title="Creling"><img src="https://avatars.githubusercontent.com/u/43109504?v=4" width="50;" alt="Creling"/></a>
<a href="https://github.com/coreff" title="Core F"><img src="https://avatars.githubusercontent.com/u/38347122?v=4" width="50;" alt="Core F"/></a>
<a href="https://github.com/weblate" title="Weblate (bot)"><img src="https://avatars.githubusercontent.com/u/1607653?v=4" width="50;" alt="Weblate (bot)"/></a>
<a href="https://github.com/adminsama" title="adminsama"><img src="https://avatars.githubusercontent.com/u/60880076?v=4" width="50;" alt="adminsama"/></a>
<a href="https://github.com/acgpiano" title="Acgpiano"><img src="https://avatars.githubusercontent.com/u/15900800?v=4" width="50;" alt="Acgpiano"/></a>
<a href="https://github.com/eya46" title="eya46"><img src="https://avatars.githubusercontent.com/u/61458340?v=4" width="50;" alt="eya46"/></a>

View File

@ -79,7 +79,7 @@ func routers(r *gin.Engine, frontendDist fs.FS) {
auth.GET("/profile", commonHandler(getProfile))
auth.POST("/profile", commonHandler(updateProfile))
auth.GET("/user", commonHandler(listUser))
auth.GET("/user", adminHandler(listUser))
auth.POST("/user", adminHandler(createUser))
auth.POST("/batch-delete/user", adminHandler(batchDeleteUser))
@ -97,7 +97,7 @@ func routers(r *gin.Engine, frontendDist fs.FS) {
auth.PATCH("/notification-group/:id", commonHandler(updateNotificationGroup))
auth.POST("/batch-delete/notification-group", commonHandler(batchDeleteNotificationGroup))
auth.GET("/server", listHandler(listServer))
auth.GET("/server", commonHandler(listServer))
auth.PATCH("/server/:id", commonHandler(updateServer))
auth.POST("/batch-delete/server", commonHandler(batchDeleteServer))
auth.POST("/force-update/server", commonHandler(forceUpdateServer))
@ -243,13 +243,13 @@ func listHandler[S ~[]E, E model.CommonInterface](handler handlerFunc[S]) func(*
return
}
c.JSON(http.StatusOK, filter(c, data))
c.JSON(http.StatusOK, model.CommonResponse[S]{Success: true, Data: filter(c, data)})
}
}
func filter[S ~[]E, E model.CommonInterface](ctx *gin.Context, s S) S {
return slices.DeleteFunc(s, func(e E) bool {
return e.HasPermission(ctx)
return !e.HasPermission(ctx)
})
}

View File

@ -168,6 +168,10 @@ func manualTriggerCron(c *gin.Context) (any, error) {
}
singleton.CronLock.RUnlock()
if !cr.HasPermission(c) {
return nil, singleton.Localizer.ErrorT("permission denied")
}
singleton.ManualTrigger(cr)
return nil, nil
}

View File

@ -201,7 +201,7 @@ func batchDeleteNotificationGroup(c *gin.Context) (any, error) {
}
var ng []model.NotificationGroup
if err := singleton.DB.Where("id in (?)", ng).Find(&ng).Error; err != nil {
if err := singleton.DB.Where("id in (?)", ngnr).Find(&ng).Error; err != nil {
return nil, err
}

View File

@ -178,6 +178,9 @@ func forceUpdateServer(c *gin.Context) (*model.ForceUpdateResponse, error) {
server := singleton.ServerList[sid]
singleton.ServerLock.RUnlock()
if server != nil && server.TaskStream != nil {
if !server.HasPermission(c) {
return nil, singleton.Localizer.ErrorT("permission denied")
}
if err := server.TaskStream.Send(&pb.Task{
Type: model.TaskTypeUpgrade,
}); err != nil {

View File

@ -126,6 +126,7 @@ func createUser(c *gin.Context) (uint64, error) {
return 0, err
}
singleton.OnUserUpdate(&u)
return u.ID, nil
}
@ -150,5 +151,6 @@ func batchDeleteUser(c *gin.Context) (any, error) {
return nil, singleton.Localizer.ErrorT("can't delete yourself")
}
singleton.OnUserDelete(ids)
return nil, singleton.DB.Where("id IN (?)", ids).Delete(&model.User{}).Error
}

View File

@ -20,7 +20,7 @@ type Common struct {
// Do not use soft deletion
// DeletedAt gorm.DeletedAt `gorm:"index" json:"deleted_at,omitempty"`
UserID uint64 `json:"user_id,omitempty"`
UserID uint64 `json:"-"`
}
func (c *Common) GetID() uint64 {

View File

@ -1,5 +1,10 @@
package model
import (
"github.com/nezhahq/nezha/pkg/utils"
"gorm.io/gorm"
)
const (
RoleAdmin uint8 = iota
RoleMember
@ -7,9 +12,20 @@ const (
type User struct {
Common
Username string `json:"username,omitempty" gorm:"uniqueIndex"`
Password string `json:"password,omitempty" gorm:"type:char(72)"`
Role uint8 `json:"role,omitempty"`
Username string `json:"username,omitempty" gorm:"uniqueIndex"`
Password string `json:"password,omitempty" gorm:"type:char(72)"`
Role uint8 `json:"role,omitempty"`
AgentSecret string `json:"agent_secret,omitempty" gorm:"type:char(32)"`
}
func (u *User) BeforeSave(tx *gorm.DB) error {
key, err := utils.GenerateRandomString(32)
if err != nil {
return err
}
u.AgentSecret = key
return nil
}
type Profile struct {

View File

@ -1,6 +0,0 @@
package model
type UserGroup struct {
Common
Name string `json:"name"`
}

View File

@ -1,7 +0,0 @@
package model
type UserGroupUser struct {
Common
UserGroupId uint64 `json:"user_group_id"`
UserId uint64 `json:"user_id"`
}

View File

@ -16,6 +16,7 @@ var Languages = map[string]string{
"zh_TW": "繁體中文",
"en_US": "English",
"es_ES": "Español",
"de_DE": "Deutsch",
}
type Localizer struct {

Binary file not shown.

View File

@ -0,0 +1,228 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-11-23 23:56+0800\n"
"PO-Revision-Date: 2024-12-17 04:52+0000\n"
"Last-Translator: UUBulb <uub@kuzu.uk>\n"
"Language-Team: German <https://hosted.weblate.org/projects/nezha/"
"nezha-dashboard/de/>\n"
"Language: de_DE\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 5.9\n"
#: cmd/dashboard/controller/alertrule.go:100
#, c-format
msgid "alert id %d does not exist"
msgstr "benachrichtigungs ID %d existiert nicht"
#: cmd/dashboard/controller/alertrule.go:155
msgid "duration need to be at least 3"
msgstr "dauer muss mindestens 3 sein"
#: cmd/dashboard/controller/alertrule.go:159
msgid "cycle_interval need to be at least 1"
msgstr "cycle_interval muss mindestens 1 sein"
#: cmd/dashboard/controller/alertrule.go:162
msgid "cycle_start is not set"
msgstr "cycle_start ist nicht eingestellt"
#: cmd/dashboard/controller/alertrule.go:165
msgid "cycle_start is a future value"
msgstr "cycle_start ist ein zukünftiger wert"
#: cmd/dashboard/controller/alertrule.go:170
msgid "need to configure at least a single rule"
msgstr "mindestens eine Regel muss konfiguriert sein"
#: cmd/dashboard/controller/controller.go:195
msgid "database error"
msgstr "datenbankfehler"
#: cmd/dashboard/controller/cron.go:63 cmd/dashboard/controller/cron.go:122
msgid "scheduled tasks cannot be triggered by alarms"
msgstr "geplante aufgaben können nicht durch Alarme ausgelöst werden"
#: cmd/dashboard/controller/cron.go:161
#, c-format
msgid "task id %d does not exist"
msgstr "task ID %d existiert nicht"
#: cmd/dashboard/controller/ddns.go:56 cmd/dashboard/controller/ddns.go:120
msgid "the retry count must be an integer between 1 and 10"
msgstr "der retry_count muss eine Zahl zwischen 1 und 10 sein"
#: cmd/dashboard/controller/ddns.go:79 cmd/dashboard/controller/ddns.go:148
msgid "error parsing %s: %v"
msgstr "fehler beim parsen von %s: %v"
#: cmd/dashboard/controller/ddns.go:125 cmd/dashboard/controller/nat.go:95
#, c-format
msgid "profile id %d does not exist"
msgstr "profil ID %d existiert nicht"
#: cmd/dashboard/controller/fm.go:45 cmd/dashboard/controller/terminal.go:43
msgid "server not found or not connected"
msgstr "server nicht gefunden oder nicht verbunden"
#: cmd/dashboard/controller/notification.go:67
#: cmd/dashboard/controller/notification.go:125
msgid "a test message"
msgstr "testnachricht"
#: cmd/dashboard/controller/notification.go:106
#, c-format
msgid "notification id %d does not exist"
msgstr "benachrichtigung ID %d existiert nicht"
#: cmd/dashboard/controller/notification_group.go:80
#: cmd/dashboard/controller/notification_group.go:142
msgid "have invalid notification id"
msgstr "haben ungültige Benachrichtigungs ID"
#: cmd/dashboard/controller/notification_group.go:131
#: cmd/dashboard/controller/server_group.go:130
#, c-format
msgid "group id %d does not exist"
msgstr "gruppen ID %d existiert nicht"
#: cmd/dashboard/controller/server.go:60
#, c-format
msgid "server id %d does not exist"
msgstr "server ID %d existiert nicht"
#: cmd/dashboard/controller/server_group.go:78
#: cmd/dashboard/controller/server_group.go:139
msgid "have invalid server id"
msgstr "haben ungültige Server ID"
#: cmd/dashboard/controller/service.go:79
#: cmd/dashboard/controller/service.go:155
msgid "server not found"
msgstr "server nicht gefunden"
#: cmd/dashboard/controller/service.go:86 cmd/dashboard/controller/user.go:23
msgid "unauthorized"
msgstr "nicht autorisiert"
#: cmd/dashboard/controller/service.go:247
#, c-format
msgid "service id %d does not exist"
msgstr "service ID %d existiert nicht"
#: cmd/dashboard/controller/user.go:66
msgid "password length must be greater than 6"
msgstr "passwort muss länger als 6 Zeichen sein"
#: cmd/dashboard/controller/user.go:69
msgid "username can't be empty"
msgstr "benutzername darf nicht leer sein"
#: service/rpc/io_stream.go:122
msgid "timeout: no connection established"
msgstr "timeout: Keine Verbindung hergestellt"
#: service/rpc/io_stream.go:125
msgid "timeout: user connection not established"
msgstr "timeout: Benutzerverbindung nicht etabliert"
#: service/rpc/io_stream.go:128
msgid "timeout: agent connection not established"
msgstr "timeout: Agent-Verbindung nicht etabliert"
#: service/rpc/nezha.go:58
msgid "Scheduled Task Executed Successfully"
msgstr "geplante Aufgabe erfolgreich ausgeführt"
#: service/rpc/nezha.go:62
msgid "Scheduled Task Executed Failed"
msgstr "geplante Aufgabe fehlgeschlagen"
#: service/rpc/nezha.go:217
msgid "IP Changed"
msgstr "IP geändert"
#: service/singleton/alertsentinel.go:159
msgid "Incident"
msgstr "Vorfall"
#: service/singleton/alertsentinel.go:169
msgid "Resolved"
msgstr "Gelöst"
#: service/singleton/crontask.go:53
msgid "Tasks failed to register: ["
msgstr "Aufgaben konnten nicht registriert werden: ["
#: service/singleton/crontask.go:60
msgid ""
"] These tasks will not execute properly. Fix them in the admin dashboard."
msgstr ""
"] Diese Aufgaben werden nicht korrekt ausgeführt. Reparieren Sie diese im "
"Admin-Dashboard."
#: service/singleton/crontask.go:146 service/singleton/crontask.go:171
#, c-format
msgid "[Task failed] %s: server %s is offline and cannot execute the task"
msgstr ""
"[Aufgabe fehlgeschlagen] %s: Server %s ist offline und kann die Aufgabe "
"nicht ausführen"
#: service/singleton/servicesentinel.go:439
#, c-format
msgid "[Latency] %s %2f > %2f, Reporter: %s"
msgstr "[Latency] %s %2f > %2f, Reporter: %s"
#: service/singleton/servicesentinel.go:446
#, c-format
msgid "[Latency] %s %2f < %2f, Reporter: %s"
msgstr "[Latency] %s %2f < %2f, Reporter: %s"
#: service/singleton/servicesentinel.go:472
#, c-format
msgid "[%s] %s Reporter: %s, Error: %s"
msgstr "[%s] %s Reporter: %s, Fehler: %s"
#: service/singleton/servicesentinel.go:515
#, c-format
msgid "[TLS] Fetch cert info failed, Reporter: %s, Error: %s"
msgstr "[TLS] Fetch cert info gescheitert, Reporter: %s, Fehler: %s"
#: service/singleton/servicesentinel.go:555
#, c-format
msgid "The TLS certificate will expire within seven days. Expiration time: %s"
msgstr "Das TLS-Zertifikat läuft innerhalb von sieben Tagen ab. Ablaufzeit: %s"
#: service/singleton/servicesentinel.go:568
#, c-format
msgid ""
"TLS certificate changed, old: issuer %s, expires at %s; new: issuer %s, "
"expires at %s"
msgstr ""
"TLS-Zertifikat geändert, alt: Emittent %s, läuft ab bei %s; neu: Emittent "
"%s, läuft ab bei %s"
#: service/singleton/servicesentinel.go:604
msgid "No Data"
msgstr "Keine Daten"
#: service/singleton/servicesentinel.go:606
msgid "Good"
msgstr "Gut"
#: service/singleton/servicesentinel.go:608
msgid "Low Availability"
msgstr "Niedere Verfügbarkeit"
#: service/singleton/servicesentinel.go:610
msgid "Down"
msgstr "Unten"

Binary file not shown.

View File

@ -0,0 +1,232 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-11-23 23:56+0800\n"
"PO-Revision-Date: 2024-12-17 04:52+0000\n"
"Last-Translator: UUBulb <uub@kuzu.uk>\n"
"Language-Team: Spanish <https://hosted.weblate.org/projects/nezha/"
"nezha-dashboard/es/>\n"
"Language: es_ES\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 5.9\n"
#: cmd/dashboard/controller/alertrule.go:100
#, c-format
msgid "alert id %d does not exist"
msgstr "el ID de alerta %d no existe"
#: cmd/dashboard/controller/alertrule.go:155
msgid "duration need to be at least 3"
msgstr "la duración debe ser al menos de 3"
#: cmd/dashboard/controller/alertrule.go:159
msgid "cycle_interval need to be at least 1"
msgstr "cycle_interval debe ser al menos 1"
#: cmd/dashboard/controller/alertrule.go:162
msgid "cycle_start is not set"
msgstr "no se ha configurado el cycle_start"
#: cmd/dashboard/controller/alertrule.go:165
msgid "cycle_start is a future value"
msgstr "cycle_start es un valor futuro"
#: cmd/dashboard/controller/alertrule.go:170
msgid "need to configure at least a single rule"
msgstr "es necesario configurar al menos una regla"
#: cmd/dashboard/controller/controller.go:195
msgid "database error"
msgstr "error de base de datos"
#: cmd/dashboard/controller/cron.go:63 cmd/dashboard/controller/cron.go:122
msgid "scheduled tasks cannot be triggered by alarms"
msgstr "las tareas programadas no pueden ser activadas por alarmas"
#: cmd/dashboard/controller/cron.go:161
#, c-format
msgid "task id %d does not exist"
msgstr "el ID de la tarea %d no existe"
#: cmd/dashboard/controller/ddns.go:56 cmd/dashboard/controller/ddns.go:120
msgid "the retry count must be an integer between 1 and 10"
msgstr "el número de reintentos debe ser un número entero entre 1 y 10"
#: cmd/dashboard/controller/ddns.go:79 cmd/dashboard/controller/ddns.go:148
msgid "error parsing %s: %v"
msgstr "error al analizar %s: %v"
#: cmd/dashboard/controller/ddns.go:125 cmd/dashboard/controller/nat.go:95
#, c-format
msgid "profile id %d does not exist"
msgstr "el ID de perfil %d no existe"
#: cmd/dashboard/controller/fm.go:45 cmd/dashboard/controller/terminal.go:43
msgid "server not found or not connected"
msgstr "servidor no encontrado o no conectado"
#: cmd/dashboard/controller/notification.go:67
#: cmd/dashboard/controller/notification.go:125
msgid "a test message"
msgstr "un mensaje de prueba"
#: cmd/dashboard/controller/notification.go:106
#, c-format
msgid "notification id %d does not exist"
msgstr "el ID de notificación %d no existe"
#: cmd/dashboard/controller/notification_group.go:80
#: cmd/dashboard/controller/notification_group.go:142
msgid "have invalid notification id"
msgstr "hay un ID de notificación no válido"
#: cmd/dashboard/controller/notification_group.go:131
#: cmd/dashboard/controller/server_group.go:130
#, c-format
msgid "group id %d does not exist"
msgstr "el ID de grupo %d no existe"
#: cmd/dashboard/controller/server.go:60
#, c-format
msgid "server id %d does not exist"
msgstr "el ID de servidor %d no existe"
#: cmd/dashboard/controller/server_group.go:78
#: cmd/dashboard/controller/server_group.go:139
msgid "have invalid server id"
msgstr "hay un ID de servidor no válido"
#: cmd/dashboard/controller/service.go:79
#: cmd/dashboard/controller/service.go:155
msgid "server not found"
msgstr "servidor no encontrado"
#: cmd/dashboard/controller/service.go:86 cmd/dashboard/controller/user.go:23
msgid "unauthorized"
msgstr "no autorizado"
#: cmd/dashboard/controller/service.go:247
#, c-format
msgid "service id %d does not exist"
msgstr "el ID de servicio %d no existe"
#: cmd/dashboard/controller/user.go:66
msgid "password length must be greater than 6"
msgstr "la longitud de la contraseña debe ser mayor a 6"
#: cmd/dashboard/controller/user.go:69
msgid "username can't be empty"
msgstr "el nombre de usuario no puede estar vacío"
#: service/rpc/io_stream.go:122
msgid "timeout: no connection established"
msgstr "tiempo de espera agotado: no se pudo establecer conexión"
#: service/rpc/io_stream.go:125
msgid "timeout: user connection not established"
msgstr "tiempo de espera agotado: no se pudo establecer conexión con el usuario"
#: service/rpc/io_stream.go:128
msgid "timeout: agent connection not established"
msgstr "tiempo de espera agotado: no se pudo establecer conexión con agent"
#: service/rpc/nezha.go:58
msgid "Scheduled Task Executed Successfully"
msgstr "tarea programada ejecutada con éxito"
#: service/rpc/nezha.go:62
msgid "Scheduled Task Executed Failed"
msgstr "falló la ejecución de la tarea programada"
#: service/rpc/nezha.go:217
msgid "IP Changed"
msgstr "IP cambiada"
#: service/singleton/alertsentinel.go:159
msgid "Incident"
msgstr "Incidente"
#: service/singleton/alertsentinel.go:169
msgid "Resolved"
msgstr "Resuelto"
#: service/singleton/crontask.go:53
msgid "Tasks failed to register: ["
msgstr "Las tareas no se pudieron registrar: ["
#: service/singleton/crontask.go:60
msgid ""
"] These tasks will not execute properly. Fix them in the admin dashboard."
msgstr ""
"] Estas tareas no se ejecutarán correctamente. Corríjalas en el dashboard de "
"administración."
#: service/singleton/crontask.go:146 service/singleton/crontask.go:171
#, c-format
msgid "[Task failed] %s: server %s is offline and cannot execute the task"
msgstr ""
"[Tarea fallida] %s: el servidor %s está fuera de línea y no puede ejecutar "
"la tarea"
#: service/singleton/servicesentinel.go:439
#, c-format
msgid "[Latency] %s %2f > %2f, Reporter: %s"
msgstr "[Latencia] %s %2f > %2f, Reportado por: %s"
#: service/singleton/servicesentinel.go:446
#, c-format
msgid "[Latency] %s %2f < %2f, Reporter: %s"
msgstr "[Latencia] %s %2f < %2f, Reportado por: %s"
#: service/singleton/servicesentinel.go:472
#, c-format
msgid "[%s] %s Reporter: %s, Error: %s"
msgstr "[%s] %s Reportado por: %s, Error: %s"
#: service/singleton/servicesentinel.go:515
#, c-format
msgid "[TLS] Fetch cert info failed, Reporter: %s, Error: %s"
msgstr ""
"[TLS] Error al obtener información del certificado, Reportado por: %s, Error:"
" %s"
#: service/singleton/servicesentinel.go:555
#, c-format
msgid "The TLS certificate will expire within seven days. Expiration time: %s"
msgstr ""
"El certificado TLS expirará en los próximos siete días. Fecha de expiración: "
"%s"
#: service/singleton/servicesentinel.go:568
#, c-format
msgid ""
"TLS certificate changed, old: issuer %s, expires at %s; new: issuer %s, "
"expires at %s"
msgstr ""
"El certificado TLS ha cambiado. Antiguo: emisor %s, expira en %s; Nuevo: "
"emisor %s, expira en %s"
#: service/singleton/servicesentinel.go:604
msgid "No Data"
msgstr "Sin datos"
#: service/singleton/servicesentinel.go:606
msgid "Good"
msgstr "Bueno"
#: service/singleton/servicesentinel.go:608
msgid "Low Availability"
msgstr "Baja disponibilidad"
#: service/singleton/servicesentinel.go:610
msgid "Down"
msgstr "Fallo"

View File

@ -2,6 +2,7 @@ package rpc
import (
"context"
"crypto/subtle"
"strings"
petname "github.com/dustinkirkland/golang-petname"
@ -36,10 +37,14 @@ func (a *authHandler) Check(ctx context.Context) (uint64, error) {
ip, _ := ctx.Value(model.CtxKeyRealIP{}).(string)
if clientSecret != singleton.Conf.AgentSecretKey {
singleton.UserLock.RLock()
userId, ok := singleton.AgentSecretToUserId[clientSecret]
if !ok && subtle.ConstantTimeCompare([]byte(clientSecret), []byte(singleton.Conf.AgentSecretKey)) != 1 {
singleton.UserLock.RUnlock()
model.BlockIP(singleton.DB, ip, model.WAFBlockReasonTypeAgentAuthFail)
return 0, status.Error(codes.Unauthenticated, "客户端认证失败")
}
singleton.UserLock.RUnlock()
model.ClearIP(singleton.DB, ip)
@ -53,21 +58,26 @@ func (a *authHandler) Check(ctx context.Context) (uint64, error) {
}
singleton.ServerLock.RLock()
defer singleton.ServerLock.RUnlock()
clientID, hasID := singleton.ServerUUIDToID[clientUUID]
singleton.ServerLock.RUnlock()
if !hasID {
s := model.Server{UUID: clientUUID, Name: petname.Generate(2, "-")}
s := model.Server{UUID: clientUUID, Name: petname.Generate(2, "-"), Common: model.Common{
UserID: userId,
}}
if err := singleton.DB.Create(&s).Error; err != nil {
return 0, status.Error(codes.Unauthenticated, err.Error())
}
s.Host = &model.Host{}
s.State = &model.HostState{}
s.GeoIP = &model.GeoIP{}
// generate a random silly server name
singleton.ServerLock.Lock()
singleton.ServerList[s.ID] = &s
singleton.ServerUUIDToID[clientUUID] = s.ID
singleton.ServerLock.Unlock()
singleton.ReSortServer()
clientID = s.ID
}

View File

@ -24,12 +24,10 @@ var (
func initDDNS() {
DB.Find(&DDNSList)
DDNSCacheLock.Lock()
DDNSCache = make(map[uint64]*model.DDNSProfile)
for i := 0; i < len(DDNSList); i++ {
DDNSCache[DDNSList[i].ID] = DDNSList[i]
}
DDNSCacheLock.Unlock()
OnNameserverUpdate()
}

View File

@ -2,14 +2,14 @@
name: "OfficialAdmin"
repository: "https://github.com/nezhahq/admin-frontend"
author: "nezhahq"
version: "v1.2.0"
version: "v1.2.2"
isadmin: true
isofficial: true
- path: "user-dist"
name: "Official"
repository: "https://github.com/hamster1963/nezha-dash-v1"
author: "hamster1963"
version: "v1.3.5"
version: "v1.4.5"
isofficial: true
- path: "nazhua-dist"
name: "Nazhua"

View File

@ -19,8 +19,6 @@ var (
func initNAT() {
DB.Find(&NATList)
NATCacheRwLock.Lock()
defer NATCacheRwLock.Unlock()
NATCache = make(map[string]*model.NAT)
for i := 0; i < len(NATList); i++ {
NATCache[NATList[i].Domain] = NATList[i]

View File

@ -30,7 +30,7 @@ var (
)
// InitNotification 初始化 GroupID <-> ID <-> Notification 的映射
func InitNotification() {
func initNotification() {
NotificationList = make(map[uint64]map[uint64]*model.Notification)
NotificationIDToGroups = make(map[uint64]map[uint64]struct{})
NotificationGroup = make(map[uint64]string)
@ -38,9 +38,7 @@ func InitNotification() {
// loadNotifications 从 DB 初始化通知方式相关参数
func loadNotifications() {
InitNotification()
NotificationsLock.Lock()
initNotification()
groupNotifications := make(map[uint64][]uint64)
var ngn []model.NotificationGroupNotification
if err := DB.Find(&ngn).Error; err != nil {
@ -74,8 +72,6 @@ func loadNotifications() {
}
}
}
NotificationsLock.Unlock()
}
func UpdateNotificationList() {

View File

@ -192,13 +192,6 @@ func (ss *ServiceSentinel) loadServiceHistory() {
panic(err)
}
ss.serviceResponseDataStoreLock.Lock()
defer ss.serviceResponseDataStoreLock.Unlock()
ss.monthlyStatusLock.Lock()
defer ss.monthlyStatusLock.Unlock()
ss.ServicesLock.Lock()
defer ss.ServicesLock.Unlock()
for i := 0; i < len(services); i++ {
task := *services[i]
// 通过cron定时将服务监控任务传递给任务调度管道

View File

@ -40,6 +40,7 @@ func InitTimezoneAndCache() {
// LoadSingleton 加载子服务并执行
func LoadSingleton() {
initUser() // 加载用户ID绑定表
initI18n() // 加载本地化服务
loadNotifications() // 加载通知服务
loadServers() // 加载服务器列表
@ -79,8 +80,8 @@ func InitDBFromPath(path string) {
}
err = DB.AutoMigrate(model.Server{}, model.User{}, model.ServerGroup{}, model.NotificationGroup{},
model.Notification{}, model.AlertRule{}, model.Service{}, model.NotificationGroupNotification{},
model.ServiceHistory{}, model.Cron{}, model.Transfer{}, model.ServerGroupServer{}, model.UserGroup{},
model.UserGroupUser{}, model.NAT{}, model.DDNSProfile{}, model.NotificationGroupNotification{},
model.ServiceHistory{}, model.Cron{}, model.Transfer{}, model.ServerGroupServer{},
model.NAT{}, model.DDNSProfile{}, model.NotificationGroupNotification{},
model.WAF{})
if err != nil {
panic(err)

54
service/singleton/user.go Normal file
View File

@ -0,0 +1,54 @@
package singleton
import (
"sync"
"github.com/nezhahq/nezha/model"
)
var (
UserIdToAgentSecret map[uint64]string
AgentSecretToUserId map[string]uint64
UserLock sync.RWMutex
)
func initUser() {
UserIdToAgentSecret = make(map[uint64]string)
AgentSecretToUserId = make(map[string]uint64)
var users []model.User
DB.Find(&users)
for _, u := range users {
UserIdToAgentSecret[u.ID] = u.AgentSecret
AgentSecretToUserId[u.AgentSecret] = u.ID
}
}
func OnUserUpdate(u *model.User) {
UserLock.Lock()
defer UserLock.Unlock()
if u == nil {
return
}
UserIdToAgentSecret[u.ID] = u.AgentSecret
AgentSecretToUserId[u.AgentSecret] = u.ID
}
func OnUserDelete(id []uint64) {
UserLock.Lock()
defer UserLock.Unlock()
if len(id) < 1 {
return
}
for _, uid := range id {
secret := UserIdToAgentSecret[uid]
delete(AgentSecretToUserId, secret)
delete(UserIdToAgentSecret, uid)
}
}