mirror of
https://github.com/hanzoai/base.git
synced 2026-10-21 03:41:50 +00:00
# Conflicts: # core/record_field_resolver_multi_match.go # ui/dist/assets/AuthMethodsDocs-ChShXLwE.js # ui/dist/assets/AuthMethodsDocs-Cqvl0T_l.js # ui/dist/assets/AuthMethodsDocs-DIqhS7qY.js # ui/dist/assets/AuthRefreshDocs-CHakkNnn.js # ui/dist/assets/AuthRefreshDocs-DLb1qree.js # ui/dist/assets/AuthRefreshDocs-DXgrlMdk.js # ui/dist/assets/AuthWithOAuth2Docs-Bl8VRxNv.js # ui/dist/assets/AuthWithOAuth2Docs-DVBOel22.js # ui/dist/assets/AuthWithOAuth2Docs-I213UiJ7.js # ui/dist/assets/AuthWithOtpDocs-B9tL6gIN.js # ui/dist/assets/AuthWithOtpDocs-C2PPm9-Z.js # ui/dist/assets/AuthWithOtpDocs-qEMoMG3D.js # ui/dist/assets/AuthWithPasswordDocs-B8JWDbPL.js # ui/dist/assets/AuthWithPasswordDocs-BTHeYJwE.js # ui/dist/assets/AuthWithPasswordDocs-D9rqgLpO.js # ui/dist/assets/BatchApiDocs-BV4PYYXn.js # ui/dist/assets/BatchApiDocs-DXa997am.js # ui/dist/assets/BatchApiDocs-Di2Qe2dV.js # ui/dist/assets/DeleteApiDocs-C3IINRwz.js # ui/dist/assets/DeleteApiDocs-LfmVp4Uf.js # ui/dist/assets/DeleteApiDocs-SU4XdGhJ.js # ui/dist/assets/EmailChangeDocs-COx1USU5.js # ui/dist/assets/EmailChangeDocs-D-qNHT_P.js # ui/dist/assets/EmailChangeDocs-DpfXIE_V.js # ui/dist/assets/FieldsQueryParam-BYe9ZK-G.js # ui/dist/assets/FieldsQueryParam-L8vpHeF4.js # ui/dist/assets/FieldsQueryParam-z0dGQ6uY.js # ui/dist/assets/ListApiDocs-BCTT-AOz.js # ui/dist/assets/ListApiDocs-C0_GwI7k.js # ui/dist/assets/ListApiDocs-T9_aO-pW.js # ui/dist/assets/PageInstaller-B22bivaN.js # ui/dist/assets/PageInstaller-C0BBhyBx.js # ui/dist/assets/PageInstaller-IYSwJhZW.js # ui/dist/assets/PageOAuth2RedirectFailure-BjbpqlFj.js # ui/dist/assets/PageOAuth2RedirectFailure-COyNF79R.js # ui/dist/assets/PageOAuth2RedirectFailure-DPmGG9zv.js # ui/dist/assets/PageOAuth2RedirectSuccess-6cMXLFAX.js # ui/dist/assets/PageOAuth2RedirectSuccess-CdkCarGH.js # ui/dist/assets/PageOAuth2RedirectSuccess-DSEIHeQL.js # ui/dist/assets/PageRecordConfirmEmailChange-BYW-6ASg.js # ui/dist/assets/PageRecordConfirmEmailChange-Bm1CIYvq.js # ui/dist/assets/PageRecordConfirmEmailChange-BpaqOT4u.js # ui/dist/assets/PageRecordConfirmPasswordReset--wx_u2lI.js # ui/dist/assets/PageRecordConfirmPasswordReset-CoeY2Z_C.js # ui/dist/assets/PageRecordConfirmPasswordReset-Ptya5bfe.js # ui/dist/assets/PageRecordConfirmVerification-6xjF3T0O.js # ui/dist/assets/PageRecordConfirmVerification-C1UTIIJh.js # ui/dist/assets/PageRecordConfirmVerification-C4LixXvA.js # ui/dist/assets/PageSuperuserConfirmPasswordReset-19z2IFg4.js # ui/dist/assets/PageSuperuserConfirmPasswordReset-DwNi0ugQ.js # ui/dist/assets/PageSuperuserConfirmPasswordReset-SMnDTRNi.js # ui/dist/assets/PageSuperuserRequestPasswordReset-BpDW217f.js # ui/dist/assets/PageSuperuserRequestPasswordReset-DK63qE6z.js # ui/dist/assets/PageSuperuserRequestPasswordReset-bG02qLIM.js # ui/dist/assets/PasswordResetDocs-CE67mLC0.js # ui/dist/assets/PasswordResetDocs-CHAK0vma.js # ui/dist/assets/PasswordResetDocs-Dap-N4rx.js # ui/dist/assets/RealtimeApiDocs-BooTSQCb.js # ui/dist/assets/RealtimeApiDocs-CoHmYVEx.js # ui/dist/assets/RealtimeApiDocs-D7ds3fHt.js # ui/dist/assets/VerificationDocs-B42AIoCQ.js # ui/dist/assets/VerificationDocs-BS7b11zH.js # ui/dist/assets/VerificationDocs-ZhkdFVIl.js # ui/dist/assets/ViewApiDocs-BXmRLHZ1.js # ui/dist/assets/ViewApiDocs-DM5JJQjh.js # ui/dist/assets/ViewApiDocs-DRpxcuJR.js # ui/public/fonts/source-sans-pro/source-sans-pro-v18-latin_cyrillic-700.woff2 # ui/public/fonts/ubuntu-mono/ubuntu-mono-v17-cyrillic_latin-700.woff2
218 lines
6.1 KiB
Go
218 lines
6.1 KiB
Go
// Package migratecmd adds a new "migrate" command support to a Base instance.
|
|
//
|
|
// It also comes with automigrations support and templates generation
|
|
// (both for JS and GO migration files).
|
|
//
|
|
// Example usage:
|
|
//
|
|
// migratecmd.MustRegister(app, app.RootCmd, migratecmd.Config{
|
|
// TemplateLang: migratecmd.TemplateLangJS, // default to migratecmd.TemplateLangGo
|
|
// Automigrate: true,
|
|
// Dir: "/custom/migrations/dir", // optional template migrations path; default to "hz_migrations" (for JS) and "migrations" (for Go)
|
|
// })
|
|
//
|
|
// Note: To allow running JS migrations you'll need to enable first
|
|
// [jsvm.MustRegister()].
|
|
package migratecmd
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"path"
|
|
"path/filepath"
|
|
"time"
|
|
|
|
"github.com/hanzoai/base/core"
|
|
"github.com/hanzoai/base/tools/inflector"
|
|
"github.com/hanzoai/base/tools/osutils"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
// Config defines the config options of the migratecmd plugin.
|
|
type Config struct {
|
|
// Dir specifies the directory with the user defined migrations.
|
|
//
|
|
// If not set it fallbacks to a relative "hz_data/../hz_migrations" (for js)
|
|
// or "hz_data/../migrations" (for go) directory.
|
|
Dir string
|
|
|
|
// Automigrate specifies whether to enable automigrations.
|
|
Automigrate bool
|
|
|
|
// TemplateLang specifies the template language to use when
|
|
// generating migrations - js or go (default).
|
|
TemplateLang string
|
|
}
|
|
|
|
// MustRegister registers the migratecmd plugin to the provided app instance
|
|
// and panic if it fails.
|
|
//
|
|
// Example usage:
|
|
//
|
|
// migratecmd.MustRegister(app, app.RootCmd, migratecmd.Config{})
|
|
func MustRegister(app core.App, rootCmd *cobra.Command, config Config) {
|
|
if err := Register(app, rootCmd, config); err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
// Register registers the migratecmd plugin to the provided app instance.
|
|
func Register(app core.App, rootCmd *cobra.Command, config Config) error {
|
|
p := &plugin{app: app, config: config}
|
|
|
|
if p.config.TemplateLang == "" {
|
|
p.config.TemplateLang = TemplateLangGo
|
|
}
|
|
|
|
if p.config.Dir == "" {
|
|
if p.config.TemplateLang == TemplateLangJS {
|
|
p.config.Dir = filepath.Join(p.app.DataDir(), "../hz_migrations")
|
|
} else {
|
|
p.config.Dir = filepath.Join(p.app.DataDir(), "../migrations")
|
|
}
|
|
}
|
|
|
|
// attach the migrate command
|
|
if rootCmd != nil {
|
|
rootCmd.AddCommand(p.createCommand())
|
|
}
|
|
|
|
// watch for collection changes
|
|
if p.config.Automigrate {
|
|
p.app.OnCollectionCreateRequest().BindFunc(p.automigrateOnCollectionChange)
|
|
p.app.OnCollectionUpdateRequest().BindFunc(p.automigrateOnCollectionChange)
|
|
p.app.OnCollectionDeleteRequest().BindFunc(p.automigrateOnCollectionChange)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
type plugin struct {
|
|
app core.App
|
|
config Config
|
|
}
|
|
|
|
func (p *plugin) createCommand() *cobra.Command {
|
|
const cmdDesc = `Supported arguments are:
|
|
- up - runs all available migrations
|
|
- down [number] - reverts the last [number] applied migrations
|
|
- create name - creates new blank migration template file
|
|
- collections - creates new migration file with snapshot of the local collections configuration
|
|
- history-sync - ensures that the _migrations history table doesn't have references to deleted migration files
|
|
`
|
|
|
|
command := &cobra.Command{
|
|
Use: "migrate",
|
|
Short: "Executes app DB migration scripts",
|
|
Long: cmdDesc,
|
|
ValidArgs: []string{"up", "down", "create", "collections"},
|
|
SilenceUsage: true,
|
|
RunE: func(command *cobra.Command, args []string) error {
|
|
cmd := ""
|
|
if len(args) > 0 {
|
|
cmd = args[0]
|
|
}
|
|
|
|
switch cmd {
|
|
case "create":
|
|
if _, err := p.migrateCreateHandler("", args[1:], true); err != nil {
|
|
return err
|
|
}
|
|
case "collections":
|
|
if _, err := p.migrateCollectionsHandler(args[1:], true); err != nil {
|
|
return err
|
|
}
|
|
default:
|
|
// note: system migrations are always applied as part of the bootstrap process
|
|
var list = core.MigrationsList{}
|
|
list.Copy(core.SystemMigrations)
|
|
list.Copy(core.AppMigrations)
|
|
|
|
runner := core.NewMigrationsRunner(p.app, list)
|
|
|
|
if err := runner.Run(args...); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
},
|
|
}
|
|
|
|
return command
|
|
}
|
|
|
|
func (p *plugin) migrateCreateHandler(template string, args []string, interactive bool) (string, error) {
|
|
if len(args) < 1 {
|
|
return "", errors.New("missing migration file name")
|
|
}
|
|
|
|
name := args[0]
|
|
dir := p.config.Dir
|
|
|
|
filename := fmt.Sprintf("%d_%s.%s", time.Now().Unix(), inflector.Snakecase(name), p.config.TemplateLang)
|
|
|
|
resultFilePath := path.Join(dir, filename)
|
|
|
|
if interactive {
|
|
confirm := osutils.YesNoPrompt(fmt.Sprintf("Do you really want to create migration %q?", resultFilePath), false)
|
|
if !confirm {
|
|
fmt.Println("The command has been cancelled")
|
|
return "", nil
|
|
}
|
|
}
|
|
|
|
// get default create template
|
|
if template == "" {
|
|
var templateErr error
|
|
if p.config.TemplateLang == TemplateLangJS {
|
|
template, templateErr = p.jsBlankTemplate()
|
|
} else {
|
|
template, templateErr = p.goBlankTemplate()
|
|
}
|
|
if templateErr != nil {
|
|
return "", fmt.Errorf("failed to resolve create template: %v", templateErr)
|
|
}
|
|
}
|
|
|
|
// ensure that the migrations dir exist
|
|
if err := os.MkdirAll(dir, os.ModePerm); err != nil {
|
|
return "", err
|
|
}
|
|
|
|
// save the migration file
|
|
if err := os.WriteFile(resultFilePath, []byte(template), 0644); err != nil {
|
|
return "", fmt.Errorf("failed to save migration file %q: %v", resultFilePath, err)
|
|
}
|
|
|
|
if interactive {
|
|
fmt.Printf("Successfully created file %q\n", resultFilePath)
|
|
}
|
|
|
|
return filename, nil
|
|
}
|
|
|
|
func (p *plugin) migrateCollectionsHandler(args []string, interactive bool) (string, error) {
|
|
createArgs := []string{"collections_snapshot"}
|
|
createArgs = append(createArgs, args...)
|
|
|
|
collections := []*core.Collection{}
|
|
if err := p.app.CollectionQuery().OrderBy("created ASC").All(&collections); err != nil {
|
|
return "", fmt.Errorf("failed to fetch migrations list: %v", err)
|
|
}
|
|
|
|
var template string
|
|
var templateErr error
|
|
if p.config.TemplateLang == TemplateLangJS {
|
|
template, templateErr = p.jsSnapshotTemplate(collections)
|
|
} else {
|
|
template, templateErr = p.goSnapshotTemplate(collections)
|
|
}
|
|
if templateErr != nil {
|
|
return "", fmt.Errorf("failed to resolve template: %v", templateErr)
|
|
}
|
|
|
|
return p.migrateCreateHandler(template, createArgs, interactive)
|
|
}
|