mirror of
https://github.com/Dvorinka/Dash.git
synced 2026-10-28 15:51:27 +00:00
feat(api): phase 5 — alerts, agent depth, subdomains, auth, boards
P5b: notifier transport registry (webhook/slack/discord/telegram/ gotify/ntfy/smtp via net/smtp), per-monitor+domain alert rules (failsBeforeDown, latencyWarn, certDays, mute). P5c: per-container cpu/mem, optional SMART/ZFS/GPU exec-or-skip collectors, crt.sh subdomain discovery with daily sweep. P5d: opt-in auth (bcrypt, sqlite sessions, 7d rotating cookie, login rate limit, public-route bypass) and multiple boards (boards table, sections.board_id, /api/boards CRUD, slug routing). Authored By: TDvorak <[email protected]> Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
8973be3b41
commit
f3143af351
33 files changed
+2772
-126
No files matched your search
@@ -10,8 +10,16 @@ import (
|
||||
|
||||
// board loads the full tree: sections ordered, each with items and urls.
|
||||
// Three flat queries assembled in memory — the whole board is ~100 rows.
|
||||
func (s *Server) board() ([]Section, error) {
|
||||
secRows, err := s.db.Query(`SELECT id, name, position, collapsed FROM sections ORDER BY position`)
|
||||
// boardID filters to one board; "" returns every section (export, legacy).
|
||||
// COALESCE maps pre-boards rows onto the default 'b_home' board.
|
||||
func (s *Server) board(boardID string) ([]Section, error) {
|
||||
q := `SELECT id, name, position, collapsed, COALESCE(board_id,'b_home') FROM sections`
|
||||
var args []any
|
||||
if boardID != "" {
|
||||
q += ` WHERE board_id = ?`
|
||||
args = append(args, boardID)
|
||||
}
|
||||
secRows, err := s.db.Query(q+` ORDER BY position`, args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -21,7 +29,7 @@ func (s *Server) board() ([]Section, error) {
|
||||
byID := map[string]*Section{}
|
||||
for secRows.Next() {
|
||||
var sec Section
|
||||
if err := secRows.Scan(&sec.ID, &sec.Name, &sec.Position, &sec.Collapsed); err != nil {
|
||||
if err := secRows.Scan(&sec.ID, &sec.Name, &sec.Position, &sec.Collapsed, &sec.BoardID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sec.Items = []Item{}
|
||||
@@ -89,7 +97,7 @@ func (s *Server) board() ([]Section, error) {
|
||||
}
|
||||
|
||||
func (s *Server) listSections(c *gin.Context) {
|
||||
sections, err := s.board()
|
||||
sections, err := s.board(c.Query("board"))
|
||||
if err != nil {
|
||||
fail(c, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
@@ -99,19 +107,27 @@ func (s *Server) listSections(c *gin.Context) {
|
||||
|
||||
func (s *Server) createSection(c *gin.Context) {
|
||||
var in struct {
|
||||
Name string `json:"name"`
|
||||
Name string `json:"name"`
|
||||
BoardID string `json:"boardId"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&in); err != nil || in.Name == "" {
|
||||
fail(c, http.StatusBadRequest, "name required")
|
||||
return
|
||||
}
|
||||
boardID := in.BoardID
|
||||
if boardID == "" {
|
||||
boardID = "b_home"
|
||||
}
|
||||
var maxPos sql.NullFloat64
|
||||
if err := s.db.QueryRow(`SELECT MAX(position) FROM sections`).Scan(&maxPos); err != nil {
|
||||
if err := s.db.QueryRow(`SELECT MAX(position) FROM sections WHERE board_id = ?`, boardID).Scan(&maxPos); err != nil {
|
||||
fail(c, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
sec := Section{ID: newID("s"), Name: in.Name, Position: midpoint(ptrOr(maxPos), nil), Items: []Item{}}
|
||||
if _, err := s.db.Exec(`INSERT INTO sections (id, name, position) VALUES (?, ?, ?)`, sec.ID, sec.Name, sec.Position); err != nil {
|
||||
sec := Section{ID: newID("s"), Name: in.Name, Position: midpoint(ptrOr(maxPos), nil), BoardID: boardID, Items: []Item{}}
|
||||
// The subquery yields NULL for an unknown board id instead of an FK error;
|
||||
// board() maps NULL back onto the default board.
|
||||
if _, err := s.db.Exec(`INSERT INTO sections (id, name, position, board_id)
|
||||
VALUES (?, ?, ?, (SELECT id FROM boards WHERE id = ?))`, sec.ID, sec.Name, sec.Position, boardID); err != nil {
|
||||
fail(c, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
@@ -140,8 +156,8 @@ func (s *Server) updateSection(c *gin.Context) {
|
||||
}
|
||||
}
|
||||
var sec Section
|
||||
err := s.db.QueryRow(`SELECT id, name, position, collapsed FROM sections WHERE id = ?`, c.Param("id")).
|
||||
Scan(&sec.ID, &sec.Name, &sec.Position, &sec.Collapsed)
|
||||
err := s.db.QueryRow(`SELECT id, name, position, collapsed, COALESCE(board_id,'b_home') FROM sections WHERE id = ?`, c.Param("id")).
|
||||
Scan(&sec.ID, &sec.Name, &sec.Position, &sec.Collapsed, &sec.BoardID)
|
||||
if notFound(err) {
|
||||
fail(c, http.StatusNotFound, "section not found")
|
||||
return
|
||||
@@ -192,8 +208,8 @@ func (s *Server) reorderSection(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
var sec Section
|
||||
if err := s.db.QueryRow(`SELECT id, name, position, collapsed FROM sections WHERE id = ?`, in.ID).
|
||||
Scan(&sec.ID, &sec.Name, &sec.Position, &sec.Collapsed); err != nil {
|
||||
if err := s.db.QueryRow(`SELECT id, name, position, collapsed, COALESCE(board_id,'b_home') FROM sections WHERE id = ?`, in.ID).
|
||||
Scan(&sec.ID, &sec.Name, &sec.Position, &sec.Collapsed, &sec.BoardID); err != nil {
|
||||
fail(c, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user