Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7f22302859 | |||
| dc0ae8eb96 | |||
| b07a114227 | |||
| 88f77edadd | |||
| decad4cc82 | |||
| fd0ff13991 | |||
| 5f53ffec4f | |||
| da4699fe08 |
@@ -21,6 +21,16 @@ jobs:
|
||||
with:
|
||||
go-version: '1.26.x'
|
||||
|
||||
- name: Cache Go Modules and Build Cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cache/go-build
|
||||
~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-
|
||||
|
||||
- name: Run Tests
|
||||
run: go test ./... -v
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@ vendor/
|
||||
# RiskRancher Compiled Binaries
|
||||
# =========================
|
||||
# Ignore the local builds so you don't accidentally push a 20MB executable
|
||||
rr
|
||||
rr.exe
|
||||
|
||||
# =========================
|
||||
|
||||
@@ -13,7 +13,7 @@ Just drop the binary on a server and start triaging.
|
||||
|
||||
### Option A: Download the Binary
|
||||
|
||||
1. Go to the [Releases](https://epigas.gitea.cloud/RiskRancher/core/releases) tab and download the compiled executable for your OS (Windows/macOS/Linux).
|
||||
1. Go to the [Releases](https://code.riskrancher.com/RiskRancher/core/releases) tab and download the compiled executable for your OS (Windows/macOS/Linux).
|
||||
2. Place the binary in a dedicated directory and execute it.
|
||||
3. Visit `http://localhost:8080` in your browser.
|
||||
|
||||
@@ -22,8 +22,12 @@ Just drop the binary on a server and start triaging.
|
||||
Ensure you have **Go 1.26+** installed (*CGO is required for the native `mattn/go-sqlite3` driver*).
|
||||
|
||||
```bash
|
||||
git clone https://epigas.gitea.cloud/RiskRancher/core
|
||||
git clone https://code.riskrancher.com/RiskRancher/core
|
||||
cd core
|
||||
go build -o rr ./cmd/rr/main.go
|
||||
./rr
|
||||
```
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
Apache License 2.0
|
||||
@@ -0,0 +1,32 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"code.riskrancher.com/RiskRancher/core/pkg/datastore"
|
||||
"code.riskrancher.com/RiskRancher/core/pkg/server"
|
||||
"code.riskrancher.com/RiskRancher/core/ui"
|
||||
)
|
||||
|
||||
var (
|
||||
BuildVersion = "dev"
|
||||
BuildCommit = "none"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ui.SetVersionInfo(BuildVersion, BuildCommit)
|
||||
|
||||
db := datastore.InitDB("./data/RiskRancher.db")
|
||||
|
||||
defer db.Close()
|
||||
|
||||
store := datastore.NewSQLiteStore(db)
|
||||
|
||||
app := server.NewApp(store)
|
||||
|
||||
server.RegisterRoutes(app)
|
||||
|
||||
log.Println("🤠 RiskRancher Core Server running on http://localhost:8080")
|
||||
log.Fatal(http.ListenAndServe(":8080", app.Router))
|
||||
}
|
||||
@@ -10,8 +10,8 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"epigas.gitea.cloud/RiskRancher/core/pkg/auth"
|
||||
"epigas.gitea.cloud/RiskRancher/core/pkg/datastore"
|
||||
"code.riskrancher.com/RiskRancher/core/pkg/auth"
|
||||
"code.riskrancher.com/RiskRancher/core/pkg/datastore"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
@@ -1,8 +1,20 @@
|
||||
module epigas.gitea.cloud/RiskRancher/core
|
||||
module code.riskrancher.com/RiskRancher/core
|
||||
|
||||
go 1.26.0
|
||||
|
||||
require (
|
||||
github.com/mattn/go-sqlite3 v1.14.34
|
||||
golang.org/x/crypto v0.48.0
|
||||
modernc.org/sqlite v1.48.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/ncruces/go-strftime v1.0.0 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
golang.org/x/sys v0.42.0 // indirect
|
||||
modernc.org/libc v1.70.0 // indirect
|
||||
modernc.org/mathutil v1.7.1 // indirect
|
||||
modernc.org/memory v1.11.0 // indirect
|
||||
)
|
||||
|
||||
@@ -1,4 +1,53 @@
|
||||
github.com/mattn/go-sqlite3 v1.14.34 h1:3NtcvcUnFBPsuRcno8pUtupspG/GM+9nZ88zgJcp6Zk=
|
||||
github.com/mattn/go-sqlite3 v1.14.34/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs=
|
||||
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w=
|
||||
github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
|
||||
golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
|
||||
golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8=
|
||||
golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w=
|
||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
|
||||
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k=
|
||||
golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0=
|
||||
modernc.org/cc/v4 v4.27.1 h1:9W30zRlYrefrDV2JE2O8VDtJ1yPGownxciz5rrbQZis=
|
||||
modernc.org/cc/v4 v4.27.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
|
||||
modernc.org/ccgo/v4 v4.32.0 h1:hjG66bI/kqIPX1b2yT6fr/jt+QedtP2fqojG2VrFuVw=
|
||||
modernc.org/ccgo/v4 v4.32.0/go.mod h1:6F08EBCx5uQc38kMGl+0Nm0oWczoo1c7cgpzEry7Uc0=
|
||||
modernc.org/fileutil v1.4.0 h1:j6ZzNTftVS054gi281TyLjHPp6CPHr2KCxEXjEbD6SM=
|
||||
modernc.org/fileutil v1.4.0/go.mod h1:EqdKFDxiByqxLk8ozOxObDSfcVOv/54xDs/DUHdvCUU=
|
||||
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
|
||||
modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
|
||||
modernc.org/gc/v3 v3.1.2 h1:ZtDCnhonXSZexk/AYsegNRV1lJGgaNZJuKjJSWKyEqo=
|
||||
modernc.org/gc/v3 v3.1.2/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY=
|
||||
modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks=
|
||||
modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI=
|
||||
modernc.org/libc v1.70.0 h1:U58NawXqXbgpZ/dcdS9kMshu08aiA6b7gusEusqzNkw=
|
||||
modernc.org/libc v1.70.0/go.mod h1:OVmxFGP1CI/Z4L3E0Q3Mf1PDE0BucwMkcXjjLntvHJo=
|
||||
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
|
||||
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
|
||||
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
|
||||
modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
|
||||
modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
|
||||
modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
|
||||
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
|
||||
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
|
||||
modernc.org/sqlite v1.48.0 h1:ElZyLop3Q2mHYk5IFPPXADejZrlHu7APbpB0sF78bq4=
|
||||
modernc.org/sqlite v1.48.0/go.mod h1:hWjRO6Tj/5Ik8ieqxQybiEOUXy0NJFNp2tpvVpKlvig=
|
||||
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
|
||||
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
|
||||
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
domain2 "epigas.gitea.cloud/RiskRancher/core/pkg/domain"
|
||||
domain2 "code.riskrancher.com/RiskRancher/core/pkg/domain"
|
||||
)
|
||||
|
||||
func (h *Handler) HandleGetAdapters(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
@@ -9,8 +9,8 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"epigas.gitea.cloud/RiskRancher/core/pkg/datastore"
|
||||
"epigas.gitea.cloud/RiskRancher/core/pkg/domain"
|
||||
"code.riskrancher.com/RiskRancher/core/pkg/datastore"
|
||||
"code.riskrancher.com/RiskRancher/core/pkg/domain"
|
||||
)
|
||||
|
||||
func setupTestAdapters(t *testing.T) (*Handler, *sql.DB) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package adapters
|
||||
|
||||
import (
|
||||
"epigas.gitea.cloud/RiskRancher/core/pkg/domain"
|
||||
"code.riskrancher.com/RiskRancher/core/pkg/domain"
|
||||
)
|
||||
|
||||
type Handler struct {
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"epigas.gitea.cloud/RiskRancher/core/pkg/auth"
|
||||
"code.riskrancher.com/RiskRancher/core/pkg/auth"
|
||||
)
|
||||
|
||||
// PasswordResetRequest is the expected JSON payload
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"epigas.gitea.cloud/RiskRancher/core/pkg/domain"
|
||||
"code.riskrancher.com/RiskRancher/core/pkg/domain"
|
||||
)
|
||||
|
||||
func TestGetGlobalConfig(t *testing.T) {
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"epigas.gitea.cloud/RiskRancher/core/pkg/domain"
|
||||
"code.riskrancher.com/RiskRancher/core/pkg/domain"
|
||||
)
|
||||
|
||||
func TestExportSystemState(t *testing.T) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"epigas.gitea.cloud/RiskRancher/core/pkg/domain"
|
||||
"code.riskrancher.com/RiskRancher/core/pkg/domain"
|
||||
)
|
||||
|
||||
// Handler encapsulates all Admin and Sheriff HTTP logic
|
||||
|
||||
@@ -7,8 +7,8 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"epigas.gitea.cloud/RiskRancher/core/pkg/datastore"
|
||||
"epigas.gitea.cloud/RiskRancher/core/pkg/domain"
|
||||
"code.riskrancher.com/RiskRancher/core/pkg/datastore"
|
||||
"code.riskrancher.com/RiskRancher/core/pkg/domain"
|
||||
)
|
||||
|
||||
// setupTestAdmin returns the clean Admin Handler and the raw DB
|
||||
|
||||
@@ -9,8 +9,8 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"epigas.gitea.cloud/RiskRancher/core/pkg/datastore"
|
||||
"epigas.gitea.cloud/RiskRancher/core/pkg/domain"
|
||||
"code.riskrancher.com/RiskRancher/core/pkg/datastore"
|
||||
"code.riskrancher.com/RiskRancher/core/pkg/domain"
|
||||
)
|
||||
|
||||
func setupTestAnalytics(t *testing.T) (*Handler, *sql.DB) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package analytics
|
||||
|
||||
import (
|
||||
"epigas.gitea.cloud/RiskRancher/core/pkg/domain"
|
||||
"code.riskrancher.com/RiskRancher/core/pkg/domain"
|
||||
)
|
||||
|
||||
type Handler struct {
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ import (
|
||||
"encoding/base64"
|
||||
"math/rand"
|
||||
|
||||
"epigas.gitea.cloud/RiskRancher/core/pkg/domain"
|
||||
"code.riskrancher.com/RiskRancher/core/pkg/domain"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"epigas.gitea.cloud/RiskRancher/core/pkg/datastore"
|
||||
"code.riskrancher.com/RiskRancher/core/pkg/datastore"
|
||||
)
|
||||
|
||||
func setupTestAuth(t *testing.T) (*Handler, *sql.DB) {
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"epigas.gitea.cloud/RiskRancher/core/pkg/domain"
|
||||
"code.riskrancher.com/RiskRancher/core/pkg/domain"
|
||||
)
|
||||
|
||||
// ErrNotFound is a standard error we can use across our handlers
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
_ "modernc.org/sqlite"
|
||||
)
|
||||
|
||||
// runChaosEngine fires 100 concurrent workers at the provided database connection
|
||||
@@ -73,8 +73,8 @@ func TestSQLiteConcurrency_Tuned_Succeeds(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
dbPath := filepath.Join(tempDir, "tuned.db")
|
||||
|
||||
dsn := fmt.Sprintf("%s?_journal_mode=WAL&_synchronous=NORMAL&_busy_timeout=5000", dbPath)
|
||||
db, err := sql.Open("sqlite3", dsn)
|
||||
dsn := fmt.Sprintf("%s?_pragma=journal_mode(WAL)&_pragma=synchronous(NORMAL)&_pragma=busy_timeout(5000)", dbPath)
|
||||
db, err := sql.Open("sqlite", dsn)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to open tuned DB: %v", err)
|
||||
}
|
||||
|
||||
+13
-5
@@ -6,10 +6,12 @@ import (
|
||||
_ "embed"
|
||||
"encoding/json"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"epigas.gitea.cloud/RiskRancher/core/pkg/domain"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
"code.riskrancher.com/RiskRancher/core/pkg/domain"
|
||||
_ "modernc.org/sqlite"
|
||||
)
|
||||
|
||||
//go:embed schema.sql
|
||||
@@ -18,10 +20,16 @@ var schemaSQL string
|
||||
//go:embed defaults/*.json
|
||||
var defaultAdaptersFS embed.FS
|
||||
|
||||
func InitDB(filepath string) *sql.DB {
|
||||
dsn := "file:" + filepath + "?_journal=WAL&_timeout=5000&_sync=1&_fk=1"
|
||||
func InitDB(dbPath string) *sql.DB {
|
||||
|
||||
db, err := sql.Open("sqlite3", dsn)
|
||||
dir := filepath.Dir(dbPath)
|
||||
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
log.Fatalf("Failed to create database directory: %v", err)
|
||||
}
|
||||
dsn := "file:" + dbPath + "?_pragma=journal_mode(WAL)&_pragma=busy_timeout(5000)&_pragma=synchronous(NORMAL)&_pragma=foreign_keys(1)"
|
||||
|
||||
db, err := sql.Open("sqlite", dsn)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to open database: %v", err)
|
||||
}
|
||||
|
||||
@@ -5,12 +5,12 @@ import (
|
||||
"database/sql"
|
||||
"testing"
|
||||
|
||||
"epigas.gitea.cloud/RiskRancher/core/pkg/domain"
|
||||
_ "github.com/mattn/go-sqlite3" // We need the SQLite driver for the test
|
||||
"code.riskrancher.com/RiskRancher/core/pkg/domain"
|
||||
_ "modernc.org/sqlite" // We need the SQLite driver for the test
|
||||
)
|
||||
|
||||
func setupTestDB(t *testing.T) *SQLiteStore {
|
||||
db, err := sql.Open("sqlite3", ":memory:")
|
||||
db, err := sql.Open("sqlite", ":memory:")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to open in-memory SQLite database: %v", err)
|
||||
}
|
||||
|
||||
@@ -4,11 +4,11 @@ import (
|
||||
"database/sql"
|
||||
"testing"
|
||||
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
_ "modernc.org/sqlite"
|
||||
)
|
||||
|
||||
func TestSchemaMigrations(t *testing.T) {
|
||||
db, err := sql.Open("sqlite3", ":memory:")
|
||||
db, err := sql.Open("sqlite", ":memory:")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to open test db: %v", err)
|
||||
}
|
||||
|
||||
@@ -28,8 +28,13 @@ CREATE TABLE IF NOT EXISTS sla_policies (
|
||||
);
|
||||
|
||||
INSERT OR IGNORE INTO sla_policies (domain, severity, days_to_triage, days_to_remediate, max_extensions) VALUES
|
||||
('Vulnerability', 'Critical', 3, 14, 1), ('Vulnerability', 'High', 3, 30, 2),
|
||||
('Privacy', 'Critical', 3, 3, 0), ('Privacy', 'High', 3, 7, 1),
|
||||
('Vulnerability', 'Critical', 3, 14, 1),
|
||||
('Vulnerability', 'High', 3, 30, 2),
|
||||
('Vulnerability', 'Medium', 7, 60, 2),
|
||||
('Vulnerability', 'Low', 14, 90, 3),
|
||||
('Vulnerability', 'Info', 30, 180, 5),
|
||||
('Privacy', 'Critical', 3, 3, 0),
|
||||
('Privacy', 'High', 3, 7, 1),
|
||||
('Incident', 'Critical', 3, 1, 0);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
|
||||
@@ -3,7 +3,7 @@ package datastore
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
"epigas.gitea.cloud/RiskRancher/core/pkg/domain"
|
||||
"code.riskrancher.com/RiskRancher/core/pkg/domain"
|
||||
)
|
||||
|
||||
type SQLiteStore struct {
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
domain2 "epigas.gitea.cloud/RiskRancher/core/pkg/domain"
|
||||
domain2 "code.riskrancher.com/RiskRancher/core/pkg/domain"
|
||||
)
|
||||
|
||||
func (s *SQLiteStore) UpdateAppConfig(ctx context.Context, config domain2.AppConfig) error {
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
domain2 "epigas.gitea.cloud/RiskRancher/core/pkg/domain"
|
||||
domain2 "code.riskrancher.com/RiskRancher/core/pkg/domain"
|
||||
)
|
||||
|
||||
func (s *SQLiteStore) GetSheriffAnalytics(ctx context.Context) (domain2.SheriffAnalytics, error) {
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
domain2 "epigas.gitea.cloud/RiskRancher/core/pkg/domain"
|
||||
domain2 "code.riskrancher.com/RiskRancher/core/pkg/domain"
|
||||
)
|
||||
|
||||
func (s *SQLiteStore) SaveDraft(ctx context.Context, d domain2.DraftTicket) error {
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"database/sql"
|
||||
"time"
|
||||
|
||||
domain2 "epigas.gitea.cloud/RiskRancher/core/pkg/domain"
|
||||
domain2 "code.riskrancher.com/RiskRancher/core/pkg/domain"
|
||||
)
|
||||
|
||||
func (s *SQLiteStore) IngestTickets(ctx context.Context, tickets []domain2.Ticket) error {
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"epigas.gitea.cloud/RiskRancher/core/pkg/domain"
|
||||
"code.riskrancher.com/RiskRancher/core/pkg/domain"
|
||||
)
|
||||
|
||||
func (s *SQLiteStore) GetTickets(ctx context.Context) ([]domain.Ticket, error) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package ingest
|
||||
|
||||
import (
|
||||
"epigas.gitea.cloud/RiskRancher/core/pkg/domain"
|
||||
"code.riskrancher.com/RiskRancher/core/pkg/domain"
|
||||
)
|
||||
|
||||
type Handler struct {
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"epigas.gitea.cloud/RiskRancher/core/pkg/domain"
|
||||
"code.riskrancher.com/RiskRancher/core/pkg/domain"
|
||||
)
|
||||
|
||||
func (h *Handler) HandleIngest(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
@@ -13,8 +13,8 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"epigas.gitea.cloud/RiskRancher/core/pkg/datastore"
|
||||
"epigas.gitea.cloud/RiskRancher/core/pkg/domain"
|
||||
"code.riskrancher.com/RiskRancher/core/pkg/datastore"
|
||||
"code.riskrancher.com/RiskRancher/core/pkg/domain"
|
||||
)
|
||||
|
||||
func setupTestIngest(t *testing.T) (*Handler, *sql.DB) {
|
||||
|
||||
@@ -5,8 +5,8 @@ import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"epigas.gitea.cloud/RiskRancher/core/pkg/auth"
|
||||
domain2 "epigas.gitea.cloud/RiskRancher/core/pkg/domain"
|
||||
"code.riskrancher.com/RiskRancher/core/pkg/auth"
|
||||
domain2 "code.riskrancher.com/RiskRancher/core/pkg/domain"
|
||||
)
|
||||
|
||||
func (h *Handler) HandleSaveDraft(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package report
|
||||
|
||||
import (
|
||||
"epigas.gitea.cloud/RiskRancher/core/pkg/domain"
|
||||
"code.riskrancher.com/RiskRancher/core/pkg/domain"
|
||||
)
|
||||
|
||||
type Handler struct {
|
||||
|
||||
@@ -13,8 +13,8 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"epigas.gitea.cloud/RiskRancher/core/pkg/datastore"
|
||||
"epigas.gitea.cloud/RiskRancher/core/pkg/domain"
|
||||
"code.riskrancher.com/RiskRancher/core/pkg/datastore"
|
||||
"code.riskrancher.com/RiskRancher/core/pkg/domain"
|
||||
)
|
||||
|
||||
func setupTestReport(t *testing.T) (*Handler, *sql.DB) {
|
||||
|
||||
+2
-2
@@ -3,8 +3,8 @@ package server
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"epigas.gitea.cloud/RiskRancher/core/pkg/domain"
|
||||
"epigas.gitea.cloud/RiskRancher/core/pkg/sla"
|
||||
"code.riskrancher.com/RiskRancher/core/pkg/domain"
|
||||
"code.riskrancher.com/RiskRancher/core/pkg/sla"
|
||||
)
|
||||
|
||||
type App struct {
|
||||
|
||||
@@ -3,14 +3,14 @@ package server
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"epigas.gitea.cloud/RiskRancher/core/pkg/adapters"
|
||||
"epigas.gitea.cloud/RiskRancher/core/pkg/admin"
|
||||
"epigas.gitea.cloud/RiskRancher/core/pkg/analytics"
|
||||
"epigas.gitea.cloud/RiskRancher/core/pkg/auth"
|
||||
"epigas.gitea.cloud/RiskRancher/core/pkg/ingest"
|
||||
"epigas.gitea.cloud/RiskRancher/core/pkg/report"
|
||||
"epigas.gitea.cloud/RiskRancher/core/pkg/tickets"
|
||||
"epigas.gitea.cloud/RiskRancher/core/ui"
|
||||
"code.riskrancher.com/RiskRancher/core/pkg/adapters"
|
||||
"code.riskrancher.com/RiskRancher/core/pkg/admin"
|
||||
"code.riskrancher.com/RiskRancher/core/pkg/analytics"
|
||||
"code.riskrancher.com/RiskRancher/core/pkg/auth"
|
||||
"code.riskrancher.com/RiskRancher/core/pkg/ingest"
|
||||
"code.riskrancher.com/RiskRancher/core/pkg/report"
|
||||
"code.riskrancher.com/RiskRancher/core/pkg/tickets"
|
||||
"code.riskrancher.com/RiskRancher/core/ui"
|
||||
)
|
||||
|
||||
func RegisterRoutes(app *App) {
|
||||
@@ -85,7 +85,7 @@ func RegisterRoutes(app *App) {
|
||||
|
||||
app.Router.Handle("POST /api/adapters", adminOnly(adapterH.HandleCreateAdapter))
|
||||
app.Router.Handle("DELETE /api/adapters/{id}", adminOnly(adapterH.HandleDeleteAdapter))
|
||||
|
||||
|
||||
app.Router.Handle("GET /api/admin/export", sheriffOnly(adminH.HandleExportState))
|
||||
app.Router.Handle("GET /api/admin/check-updates", sheriffOnly(adminH.HandleCheckUpdates))
|
||||
app.Router.Handle("POST /api/admin/shutdown", sheriffOnly(adminH.HandleShutdown))
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"epigas.gitea.cloud/RiskRancher/core/pkg/domain"
|
||||
"code.riskrancher.com/RiskRancher/core/pkg/domain"
|
||||
)
|
||||
|
||||
// DefaultSLACalculator implements the SLACalculator interface
|
||||
|
||||
+2
-2
@@ -4,7 +4,7 @@ import (
|
||||
"database/sql"
|
||||
"testing"
|
||||
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
_ "modernc.org/sqlite"
|
||||
)
|
||||
|
||||
// GetSLAPolicy simulates the core engine function that fetches SLA rules
|
||||
@@ -16,7 +16,7 @@ func GetSLAPolicy(db *sql.DB, domain string, severity string) (daysToRemediate i
|
||||
|
||||
// setupTestDB spins up an isolated, in-memory database for testing
|
||||
func setupTestDB(t *testing.T) *sql.DB {
|
||||
db, err := sql.Open("sqlite3", ":memory:")
|
||||
db, err := sql.Open("sqlite", ":memory:")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to open test database: %v", err)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package tickets
|
||||
|
||||
import (
|
||||
"epigas.gitea.cloud/RiskRancher/core/pkg/domain"
|
||||
"code.riskrancher.com/RiskRancher/core/pkg/domain"
|
||||
)
|
||||
|
||||
// Handler encapsulates all Ticket-related HTTP logic
|
||||
|
||||
@@ -10,8 +10,8 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"epigas.gitea.cloud/RiskRancher/core/pkg/datastore"
|
||||
"epigas.gitea.cloud/RiskRancher/core/pkg/domain"
|
||||
"code.riskrancher.com/RiskRancher/core/pkg/datastore"
|
||||
"code.riskrancher.com/RiskRancher/core/pkg/domain"
|
||||
)
|
||||
|
||||
func setupTestTickets(t *testing.T) (*Handler, *sql.DB) {
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"epigas.gitea.cloud/RiskRancher/core/pkg/domain"
|
||||
"code.riskrancher.com/RiskRancher/core/pkg/domain"
|
||||
)
|
||||
|
||||
type InlineUpdateRequest struct {
|
||||
|
||||
@@ -10,15 +10,18 @@
|
||||
<div class="tab-nav">
|
||||
<button class="tab-btn active" onclick="switchTab('tab-metrics', this)">📊 Metrics</button>
|
||||
<button class="tab-btn" onclick="switchTab('tab-performance', this)">📡 Performance</button>
|
||||
{{ block "pro_risk_tab_btn" .}}
|
||||
<button class="tab-btn" style="color: #94a3b8; cursor: not-allowed;" title="Available in RiskRancher Pro">
|
||||
🔒 Risk Reviews (Pro)
|
||||
</button>
|
||||
{{ end }}
|
||||
<button class="tab-btn" onclick="switchTab('tab-config', this)">⚙️ Configuration</button>
|
||||
<button class="tab-btn" onclick="switchTab('tab-feed', this)">📻 System Logs</button>
|
||||
</div>
|
||||
|
||||
{{template "admin_metrics" .}}
|
||||
{{template "admin_performance" .}}
|
||||
{{block "pro_risk_tab_content" .}}{{end}}
|
||||
{{template "admin_config" .}}
|
||||
{{template "admin_feed" .}}
|
||||
{{template "admin_modals" .}}
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div style="border: 1px solid #e2e8f0; border-radius: 8px; padding: 15px;">
|
||||
{{block "pro_source_routing" .}}
|
||||
<div style="border: 1px solid #e2e8f0; border-radius: 8px; padding: 15px; background: #f8fafc;">
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px;">
|
||||
<h3 style="margin: 0; color: #64748b;">🛤️ Source Routing</h3>
|
||||
@@ -35,65 +35,69 @@
|
||||
<a href="https://RiskRancher.com/pro" target="_blank" style="color: #8b5cf6; text-decoration: none; font-weight: bold; font-size: 0.85rem;">Learn about RiskRancher Pro →</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
<div style="display: grid; grid-template-columns: 2fr 1fr; gap: 20px;">
|
||||
|
||||
<div style="border: 1px solid #e2e8f0; border-radius: 8px; padding: 15px; position: relative;">
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px;">
|
||||
<div>
|
||||
<h3 style="margin: 0;">⏱️ SLA Policies & System Time</h3>
|
||||
<p style="margin: 2px 0 0 0; font-size: 0.8rem; color: #64748b;">Locked to Standard FedRAMP/NIST Default Timeframes</p>
|
||||
{{block "pro_sla_policy" .}}
|
||||
<div style="border: 1px solid #e2e8f0; border-radius: 8px; padding: 15px; position: relative;">
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px;">
|
||||
<div>
|
||||
<h3 style="margin: 0;">⏱️ SLA Policies & System Time</h3>
|
||||
<p style="margin: 2px 0 0 0; font-size: 0.8rem; color: #64748b;">Locked to Standard FedRAMP/NIST Default Timeframes</p>
|
||||
</div>
|
||||
<button class="btn" style="padding: 6px 12px; font-size: 0.85rem; background: #f8fafc; color: #64748b; border: 1px solid #cbd5e1;" onclick="showUpsell('Custom SLA Timers & Business Hours')">🔒 Customize (Pro)</button>
|
||||
</div>
|
||||
<button class="btn" style="padding: 6px 12px; font-size: 0.85rem; background: #f8fafc; color: #64748b; border: 1px solid #cbd5e1;" onclick="showUpsell('Custom SLA Timers & Business Hours')">🔒 Customize (Pro)</button>
|
||||
</div>
|
||||
<div style="display: flex; gap: 30px; opacity: 0.7; pointer-events: none;">
|
||||
<div style="flex: 1; padding: 15px; background: #f8fafc; border-radius: 6px; border: 1px solid #e2e8f0;">
|
||||
<h4 style="margin-top: 0;">Base Configuration</h4>
|
||||
<label>System Timezone:</label>
|
||||
<select disabled style="width: 100%; padding: 6px; margin-bottom: 10px; background: #e2e8f0;">
|
||||
<option selected>UTC (Universal)</option>
|
||||
</select>
|
||||
<div style="display: flex; gap: 10px; margin-bottom: 10px;">
|
||||
<div style="flex: 1;"><label>Biz Start:</label><input type="text" disabled value="09:00" style="width: 100%; padding: 6px; background: #e2e8f0;"></div>
|
||||
<div style="flex: 1;"><label>Biz End:</label><input type="text" disabled value="17:00" style="width: 100%; padding: 6px; background: #e2e8f0;"></div>
|
||||
<div style="display: flex; gap: 30px; opacity: 0.7; pointer-events: none;">
|
||||
<div style="flex: 1; padding: 15px; background: #f8fafc; border-radius: 6px; border: 1px solid #e2e8f0;">
|
||||
<h4 style="margin-top: 0;">Base Configuration</h4>
|
||||
<label>System Timezone:</label>
|
||||
<select disabled style="width: 100%; padding: 6px; margin-bottom: 10px; background: #e2e8f0;">
|
||||
<option selected>UTC (Universal)</option>
|
||||
</select>
|
||||
<div style="display: flex; gap: 10px; margin-bottom: 10px;">
|
||||
<div style="flex: 1;"><label>Biz Start:</label><input type="text" disabled value="09:00" style="width: 100%; padding: 6px; background: #e2e8f0;"></div>
|
||||
<div style="flex: 1;"><label>Biz End:</label><input type="text" disabled value="17:00" style="width: 100%; padding: 6px; background: #e2e8f0;"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="flex: 2;">
|
||||
<div style="margin-bottom: 10px; display: flex; justify-content: space-between;">
|
||||
<h4 style="margin: 0;">SLA Matrix (Days to Patch)</h4>
|
||||
</div>
|
||||
<table style="font-size: 0.85rem; width: 100%; text-align: left;">
|
||||
<thead><tr><th style="padding-bottom: 5px;">Severity</th><th>Triage</th><th>Patch</th></tr></thead>
|
||||
<tbody>
|
||||
<tr class="sla-row"><td style="padding: 4px 0;"><span class="badge critical">Critical</span></td><td>1</td><td>3</td></tr>
|
||||
<tr class="sla-row"><td style="padding: 4px 0;"><span class="badge high">High</span></td><td>3</td><td>14</td></tr>
|
||||
<tr class="sla-row"><td style="padding: 4px 0;"><span class="badge medium">Medium</span></td><td>7</td><td>30</td></tr>
|
||||
<tr class="sla-row"><td style="padding: 4px 0;"><span class="badge low">Low</span></td><td>14</td><td>90</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div style="flex: 2;">
|
||||
<div style="margin-bottom: 10px; display: flex; justify-content: space-between;">
|
||||
<h4 style="margin: 0;">SLA Matrix (Days to Patch)</h4>
|
||||
</div>
|
||||
<table style="font-size: 0.85rem; width: 100%; text-align: left;">
|
||||
<thead><tr><th style="padding-bottom: 5px;">Severity</th><th>Triage</th><th>Patch</th></tr></thead>
|
||||
<tbody>
|
||||
<tr class="sla-row"><td style="padding: 4px 0;"><span class="badge critical">Critical</span></td><td>1</td><td>3</td></tr>
|
||||
<tr class="sla-row"><td style="padding: 4px 0;"><span class="badge high">High</span></td><td>3</td><td>14</td></tr>
|
||||
<tr class="sla-row"><td style="padding: 4px 0;"><span class="badge medium">Medium</span></td><td>7</td><td>30</td></tr>
|
||||
<tr class="sla-row"><td style="padding: 4px 0;"><span class="badge low">Low</span></td><td>14</td><td>90</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
<div style="border: 1px solid #e2e8f0; border-radius: 8px; padding: 15px; display: flex; flex-direction: column;">
|
||||
<h3 style="margin: 0 0 15px 0;">⚙️ Operations</h3>
|
||||
|
||||
<div style="margin-bottom: 20px;">
|
||||
<label style="font-weight: bold; display: flex; justify-content: space-between;">
|
||||
Automated Backups
|
||||
<span style="font-size: 0.75rem; color: #8b5cf6; font-weight: normal;">Pro Feature</span>
|
||||
</label>
|
||||
<div style="display: flex; gap: 10px; margin-top: 5px;">
|
||||
<select disabled style="flex: 1; padding: 6px; background: #f1f5f9; color: #94a3b8; cursor: not-allowed; border: 1px solid #cbd5e1;">
|
||||
<option>Manual Only (Free Core)</option>
|
||||
<option>Daily Automated</option>
|
||||
<option>Weekly Automated</option>
|
||||
</select>
|
||||
<button class="btn btn-secondary" style="color: #94a3b8; border-color: #cbd5e1;" onclick="showUpsell('Automated DB Backups')">🔒 Apply</button>
|
||||
{{block "pro_backups" .}}
|
||||
<div style="margin-bottom: 20px;">
|
||||
<label style="font-weight: bold; display: flex; justify-content: space-between;">
|
||||
Automated Backups
|
||||
<span style="font-size: 0.75rem; color: #8b5cf6; font-weight: normal;">Pro Feature</span>
|
||||
</label>
|
||||
<div style="display: flex; gap: 10px; margin-top: 5px;">
|
||||
<select disabled style="flex: 1; padding: 6px; background: #f1f5f9; color: #94a3b8; cursor: not-allowed; border: 1px solid #cbd5e1;">
|
||||
<option>Manual Only (Free Core)</option>
|
||||
<option>Daily Automated</option>
|
||||
<option>Weekly Automated</option>
|
||||
</select>
|
||||
<button class="btn btn-secondary" style="color: #94a3b8; border-color: #cbd5e1;" onclick="showUpsell('Automated DB Backups')">🔒 Apply</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
<div style="margin-bottom: 20px;">
|
||||
<label style="font-weight: bold;">Data Portability</label>
|
||||
|
||||
@@ -8,9 +8,11 @@
|
||||
</a>
|
||||
<a href="/dashboard?tab=chute" class="tab-btn {{if eq .CurrentTab "chute"}}active{{end}}">🤠 The Chute (Assigned)</a>
|
||||
|
||||
<a href="javascript:void(0)" onclick="showUpsell('E2E Exception & Verification Pipeline')" class="tab-btn" style="color: #94a3b8;">
|
||||
🔒 Pending Verification (Pro)
|
||||
</a>
|
||||
{{block "pro_verification_tab" .}}
|
||||
<a href="javascript:void(0)" onclick="showUpsell('E2E Exception & Verification Pipeline')" class="tab-btn" style="color: #94a3b8;">
|
||||
🔒 Pending Verification (Pro)
|
||||
</a>
|
||||
{{end}}
|
||||
|
||||
<a href="/dashboard?tab=archives" class="tab-btn {{if eq .CurrentTab "archives"}}active{{end}}">
|
||||
🗄️ The Archives
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
{{define "content"}}
|
||||
|
||||
<script>
|
||||
window.CurrentTab = "{{.CurrentTab}}";
|
||||
</script>
|
||||
|
||||
{{template "dash_tabs" .}}
|
||||
|
||||
{{block "pro_risk_tab_content" .}}{{end}}
|
||||
|
||||
<div class="tab-pane active">
|
||||
{{template "dash_table" .}}
|
||||
</div>
|
||||
@@ -13,5 +14,4 @@
|
||||
{{template "dash_modals" .}}
|
||||
|
||||
<script src="/static/dashboard.js"></script>
|
||||
|
||||
{{end}}
|
||||
@@ -13,9 +13,9 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"epigas.gitea.cloud/RiskRancher/core/pkg/auth"
|
||||
"epigas.gitea.cloud/RiskRancher/core/pkg/domain"
|
||||
"epigas.gitea.cloud/RiskRancher/core/pkg/report"
|
||||
"code.riskrancher.com/RiskRancher/core/pkg/auth"
|
||||
"code.riskrancher.com/RiskRancher/core/pkg/domain"
|
||||
"code.riskrancher.com/RiskRancher/core/pkg/report"
|
||||
)
|
||||
|
||||
//go:embed templates/* templates/components/* static/*
|
||||
|
||||
Reference in New Issue
Block a user