Skip to content

Instantly share code, notes, and snippets.

@rfletchr
Created April 8, 2026 17:23
Show Gist options
  • Select an option

  • Save rfletchr/9bb7531dd22fdfb6d26340f4e272f66f to your computer and use it in GitHub Desktop.

Select an option

Save rfletchr/9bb7531dd22fdfb6d26340f4e272f66f to your computer and use it in GitHub Desktop.
asset management server
package main
import (
"fmt"
"log"
"github.com/pocketbase/pocketbase"
"github.com/pocketbase/pocketbase/core"
)
func bootstrapReviewables(app core.App) (*core.Collection, error) {
if collection, err := app.FindCollectionByNameOrId("reviewables"); err == nil {
return collection, nil
}
collection := core.NewBaseCollection("reviewables")
collection.ListRule = new(string)
collection.ViewRule = new(string)
collection.CreateRule = new(string)
collection.UpdateRule = new(string)
// required fields
collection.Fields.Add(&core.TextField{Name: "name", Required: true})
collection.Fields.Add(&core.TextField{Name: "description", Required: true})
collection.Fields.Add(&core.TextField{Name: "path_to_movie", Required: true})
collection.Fields.Add(&core.TextField{Name: "task", Required: true})
collection.Fields.Add(&core.TextField{Name: "project", Required: true})
collection.Fields.Add(&core.AutodateField{Name: "created_at", OnCreate: true})
collection.Fields.Add(&core.TextField{Name: "created_by", Required: true})
// optional context fields
collection.Fields.Add(&core.TextField{Name: "episode"})
collection.Fields.Add(&core.TextField{Name: "sequence"})
collection.Fields.Add(&core.TextField{Name: "shot"})
collection.Fields.Add(&core.TextField{Name: "asset"})
// optional upload fields
collection.Fields.Add(&core.TextField{Name: "path_to_frames"})
collection.Fields.Add(&core.SelectField{
Name: "status",
Values: []string{"pending", "uploaded", "failed"},
})
collection.Fields.Add(&core.NumberField{Name: "foreign_id"})
collection.Fields.Add(&core.TextField{Name: "error"})
if err := app.Save(collection); err != nil {
return nil, err
}
return collection, nil
}
func bootstrapPublishes(app core.App, reviewables *core.Collection) (*core.Collection, error) {
if collection, err := app.FindCollectionByNameOrId("publishes"); err == nil {
return collection, nil
}
collection := core.NewBaseCollection("publishes")
collection.ListRule = new(string)
collection.ViewRule = new(string)
collection.CreateRule = new(string)
collection.UpdateRule = new(string)
// required fields
collection.Fields.Add(&core.TextField{Name: "name", Required: true})
collection.Fields.Add(&core.TextField{Name: "type", Required: true})
collection.Fields.Add(&core.NumberField{Name: "version_number", Required: true})
collection.Fields.Add(&core.TextField{Name: "path", Required: true})
collection.Fields.Add(&core.AutodateField{Name: "created_at", OnCreate: true})
collection.Fields.Add(&core.TextField{Name: "created_by", Required: true})
collection.Fields.Add(&core.SelectField{
Name: "status",
Required: true,
Values: []string{"pending", "completed", "deprecated", "failed"},
})
collection.Fields.Add(&core.TextField{Name: "project", Required: true})
// optional context fields
collection.Fields.Add(&core.TextField{Name: "episode"})
collection.Fields.Add(&core.TextField{Name: "sequence"})
collection.Fields.Add(&core.TextField{Name: "shot"})
collection.Fields.Add(&core.TextField{Name: "task"})
collection.Fields.Add(&core.TextField{Name: "asset"})
// reviewable relation
collection.Fields.Add(&core.RelationField{
Name: "reviewable",
CollectionId: reviewables.Id,
MaxSelect: 1,
})
collection.AddIndex("idx_publishes_relative_path", true, "path", "")
// we need to save the collection before we can add a relation field that references it.
if err := app.Save(collection); err != nil {
return nil, err
}
collection.Fields.Add(&core.RelationField{
Name: "inputs",
CollectionId: collection.Id,
})
if err := app.Save(collection); err != nil {
return nil, err
}
return collection, nil
}
func bootstrapCollections(app core.App) error {
reviewables, err := bootstrapReviewables(app)
if err != nil {
return err
}
_, err = bootstrapPublishes(app, reviewables)
return err
}
func bootstrapSettings(app core.App) error {
settings := app.Settings()
if settings.Backups.Cron != "" {
return nil // already configured
}
settings.Backups.Cron = "0 2 * * *"
settings.Backups.CronMaxKeep = 7
return app.Save(settings)
}
func main() {
app := pocketbase.New()
app.OnRecordUpdate("publishes").BindFunc(func(e *core.RecordEvent) error {
mutable := map[string]bool{"status": true}
for _, field := range e.Record.Collection().Fields.FieldNames() {
if !mutable[field] && e.Record.GetString(field) != e.Record.Original().GetString(field) {
return fmt.Errorf("field %q cannot be changed after creation", field)
}
}
return e.Next()
})
app.OnRecordCreate("publishes").BindFunc(func(e *core.RecordEvent) error {
if e.Record.GetString("status") == "" {
e.Record.Set("status", "pending")
}
return e.Next()
})
app.OnRecordUpdate("reviewables").BindFunc(func(e *core.RecordEvent) error {
mutable := map[string]bool{"status": true, "error": true, "foreign_id": true}
for _, field := range e.Record.Collection().Fields.FieldNames() {
if !mutable[field] && e.Record.GetString(field) != e.Record.Original().GetString(field) {
return fmt.Errorf("field %q cannot be changed after creation", field)
}
}
return e.Next()
})
app.OnRecordCreate("reviewables").BindFunc(func(e *core.RecordEvent) error {
if e.Record.GetString("status") == "" {
e.Record.Set("status", "pending")
}
return e.Next()
})
app.OnServe().BindFunc(func(se *core.ServeEvent) error {
if err := bootstrapSettings(se.App); err != nil {
return err
}
if err := bootstrapCollections(se.App); err != nil {
return err
}
return se.Next()
})
if err := app.Start(); err != nil {
log.Fatal(err)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment