2022-09-10 11:39:07 +00:00
|
|
|
package v2migrator
|
|
|
|
|
|
|
|
import (
|
|
|
|
"compress/gzip"
|
|
|
|
"encoding/json"
|
|
|
|
"os"
|
|
|
|
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
"github.com/sirupsen/logrus"
|
|
|
|
|
2022-11-02 21:38:14 +00:00
|
|
|
"github.com/Luzifer/twitch-bot/v3/internal/v2migrator/crypt"
|
|
|
|
"github.com/Luzifer/twitch-bot/v3/pkg/database"
|
|
|
|
"github.com/Luzifer/twitch-bot/v3/plugins"
|
2022-09-10 11:39:07 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type (
|
|
|
|
Migrator interface {
|
|
|
|
Load(filename, encryptionPass string) error
|
|
|
|
Migrate(db database.Connector) error
|
|
|
|
}
|
|
|
|
|
|
|
|
storageExtendedPermission struct {
|
|
|
|
AccessToken string `encrypt:"true" json:"access_token,omitempty"`
|
|
|
|
RefreshToken string `encrypt:"true" json:"refresh_token,omitempty"`
|
|
|
|
Scopes []string `json:"scopes,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
storageFile struct {
|
|
|
|
Counters map[string]int64 `json:"counters"`
|
|
|
|
Timers map[string]plugins.TimerEntry `json:"timers"`
|
|
|
|
Variables map[string]string `json:"variables"`
|
|
|
|
|
|
|
|
ModuleStorage struct {
|
|
|
|
ModOverlays storageModOverlays `json:"f9ca2b3a-baf6-45ea-a347-c626168665e8"`
|
|
|
|
ModQuoteDB storageModQuoteDB `json:"917c83ee-ed40-41e4-a558-1c2e59fdf1f5"`
|
|
|
|
} `json:"module_storage"`
|
|
|
|
|
|
|
|
ExtendedPermissions map[string]*storageExtendedPermission `json:"extended_permissions"`
|
|
|
|
|
|
|
|
EventSubSecret string `encrypt:"true" json:"event_sub_secret,omitempty"`
|
|
|
|
|
|
|
|
BotAccessToken string `encrypt:"true" json:"bot_access_token,omitempty"`
|
|
|
|
BotRefreshToken string `encrypt:"true" json:"bot_refresh_token,omitempty"`
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
|
|
|
func NewStorageFile() Migrator {
|
|
|
|
return &storageFile{
|
|
|
|
Counters: map[string]int64{},
|
|
|
|
Timers: map[string]plugins.TimerEntry{},
|
|
|
|
Variables: map[string]string{},
|
|
|
|
|
|
|
|
ExtendedPermissions: map[string]*storageExtendedPermission{},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *storageFile) Load(filename, encryptionPass string) error {
|
|
|
|
f, err := os.Open(filename)
|
|
|
|
if err != nil {
|
|
|
|
if os.IsNotExist(err) {
|
|
|
|
// Store init state
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return errors.Wrap(err, "open storage file")
|
|
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
|
|
|
|
zf, err := gzip.NewReader(f)
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "create gzip reader")
|
|
|
|
}
|
|
|
|
defer zf.Close()
|
|
|
|
|
|
|
|
if err = json.NewDecoder(zf).Decode(s); err != nil {
|
|
|
|
return errors.Wrap(err, "decode storage object")
|
|
|
|
}
|
|
|
|
|
|
|
|
if err = crypt.DecryptFields(s, encryptionPass); err != nil {
|
|
|
|
return errors.Wrap(err, "decrypting storage object")
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s storageFile) Migrate(db database.Connector) error {
|
|
|
|
var bat string
|
|
|
|
err := db.ReadCoreMeta("bot_access_token", &bat)
|
|
|
|
switch {
|
|
|
|
case err == nil:
|
|
|
|
return errors.New("Access token is set, database already initialized")
|
|
|
|
|
|
|
|
case errors.Is(err, database.ErrCoreMetaNotFound):
|
|
|
|
// This is the expected state
|
|
|
|
|
|
|
|
default:
|
|
|
|
return errors.Wrap(err, "checking for bot access token")
|
|
|
|
}
|
|
|
|
|
|
|
|
for name, fn := range map[string]func(database.Connector) error{
|
|
|
|
// Core
|
|
|
|
"core": s.migrateCoreKV,
|
|
|
|
"counter": s.migrateCounters,
|
|
|
|
"permissions": s.migratePermissions,
|
|
|
|
"timers": s.migrateTimers,
|
|
|
|
"variables": s.migrateVariables,
|
|
|
|
// Modules
|
|
|
|
"mod_overlays": s.ModuleStorage.ModOverlays.migrate,
|
|
|
|
"mod_quotedb": s.ModuleStorage.ModQuoteDB.migrate,
|
|
|
|
} {
|
|
|
|
logrus.WithField("module", name).Info("Starting migration...")
|
|
|
|
if err = fn(db); err != nil {
|
|
|
|
return errors.Wrapf(err, "executing %q migration", name)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|