First release of open core
This commit is contained in:
345
ui/ui.go
Normal file
345
ui/ui.go
Normal file
@@ -0,0 +1,345 @@
|
||||
package ui
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"embed"
|
||||
"html/template"
|
||||
"io/fs"
|
||||
"log"
|
||||
"math"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"epigas.gitea.cloud/RiskRancher/core/pkg/auth"
|
||||
"epigas.gitea.cloud/RiskRancher/core/pkg/domain"
|
||||
"epigas.gitea.cloud/RiskRancher/core/pkg/report"
|
||||
)
|
||||
|
||||
//go:embed templates/* templates/components/* static/*
|
||||
var CoreUIFS embed.FS
|
||||
|
||||
var (
|
||||
AppVersion = "dev"
|
||||
AppCommit = "none"
|
||||
)
|
||||
|
||||
var CoreTemplates *template.Template
|
||||
var Pages map[string]*template.Template
|
||||
|
||||
// SetVersionInfo is called by main.go on startup to inject ldflags
|
||||
func SetVersionInfo(version, commit string) {
|
||||
AppVersion = version
|
||||
AppCommit = commit
|
||||
}
|
||||
|
||||
func init() {
|
||||
funcMap := template.FuncMap{"lower": strings.ToLower}
|
||||
Pages = make(map[string]*template.Template)
|
||||
|
||||
var err error
|
||||
|
||||
CoreTemplates, err = template.New("").Funcs(funcMap).ParseFS(CoreUIFS, "templates/*.gohtml", "templates/components/*.gohtml")
|
||||
if err != nil && !strings.Contains(err.Error(), "pattern matches no files") {
|
||||
log.Printf("Warning: Failed to parse master core templates: %v", err)
|
||||
}
|
||||
|
||||
dashTmpl := template.New("").Funcs(funcMap)
|
||||
dashTmpl, err = dashTmpl.ParseFS(CoreUIFS, "templates/base.gohtml", "templates/dashboard.gohtml", "templates/components/*.gohtml")
|
||||
if err != nil {
|
||||
log.Fatalf("FATAL: Failed to parse dashboard shell. Err: %v", err)
|
||||
}
|
||||
Pages["dashboard"] = dashTmpl
|
||||
|
||||
adminTmpl := template.New("").Funcs(funcMap)
|
||||
adminTmpl, err = adminTmpl.ParseFS(CoreUIFS, "templates/base.gohtml", "templates/admin.gohtml", "templates/components/*.gohtml")
|
||||
if err != nil {
|
||||
log.Fatalf("FATAL: Failed to parse admin shell. Err: %v", err)
|
||||
}
|
||||
Pages["admin"] = adminTmpl
|
||||
|
||||
Pages["login"], err = template.New("").Funcs(funcMap).ParseFS(CoreUIFS, "templates/login.gohtml")
|
||||
if err != nil {
|
||||
log.Fatalf("FATAL: Failed to parse login. Err: %v", err)
|
||||
}
|
||||
|
||||
Pages["register"], err = template.New("").Funcs(funcMap).ParseFS(CoreUIFS, "templates/register.gohtml")
|
||||
if err != nil {
|
||||
log.Fatalf("FATAL: Failed to parse register. Err: %v", err)
|
||||
}
|
||||
|
||||
Pages["assets"], err = template.New("").Funcs(funcMap).ParseFS(CoreUIFS, "templates/base.gohtml", "templates/assets.gohtml", "templates/components/*.gohtml")
|
||||
if err != nil {
|
||||
log.Fatalf("FATAL: Failed to parse assets. Err: %v", err)
|
||||
}
|
||||
|
||||
ingestTmpl := template.New("").Funcs(funcMap)
|
||||
ingestTmpl, err = ingestTmpl.ParseFS(CoreUIFS, "templates/base.gohtml", "templates/ingest.gohtml", "templates/components/*.gohtml")
|
||||
if err != nil {
|
||||
log.Fatalf("FATAL: Failed to parse ingest shell. Err: %v", err)
|
||||
}
|
||||
Pages["ingest"] = ingestTmpl
|
||||
|
||||
adapterTmpl := template.New("").Funcs(funcMap)
|
||||
adapterTmpl, err = adapterTmpl.ParseFS(CoreUIFS, "templates/base.gohtml", "templates/adapter_builder.gohtml", "templates/components/*.gohtml")
|
||||
if err != nil {
|
||||
log.Fatalf("FATAL: Failed to parse adapter builder shell. Err: %v", err)
|
||||
}
|
||||
Pages["adapter_builder"] = adapterTmpl
|
||||
|
||||
uploadTmpl := template.New("").Funcs(funcMap)
|
||||
uploadTmpl, err = uploadTmpl.ParseFS(CoreUIFS, "templates/base.gohtml", "templates/report_upload.gohtml", "templates/components/*.gohtml")
|
||||
if err != nil {
|
||||
log.Fatalf("FATAL: Failed to parse report upload template. Err: %v", err)
|
||||
}
|
||||
Pages["report_upload"] = uploadTmpl
|
||||
|
||||
parserTmpl := template.New("").Funcs(funcMap)
|
||||
parserTmpl, err = parserTmpl.ParseFS(CoreUIFS, "templates/base.gohtml", "templates/report_parser.gohtml", "templates/components/*.gohtml")
|
||||
if err != nil {
|
||||
log.Fatalf("FATAL: Failed to parse report parser template. Err: %v", err)
|
||||
}
|
||||
Pages["report_parser"] = parserTmpl
|
||||
}
|
||||
|
||||
func StaticHandler() http.Handler {
|
||||
staticFS, err := fs.Sub(CoreUIFS, "static")
|
||||
if err != nil {
|
||||
log.Fatal("Failed to load embedded static files:", err)
|
||||
}
|
||||
return http.StripPrefix("/static/", http.FileServer(http.FS(staticFS)))
|
||||
}
|
||||
|
||||
type PageData struct {
|
||||
Tickets any
|
||||
CurrentTab string
|
||||
CurrentFilter string
|
||||
CurrentAsset string
|
||||
ReturnedCount int
|
||||
CountCritical int
|
||||
CountOverdue int
|
||||
CountMine int
|
||||
CurrentPage int
|
||||
TotalPages int
|
||||
NextPage int
|
||||
PrevPage int
|
||||
CountVerification int
|
||||
HasNext bool
|
||||
HasPrev bool
|
||||
Version string
|
||||
Commit string
|
||||
}
|
||||
|
||||
func HandleDashboard(store domain.Store) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
userIDVal := r.Context().Value(auth.UserIDKey)
|
||||
if userIDVal == nil {
|
||||
http.Redirect(w, r, "/login", http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
|
||||
userID := userIDVal.(int)
|
||||
user, err := store.GetUserByID(r.Context(), userID)
|
||||
if err != nil {
|
||||
http.Redirect(w, r, "/login", http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
|
||||
if user.GlobalRole == "Sheriff" {
|
||||
http.Redirect(w, r, "/admin", http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
|
||||
currentUserEmail := user.Email
|
||||
currentUserRole := user.GlobalRole
|
||||
|
||||
tab := r.URL.Query().Get("tab")
|
||||
if tab == "" {
|
||||
tab = "holding_pen"
|
||||
}
|
||||
|
||||
statusFilter := tab
|
||||
if tab == "holding_pen" {
|
||||
statusFilter = "Waiting to be Triaged"
|
||||
} else if tab == "chute" {
|
||||
statusFilter = "Assigned Out"
|
||||
} else if tab == "verification" {
|
||||
statusFilter = "Pending Verification"
|
||||
}
|
||||
|
||||
filter := r.URL.Query().Get("filter")
|
||||
assetFilter := r.URL.Query().Get("asset")
|
||||
|
||||
pageStr := r.URL.Query().Get("page")
|
||||
page, _ := strconv.Atoi(pageStr)
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
limit := 50
|
||||
offset := (page - 1) * limit
|
||||
|
||||
tickets, totalRecords, metrics, err := store.GetDashboardTickets(
|
||||
r.Context(), statusFilter, filter, assetFilter, currentUserEmail, currentUserRole, limit, offset,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
http.Error(w, "Database query error: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
totalPages := int(math.Ceil(float64(totalRecords) / float64(limit)))
|
||||
if totalPages == 0 {
|
||||
totalPages = 1
|
||||
}
|
||||
|
||||
data := PageData{
|
||||
Tickets: tickets,
|
||||
CurrentTab: tab,
|
||||
CurrentFilter: filter,
|
||||
CurrentAsset: assetFilter,
|
||||
ReturnedCount: metrics["returned"],
|
||||
CountCritical: metrics["critical"],
|
||||
CountOverdue: metrics["overdue"],
|
||||
CountMine: metrics["mine"],
|
||||
CountVerification: metrics["verification"],
|
||||
CurrentPage: page,
|
||||
TotalPages: totalPages,
|
||||
NextPage: page + 1,
|
||||
PrevPage: page - 1,
|
||||
HasNext: page < totalPages,
|
||||
HasPrev: page > 1,
|
||||
Version: AppVersion,
|
||||
Commit: AppCommit,
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
if err := Pages["dashboard"].ExecuteTemplate(&buf, "base", data); err != nil {
|
||||
http.Error(w, "Template rendering error: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
buf.WriteTo(w)
|
||||
}
|
||||
}
|
||||
|
||||
func HandleLoginUI() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
if err := Pages["login"].ExecuteTemplate(w, "login", nil); err != nil {
|
||||
http.Error(w, "Template render error: "+err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func HandleRegisterUI() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
if err := Pages["register"].ExecuteTemplate(w, "register", nil); err != nil {
|
||||
http.Error(w, "Template render error: "+err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func HandleAdminDashboard(store domain.Store) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
users, _ := store.GetAllUsers(r.Context())
|
||||
config, _ := store.GetAppConfig(r.Context())
|
||||
slas, _ := store.GetSLAPolicies(r.Context())
|
||||
adapters, _ := store.GetAdapters(r.Context())
|
||||
|
||||
analytics, _ := store.GetSheriffAnalytics(r.Context())
|
||||
activityFeed, _ := store.GetGlobalActivityFeed(r.Context(), 15)
|
||||
syncLogs, _ := store.GetRecentSyncLogs(r.Context(), 10)
|
||||
|
||||
data := map[string]any{
|
||||
"Users": users,
|
||||
"Config": config,
|
||||
"SLAs": slas,
|
||||
"Adapters": adapters,
|
||||
"Analytics": analytics,
|
||||
"Feed": activityFeed,
|
||||
"SyncLogs": syncLogs,
|
||||
"Version": AppVersion,
|
||||
"Commit": AppCommit,
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
if err := Pages["admin"].ExecuteTemplate(&buf, "base", data); err != nil {
|
||||
http.Error(w, "Template render error: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
buf.WriteTo(w)
|
||||
}
|
||||
}
|
||||
|
||||
func HandleIngestUI(store domain.Store) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
|
||||
adapters, _ := store.GetAdapters(r.Context())
|
||||
data := map[string]any{
|
||||
"Adapters": adapters,
|
||||
"Version": AppVersion,
|
||||
"Commit": AppCommit,
|
||||
}
|
||||
|
||||
if err := Pages["ingest"].ExecuteTemplate(w, "base", data); err != nil {
|
||||
http.Error(w, "Template render error: "+err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func HandleAdapterBuilderUI(store domain.Store) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
data := map[string]any{
|
||||
"Filename": r.URL.Query().Get("filename"),
|
||||
"Version": AppVersion,
|
||||
"Commit": AppCommit,
|
||||
}
|
||||
if err := Pages["adapter_builder"].ExecuteTemplate(w, "base", data); err != nil {
|
||||
http.Error(w, "Template render error: "+err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func HandleParserUI(store domain.Store) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
reportID := r.PathValue("id")
|
||||
|
||||
filePath := filepath.Join(report.UploadDir, reportID)
|
||||
recorder := httptest.NewRecorder()
|
||||
report.ServeDOCXAsHTML(recorder, filePath)
|
||||
safeHTML := template.HTML(recorder.Body.String())
|
||||
|
||||
data := map[string]any{
|
||||
"ReportID": reportID,
|
||||
"RenderedHTML": safeHTML,
|
||||
"Version": AppVersion,
|
||||
"Commit": AppCommit,
|
||||
}
|
||||
|
||||
if err := Pages["report_parser"].ExecuteTemplate(w, "base", data); err != nil {
|
||||
http.Error(w, "Template render error: "+err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func HandlePentestUploadUI(store domain.Store) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
data := map[string]any{
|
||||
"Version": AppVersion,
|
||||
"Commit": AppCommit,
|
||||
}
|
||||
if err := Pages["report_upload"].ExecuteTemplate(w, "base", data); err != nil {
|
||||
http.Error(w, "Template render error: "+err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user