mirror of
https://github.com/SecurityBrewery/catalyst.git
synced 2025-12-19 05:23:05 +01:00
refactor: improve setup and maintainability (#1067)
This commit is contained in:
57
migrations/0_base.go
Normal file
57
migrations/0_base.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"github.com/pocketbase/dbx"
|
||||
"github.com/pocketbase/pocketbase/daos"
|
||||
"github.com/pocketbase/pocketbase/models/settings"
|
||||
"github.com/pocketbase/pocketbase/tools/types"
|
||||
)
|
||||
|
||||
type baseUpFunc func(dao *daos.Dao) error
|
||||
|
||||
func baseUp(db dbx.Builder) error {
|
||||
dao := daos.New(db)
|
||||
|
||||
for _, f := range []baseUpFunc{
|
||||
settingsUp,
|
||||
allowUserViewUp,
|
||||
} {
|
||||
if err := f(dao); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func settingsUp(dao *daos.Dao) error {
|
||||
s := settings.New()
|
||||
s.Meta.AppName = "Catalyst"
|
||||
s.Meta.HideControls = false
|
||||
|
||||
return dao.SaveSettings(s)
|
||||
}
|
||||
|
||||
func allowUserViewUp(dao *daos.Dao) error {
|
||||
collection, err := dao.FindCollectionByNameOrId(UserCollectionName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
collection.ViewRule = types.Pointer("@request.auth.id != ''")
|
||||
collection.ListRule = types.Pointer("@request.auth.id != ''")
|
||||
|
||||
return dao.SaveCollection(collection)
|
||||
}
|
||||
|
||||
func baseDown(db dbx.Builder) error {
|
||||
collection, err := daos.New(db).FindCollectionByNameOrId(UserCollectionName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
collection.ViewRule = types.Pointer("id = @request.auth.id")
|
||||
collection.ListRule = types.Pointer("id = @request.auth.id")
|
||||
|
||||
return daos.New(db).SaveCollection(collection)
|
||||
}
|
||||
165
migrations/1_collections.go
Normal file
165
migrations/1_collections.go
Normal file
@@ -0,0 +1,165 @@
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/pocketbase/dbx"
|
||||
"github.com/pocketbase/pocketbase/daos"
|
||||
"github.com/pocketbase/pocketbase/models"
|
||||
"github.com/pocketbase/pocketbase/models/schema"
|
||||
"github.com/pocketbase/pocketbase/tools/types"
|
||||
)
|
||||
|
||||
const (
|
||||
TimelineCollectionName = "timeline"
|
||||
CommentCollectionName = "comments"
|
||||
fileCollectionName = "files"
|
||||
LinkCollectionName = "links"
|
||||
TaskCollectionName = "tasks"
|
||||
TicketCollectionName = "tickets"
|
||||
TypeCollectionName = "types"
|
||||
WebhookCollectionName = "webhooks"
|
||||
FeatureCollectionName = "features"
|
||||
|
||||
UserCollectionName = "_pb_users_auth_"
|
||||
)
|
||||
|
||||
func collectionsUp(db dbx.Builder) error {
|
||||
collections := []*models.Collection{
|
||||
internalCollection(&models.Collection{
|
||||
Name: TypeCollectionName,
|
||||
Type: models.CollectionTypeBase,
|
||||
Schema: schema.NewSchema(
|
||||
&schema.SchemaField{Name: "singular", Type: schema.FieldTypeText, Required: true},
|
||||
&schema.SchemaField{Name: "plural", Type: schema.FieldTypeText, Required: true},
|
||||
&schema.SchemaField{Name: "icon", Type: schema.FieldTypeText, Required: true},
|
||||
&schema.SchemaField{Name: "schema", Type: schema.FieldTypeJson, Required: true, Options: &schema.JsonOptions{MaxSize: 50_000}},
|
||||
),
|
||||
}),
|
||||
internalCollection(&models.Collection{
|
||||
Name: TicketCollectionName,
|
||||
Type: models.CollectionTypeBase,
|
||||
Schema: schema.NewSchema(
|
||||
&schema.SchemaField{Name: "name", Type: schema.FieldTypeText, Required: true},
|
||||
&schema.SchemaField{Name: "type", Type: schema.FieldTypeRelation, Options: &schema.RelationOptions{CollectionId: TypeCollectionName, MaxSelect: types.Pointer(1)}},
|
||||
&schema.SchemaField{Name: "description", Type: schema.FieldTypeText},
|
||||
&schema.SchemaField{Name: "open", Type: schema.FieldTypeBool},
|
||||
&schema.SchemaField{Name: "resolution", Type: schema.FieldTypeText},
|
||||
&schema.SchemaField{Name: "schema", Type: schema.FieldTypeJson, Options: &schema.JsonOptions{MaxSize: 50_000}},
|
||||
&schema.SchemaField{Name: "state", Type: schema.FieldTypeJson, Options: &schema.JsonOptions{MaxSize: 50_000}},
|
||||
&schema.SchemaField{Name: "owner", Type: schema.FieldTypeRelation, Options: &schema.RelationOptions{CollectionId: UserCollectionName, MaxSelect: types.Pointer(1)}},
|
||||
),
|
||||
}),
|
||||
internalCollection(&models.Collection{
|
||||
Name: TaskCollectionName,
|
||||
Type: models.CollectionTypeBase,
|
||||
Schema: schema.NewSchema(
|
||||
&schema.SchemaField{Name: "ticket", Type: schema.FieldTypeRelation, Required: true, Options: &schema.RelationOptions{CollectionId: TicketCollectionName, MaxSelect: types.Pointer(1), CascadeDelete: true}},
|
||||
&schema.SchemaField{Name: "name", Type: schema.FieldTypeText, Required: true},
|
||||
&schema.SchemaField{Name: "open", Type: schema.FieldTypeBool},
|
||||
&schema.SchemaField{Name: "owner", Type: schema.FieldTypeRelation, Options: &schema.RelationOptions{CollectionId: UserCollectionName, MaxSelect: types.Pointer(1)}},
|
||||
),
|
||||
}),
|
||||
internalCollection(&models.Collection{
|
||||
Name: CommentCollectionName,
|
||||
Type: models.CollectionTypeBase,
|
||||
Schema: schema.NewSchema(
|
||||
&schema.SchemaField{Name: "ticket", Type: schema.FieldTypeRelation, Required: true, Options: &schema.RelationOptions{CollectionId: TicketCollectionName, MaxSelect: types.Pointer(1), CascadeDelete: true}},
|
||||
&schema.SchemaField{Name: "author", Type: schema.FieldTypeRelation, Options: &schema.RelationOptions{CollectionId: UserCollectionName, MaxSelect: types.Pointer(1)}},
|
||||
&schema.SchemaField{Name: "message", Type: schema.FieldTypeText, Required: true},
|
||||
),
|
||||
}),
|
||||
internalCollection(&models.Collection{
|
||||
Name: TimelineCollectionName,
|
||||
Type: models.CollectionTypeBase,
|
||||
Schema: schema.NewSchema(
|
||||
&schema.SchemaField{Name: "ticket", Type: schema.FieldTypeRelation, Required: true, Options: &schema.RelationOptions{CollectionId: TicketCollectionName, MaxSelect: types.Pointer(1), CascadeDelete: true}},
|
||||
&schema.SchemaField{Name: "time", Type: schema.FieldTypeDate, Required: true},
|
||||
&schema.SchemaField{Name: "message", Type: schema.FieldTypeText, Required: true},
|
||||
),
|
||||
}),
|
||||
internalCollection(&models.Collection{
|
||||
Name: LinkCollectionName,
|
||||
Type: models.CollectionTypeBase,
|
||||
Schema: schema.NewSchema(
|
||||
&schema.SchemaField{Name: "ticket", Type: schema.FieldTypeRelation, Required: true, Options: &schema.RelationOptions{CollectionId: TicketCollectionName, MaxSelect: types.Pointer(1), CascadeDelete: true}},
|
||||
&schema.SchemaField{Name: "name", Type: schema.FieldTypeText, Required: true},
|
||||
&schema.SchemaField{Name: "url", Type: schema.FieldTypeUrl, Required: true},
|
||||
),
|
||||
}),
|
||||
|
||||
internalCollection(&models.Collection{
|
||||
Name: fileCollectionName,
|
||||
Type: models.CollectionTypeBase,
|
||||
Schema: schema.NewSchema(
|
||||
&schema.SchemaField{Name: "ticket", Type: schema.FieldTypeRelation, Required: true, Options: &schema.RelationOptions{CollectionId: TicketCollectionName, MaxSelect: types.Pointer(1), CascadeDelete: true}},
|
||||
&schema.SchemaField{Name: "name", Type: schema.FieldTypeText, Required: true},
|
||||
&schema.SchemaField{Name: "size", Type: schema.FieldTypeNumber, Required: true},
|
||||
&schema.SchemaField{Name: "blob", Type: schema.FieldTypeFile, Required: true, Options: &schema.FileOptions{MaxSelect: 1, MaxSize: 1024 * 1024 * 100}},
|
||||
),
|
||||
}),
|
||||
{
|
||||
BaseModel: models.BaseModel{
|
||||
Id: FeatureCollectionName,
|
||||
},
|
||||
Name: FeatureCollectionName,
|
||||
Type: models.CollectionTypeBase,
|
||||
Schema: schema.NewSchema(
|
||||
&schema.SchemaField{Name: "name", Type: schema.FieldTypeText, Required: true},
|
||||
),
|
||||
ListRule: types.Pointer("@request.auth.id != ''"),
|
||||
ViewRule: types.Pointer("@request.auth.id != ''"),
|
||||
Indexes: types.JsonArray[string]{
|
||||
fmt.Sprintf("CREATE UNIQUE INDEX `unique_name` ON `%s` (`name`)", FeatureCollectionName),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
dao := daos.New(db)
|
||||
for _, c := range collections {
|
||||
if err := dao.SaveCollection(c); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func internalCollection(c *models.Collection) *models.Collection {
|
||||
c.Id = c.Name
|
||||
c.ListRule = types.Pointer("@request.auth.id != ''")
|
||||
c.ViewRule = types.Pointer("@request.auth.id != ''")
|
||||
c.CreateRule = types.Pointer("@request.auth.id != ''")
|
||||
c.UpdateRule = types.Pointer("@request.auth.id != ''")
|
||||
c.DeleteRule = types.Pointer("@request.auth.id != ''")
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func collectionsDown(db dbx.Builder) error {
|
||||
collections := []string{
|
||||
TicketCollectionName,
|
||||
TypeCollectionName,
|
||||
fileCollectionName,
|
||||
LinkCollectionName,
|
||||
TaskCollectionName,
|
||||
CommentCollectionName,
|
||||
TimelineCollectionName,
|
||||
FeatureCollectionName,
|
||||
}
|
||||
|
||||
dao := daos.New(db)
|
||||
|
||||
for _, name := range collections {
|
||||
id, err := dao.FindCollectionByNameOrId(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := dao.DeleteCollection(id); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
51
migrations/3_defaultdata.go
Normal file
51
migrations/3_defaultdata.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"github.com/pocketbase/dbx"
|
||||
"github.com/pocketbase/pocketbase/daos"
|
||||
"github.com/pocketbase/pocketbase/models"
|
||||
"github.com/pocketbase/pocketbase/tools/security"
|
||||
)
|
||||
|
||||
func defaultDataUp(db dbx.Builder) error {
|
||||
dao := daos.New(db)
|
||||
|
||||
for _, records := range [][]*models.Record{typeRecords(dao)} {
|
||||
for _, record := range records {
|
||||
if err := dao.SaveRecord(record); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func typeRecords(dao *daos.Dao) []*models.Record {
|
||||
collection, err := dao.FindCollectionByNameOrId(TypeCollectionName)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
var records []*models.Record
|
||||
|
||||
record := models.NewRecord(collection)
|
||||
record.SetId("y_" + security.PseudorandomString(5))
|
||||
record.Set("singular", "Incident")
|
||||
record.Set("plural", "Incidents")
|
||||
record.Set("icon", "Flame")
|
||||
record.Set("schema", `{"type":"object","properties":{"tlp":{"title":"TLP","type":"string"}}}`)
|
||||
|
||||
records = append(records, record)
|
||||
|
||||
record = models.NewRecord(collection)
|
||||
record.SetId("y_" + security.PseudorandomString(5))
|
||||
record.Set("singular", "Alert")
|
||||
record.Set("plural", "Alerts")
|
||||
record.Set("icon", "AlertTriangle")
|
||||
record.Set("schema", `{"type":"object","properties":{"severity":{"title":"Severity","type":"string"}},"required": ["severity"]}`)
|
||||
|
||||
records = append(records, record)
|
||||
|
||||
return records
|
||||
}
|
||||
70
migrations/4_views.go
Normal file
70
migrations/4_views.go
Normal file
@@ -0,0 +1,70 @@
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"github.com/pocketbase/dbx"
|
||||
"github.com/pocketbase/pocketbase/daos"
|
||||
"github.com/pocketbase/pocketbase/models"
|
||||
"github.com/pocketbase/pocketbase/tools/types"
|
||||
)
|
||||
|
||||
const (
|
||||
dashboardCountsViewName = "dashboard_counts"
|
||||
sidebarViewName = "sidebar"
|
||||
)
|
||||
|
||||
const dashboardCountsViewQuery = `SELECT id, count FROM (
|
||||
SELECT 'users' as id, COUNT(users.id) as count FROM users
|
||||
UNION
|
||||
SELECT 'tickets' as id, COUNT(tickets.id) as count FROM tickets
|
||||
UNION
|
||||
SELECT 'tasks' as id, COUNT(tasks.id) as count FROM tasks
|
||||
) as counts;`
|
||||
|
||||
const sidebarViewQuery = `SELECT types.id as id, types.singular as singular, types.plural as plural, types.icon as icon, (SELECT COUNT(tickets.id) FROM tickets WHERE tickets.type = types.id AND tickets.open = true) as count
|
||||
FROM types
|
||||
ORDER BY types.plural;`
|
||||
|
||||
func viewsUp(db dbx.Builder) error {
|
||||
collections := []*models.Collection{
|
||||
internalView(dashboardCountsViewName, dashboardCountsViewQuery),
|
||||
internalView(sidebarViewName, sidebarViewQuery),
|
||||
}
|
||||
|
||||
dao := daos.New(db)
|
||||
for _, c := range collections {
|
||||
if err := dao.SaveCollection(c); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func internalView(name, query string) *models.Collection {
|
||||
return &models.Collection{
|
||||
Name: name,
|
||||
Type: models.CollectionTypeView,
|
||||
Options: types.JsonMap{"query": query},
|
||||
ListRule: types.Pointer("@request.auth.id != ''"),
|
||||
ViewRule: types.Pointer("@request.auth.id != ''"),
|
||||
}
|
||||
}
|
||||
|
||||
func viewsDown(db dbx.Builder) error {
|
||||
dao := daos.New(db)
|
||||
|
||||
collections := []string{dashboardCountsViewName, sidebarViewName}
|
||||
|
||||
for _, c := range collections {
|
||||
id, err := dao.FindCollectionByNameOrId(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := dao.DeleteCollection(id); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
12
migrations/migrations.go
Normal file
12
migrations/migrations.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"github.com/pocketbase/pocketbase/migrations"
|
||||
)
|
||||
|
||||
func Register() {
|
||||
migrations.Register(baseUp, baseDown, "1700000000_base.go")
|
||||
migrations.Register(collectionsUp, collectionsDown, "1700000001_collections.go")
|
||||
migrations.Register(defaultDataUp, nil, "1700000003_defaultdata.go")
|
||||
migrations.Register(viewsUp, viewsDown, "1700000004_views.go")
|
||||
}
|
||||
Reference in New Issue
Block a user