Compare commits

...

6 Commits

Author SHA1 Message Date
sawka e2ff745f55 checkpoint, filedb cache, wait, migrations 2024-02-13 12:56:16 -05:00
sawka eab8e3be02 checkpoint 2024-02-13 08:43:51 -05:00
sawka e589d9c4fa move screendir functions (and ptyout file func to fileops) 2024-02-12 09:40:21 -03:00
sawka 94de0460b4 fix history layout (item height and table width) 2024-02-11 23:14:29 -03:00
sawka 71b09cb44e fix typescript errors in emain.ts 2024-02-11 17:23:40 -03:00
sawka 07abaa92fa fix types for GlobalModel / GlobalCommandRunner so references work 2024-02-11 15:34:31 -03:00
14 changed files with 334 additions and 126 deletions
+5 -5
View File
@@ -14,10 +14,10 @@
width: 16px;
height: 16px;
border-radius: 4px;
border: 1px solid #3B3F3A;
border: 1px solid #3b3f3a;
background: rgba(213, 254, 175, 0.03);
}
&.checkbox-icon {
width: 16px;
height: 16px;
@@ -30,7 +30,7 @@
height: 16px;
fill: rgba(213, 254, 175, 0.03);
stroke-width: 1px;
stroke: #3B3F3A;
stroke: #3b3f3a;
}
}
@@ -264,6 +264,7 @@
margin: 0px 10px 10px 10px;
table-layout: fixed;
border-top: 2px solid #ccc;
width: calc(100% - 20px);
tr.active-history-item {
td {
@@ -309,9 +310,8 @@
tr.history-item {
padding: 0 10px 0 10px;
display: flex;
border-bottom: 1px solid rgba(250, 250, 250, 0.10);
border-bottom: 1px solid rgba(250, 250, 250, 0.1);
align-items: center;
height: 40px;
color: @text-secondary;
&.is-selected {
+23 -17
View File
@@ -14,6 +14,7 @@ import { sprintf } from "sprintf-js";
import { v4 as uuidv4 } from "uuid";
import { checkKeyPressed, adaptFromElectronKeyEvent, setKeyUtilPlatform } from "../util/keyutil";
import { platform } from "os";
import type * as T from "../types/types";
const WaveAppPathVarName = "WAVETERM_APP_PATH";
const WaveDevVarName = "WAVETERM_DEV";
@@ -40,19 +41,21 @@ let unameArch: string = process.arch;
if (unameArch == "x64") {
unameArch = "amd64";
}
let logger;
let loggerTransports: winston.transport[] = [
new winston.transports.File({ filename: path.join(waveHome, "waveterm-app.log"), level: "info" }),
];
if (isDev) {
loggerTransports.push(new winston.transports.Console());
}
let loggerConfig = {
level: "info",
format: winston.format.combine(
winston.format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }),
winston.format.printf((info) => `${info.timestamp} ${info.message}`)
),
transports: [new winston.transports.File({ filename: path.join(waveHome, "waveterm-app.log"), level: "info" })],
transports: loggerTransports,
};
if (isDev) {
loggerConfig.transports.push(new winston.transports.Console());
}
logger = winston.createLogger(loggerConfig);
let logger = winston.createLogger(loggerConfig);
function log(...msg) {
try {
logger.info(util.format(...msg));
@@ -75,7 +78,7 @@ if (isDev) {
}
let app = electron.app;
app.setName(isDev ? "Wave (Dev)" : "Wave");
let waveSrvProc = null;
let waveSrvProc: child_process.ChildProcessWithoutNullStreams | null = null;
let waveSrvShouldRestart = false;
electron.dialog.showErrorBox = (title, content) => {
@@ -101,8 +104,11 @@ function checkPromptMigrate() {
// don't migrate if we're running dev version or if wave home directory already exists
return;
}
let homeDir = process.env.HOME;
let promptHome = path.join(homeDir, "prompt");
if (process.env.HOME == null) {
return;
}
let homeDir: string = process.env.HOME;
let promptHome: string = path.join(homeDir, "prompt");
if (!fs.existsSync(promptHome) || !fs.existsSync(path.join(promptHome, "prompt.db"))) {
// make sure we have a valid prompt home directory (prompt.db must exist inside)
return;
@@ -170,7 +176,7 @@ function readAuthKey() {
return authKeyStr.trim();
}
let menuTemplate = [
let menuTemplate: Electron.MenuItemConstructorOptions[] = [
{
role: "appMenu",
submenu: [
@@ -222,7 +228,7 @@ function getMods(input: any) {
return { meta: input.meta, shift: input.shift, ctrl: input.control, alt: input.alt };
}
function shNavHandler(event: any, url: any) {
function shNavHandler(event: Electron.Event<Electron.WebContentsWillNavigateEventParams>, url: string) {
event.preventDefault();
if (url.startsWith("https://") || url.startsWith("http://") || url.startsWith("file://")) {
console.log("open external, shNav", url);
@@ -232,12 +238,13 @@ function shNavHandler(event: any, url: any) {
}
}
function shFrameNavHandler(event: any, url: any) {
function shFrameNavHandler(event: Electron.Event<Electron.WebContentsWillFrameNavigateEventParams>) {
if (!event.frame || event.frame.parent == null) {
// only use this handler to process iframe events (non-iframe events go to shNavHandler)
return;
}
event.preventDefault();
let url = event.url;
console.log(`frame-navigation url=${url} frame=${event.frame.name}`);
if (event.frame.name == "webview") {
// "webview" links always open in new window
@@ -250,7 +257,7 @@ function shFrameNavHandler(event: any, url: any) {
return;
}
function createMainWindow(clientData) {
function createMainWindow(clientData: T.ClientDataType | null) {
let bounds = calcBounds(clientData);
setKeyUtilPlatform(platform());
let win = new electron.BrowserWindow({
@@ -644,12 +651,11 @@ electron.ipcMain.on("context-editmenu", (event, { x, y }, opts) => {
}
console.log("context-editmenu");
let menu = new electron.Menu();
let menuItem = null;
if (opts.showCut) {
menuItem = new electron.MenuItem({ label: "Cut", role: "cut" });
let menuItem = new electron.MenuItem({ label: "Cut", role: "cut" });
menu.append(menuItem);
}
menuItem = new electron.MenuItem({ label: "Copy", role: "copy" });
let menuItem = new electron.MenuItem({ label: "Copy", role: "copy" });
menu.append(menuItem);
menuItem = new electron.MenuItem({ label: "Paste", role: "paste" });
menu.append(menuItem);
@@ -657,7 +663,7 @@ electron.ipcMain.on("context-editmenu", (event, { x, y }, opts) => {
});
async function createMainWindowWrap() {
let clientData = null;
let clientData: T.ClientDataType | null = null;
try {
clientData = await getClientDataPoll(1);
} catch (e) {
+1 -1
View File
@@ -8,7 +8,7 @@ import { GlobalModel } from "./global";
class CommandRunner {
private constructor() {}
static getInstance() {
static getInstance(): CommandRunner {
if (!(window as any).GlobalCommandRunner) {
(window as any).GlobalCommandRunner = new CommandRunner();
}
+2 -2
View File
@@ -1,6 +1,6 @@
import { Model } from "./model";
import { CommandRunner } from "./commandrunner";
const GlobalModel = Model.getInstance();
const GlobalCommandRunner = CommandRunner.getInstance();
const GlobalModel: Model = Model.getInstance();
const GlobalCommandRunner: CommandRunner = CommandRunner.getInstance();
export { GlobalModel, GlobalCommandRunner };
+1 -1
View File
@@ -241,7 +241,7 @@ class Model {
setTimeout(() => this.getClientDataLoop(1), 10);
}
static getInstance() {
static getInstance(): Model {
if (!(window as any).GlobalModel) {
(window as any).GlobalModel = new Model();
}
+9
View File
@@ -560,6 +560,14 @@ type ReleaseInfoType = {
latestversion: string;
};
type ClientWinSize = {
width: number;
height: number;
top: number;
left: number;
fullscreen: boolean;
};
type ClientDataType = {
clientid: string;
userid: string;
@@ -569,6 +577,7 @@ type ClientDataType = {
dbversion: number;
openaiopts?: OpenAIOptsType;
releaseinfo?: ReleaseInfoType;
winsize: ClientWinSize;
};
type OpenAIOptsType = {
+2 -2
View File
@@ -824,7 +824,7 @@ func main() {
return
}
if len(os.Args) >= 2 && strings.HasPrefix(os.Args[1], "--migrate") {
err := sstore.MigrateCommandOpts(os.Args[1:])
err := sstore.MainDBMigrateCommandOpts(os.Args[1:])
if err != nil {
log.Printf("[error] migrate cmd: %v\n", err)
}
@@ -836,7 +836,7 @@ func main() {
return
}
GlobalAuthKey = authKey
err = sstore.TryMigrateUp()
err = sstore.MainDBTryMigrateUp()
if err != nil {
log.Printf("[error] migrate up: %v\n", err)
return
@@ -0,0 +1 @@
DROP TABLE file;
@@ -0,0 +1,9 @@
CREATE TABLE file (
screenid varchar(36) NOT NULL,
lineid varchar(36) NOT NULL,
filename varchar(200) NOT NULL,
filetype varchar(20) NOT NULL,
diskfilename varchar(250) NOT NULL,
contents blob NOT NULL,
PRIMARY KEY (screenid, lineid, filename)
);
+4 -40
View File
@@ -122,7 +122,7 @@ func createWaveAuthKeyFile(fileName string) (string, error) {
func ReadWaveAuthKey() (string, error) {
homeDir := GetWaveHomeDir()
err := ensureDir(homeDir)
err := EnsureDir(homeDir)
if err != nil {
return "", fmt.Errorf("cannot find/create WAVETERM_HOME directory %q", homeDir)
}
@@ -149,7 +149,7 @@ func ReadWaveAuthKey() (string, error) {
func AcquireWaveLock() (*os.File, error) {
homeDir := GetWaveHomeDir()
err := ensureDir(homeDir)
err := EnsureDir(homeDir)
if err != nil {
return nil, fmt.Errorf("cannot find/create WAVETERM_HOME directory %q", homeDir)
}
@@ -180,7 +180,7 @@ func EnsureSessionDir(sessionId string) (string, error) {
}
scHome := GetWaveHomeDir()
sdir = path.Join(scHome, SessionsDirBaseName, sessionId)
err := ensureDir(sdir)
err := EnsureDir(sdir)
if err != nil {
return "", err
}
@@ -197,35 +197,13 @@ func GetSessionsDir() string {
return sdir
}
func EnsureScreenDir(screenId string) (string, error) {
if screenId == "" {
return "", fmt.Errorf("cannot get screen dir for blank sessionid")
}
BaseLock.Lock()
sdir, ok := ScreenDirCache[screenId]
BaseLock.Unlock()
if ok {
return sdir, nil
}
scHome := GetWaveHomeDir()
sdir = path.Join(scHome, ScreensDirBaseName, screenId)
err := ensureDir(sdir)
if err != nil {
return "", err
}
BaseLock.Lock()
ScreenDirCache[screenId] = sdir
BaseLock.Unlock()
return sdir, nil
}
func GetScreensDir() string {
waveHome := GetWaveHomeDir()
sdir := path.Join(waveHome, ScreensDirBaseName)
return sdir
}
func ensureDir(dirName string) error {
func EnsureDir(dirName string) error {
info, err := os.Stat(dirName)
if errors.Is(err, fs.ErrNotExist) {
err = os.MkdirAll(dirName, 0700)
@@ -259,20 +237,6 @@ func PtyOutFile_Sessions(sessionId string, cmdId string) (string, error) {
return fmt.Sprintf("%s/%s.ptyout.cf", sdir, cmdId), nil
}
func PtyOutFile(screenId string, lineId string) (string, error) {
sdir, err := EnsureScreenDir(screenId)
if err != nil {
return "", err
}
if screenId == "" {
return "", fmt.Errorf("cannot get ptyout file for blank screenid")
}
if lineId == "" {
return "", fmt.Errorf("cannot get ptyout file for blank lineid")
}
return fmt.Sprintf("%s/%s.ptyout.cf", sdir, lineId), nil
}
func GenWaveUUID() string {
for {
rtn := uuid.New().String()
+3 -5
View File
@@ -477,9 +477,7 @@ func GetConnectUpdate(ctx context.Context) (*ConnectUpdate, error) {
}
query := `SELECT * FROM screen ORDER BY archived, screenidx, archivedts`
screens := dbutil.SelectMapsGen[*ScreenType](tx, query)
for _, screen := range screens {
update.Screens = append(update.Screens, screen)
}
update.Screens = append(update.Screens, screens...)
query = `SELECT * FROM remote_instance`
riArr := dbutil.SelectMapsGen[*RemoteInstance](tx, query)
for _, ri := range riArr {
@@ -674,7 +672,7 @@ func fmtUniqueName(name string, defaultFmtStr string, startIdx int, strs []strin
} else {
fmtStr = defaultFmtStr
}
if strings.Index(fmtStr, "%d") == -1 {
if !strings.Contains(fmtStr, "%d") {
panic("invalid fmtStr: " + fmtStr)
}
for {
@@ -1159,7 +1157,7 @@ func ArchiveScreen(ctx context.Context, sessionId string, screenId string) (Upda
return fmt.Errorf("cannot close screen (not found)")
}
if isWebShare(tx, screenId) {
return fmt.Errorf("cannot archive screen while web-sharing. stop web-sharing before trying to archive.")
return fmt.Errorf("cannot archive screen while web-sharing. stop web-sharing before trying to archive")
}
query = `SELECT archived FROM screen WHERE sessionid = ? AND screenid = ?`
closeVal := tx.GetBool(query, sessionId, screenId)
+212 -8
View File
@@ -12,16 +12,184 @@ import (
"log"
"os"
"path"
"sync"
"time"
"github.com/google/uuid"
"github.com/jmoiron/sqlx"
"github.com/wavetermdev/waveterm/waveshell/pkg/cirfile"
"github.com/wavetermdev/waveterm/waveshell/pkg/shexec"
"github.com/wavetermdev/waveterm/wavesrv/pkg/scbase"
)
const MaxFileDBInlineFileSize = 10 * 1024
var screenDirLock = &sync.Mutex{}
var screenDirCache = make(map[string]string) // locked with screenDirLock
var globalFileDBCache = makeFileDBCache()
type fileDBCacheEntry struct {
ScreenId string
CVar *sync.Cond // condition variable to lock entry fields and wait on InUse flag
DB *sqlx.DB // can be nil (when not in use), will need to be reopend on access
InUse bool
Migrated bool // only try to migrate the DB once per run
Waiters int
LastUse time.Time
OpenErr error // we cache open errors (and return them on GetDB)
}
// we store all screens in this cache (added on demand)
// when not in use we can close the DB object
type FileDBCache struct {
Lock *sync.Mutex
Cache map[string]*fileDBCacheEntry // key = screenid
}
// will create an entry if it doesn't exist
func (dbc *FileDBCache) GetEntry(screenId string) *fileDBCacheEntry {
dbc.Lock.Lock()
defer dbc.Lock.Unlock()
entry := dbc.Cache[screenId]
if entry != nil {
return entry
}
entry = &fileDBCacheEntry{
ScreenId: screenId,
CVar: sync.NewCond(&sync.Mutex{}),
DB: nil,
Migrated: false,
InUse: false,
Waiters: 0,
LastUse: time.Time{},
}
dbc.Cache[screenId] = entry
return entry
}
func makeFileDBCache() *FileDBCache {
return &FileDBCache{
Lock: &sync.Mutex{},
Cache: make(map[string]*fileDBCacheEntry),
}
}
func MakeFileDBUrl(screenId string) (string, error) {
screenDir, err := EnsureScreenDir(screenId)
if err != nil {
return "", err
}
fileDBName := path.Join(screenDir, "filedb.db")
return fmt.Sprintf("file:%s?cache=shared&mode=rwc&_journal_mode=WAL&_busy_timeout=5000", fileDBName), nil
}
func MakeFileDB(screenId string) (*sqlx.DB, error) {
dbUrl, err := MakeFileDBUrl(screenId)
if err != nil {
return nil, err
}
return sqlx.Open("sqlite3", dbUrl)
}
// will close the DB if not in use (and no waiters)
// returns (closed, closeErr)
// if we cannot close the DB (in use), then we return (false, nil)
// if DB is already closed, we'll return (true, nil)
// if there is an error closing the DB, we'll return (true, err)
// on successful close returns (true, nil)
func (entry *fileDBCacheEntry) CloseDB() (bool, error) {
entry.CVar.L.Lock()
defer entry.CVar.L.Unlock()
if entry.DB == nil {
return true, nil
}
if entry.InUse || entry.Waiters > 0 {
return false, nil
}
err := entry.DB.Close()
entry.DB = nil
return true, err
}
// will create DB if doesn't exist
// will Wait() on CVar if InUse
// updates Waiters appropriately
func (entry *fileDBCacheEntry) GetDB() (*sqlx.DB, error) {
entry.CVar.L.Lock()
defer entry.CVar.L.Unlock()
if entry.OpenErr != nil {
return nil, entry.OpenErr
}
entry.Waiters++
for {
if entry.InUse {
entry.CVar.Wait()
continue
}
break
}
entry.Waiters--
if !entry.Migrated {
FileDBMigrateUp(entry.ScreenId)
entry.Migrated = true
}
if entry.DB == nil {
db, err := MakeFileDB(entry.ScreenId)
if err != nil {
entry.OpenErr = err
return nil, err
}
entry.DB = db
}
entry.InUse = true
entry.LastUse = time.Now()
return entry.DB, nil
}
func (entry *fileDBCacheEntry) ReleaseDB() {
entry.CVar.L.Lock()
defer entry.CVar.L.Unlock()
entry.InUse = false
entry.CVar.Signal()
}
func (c *FileDBCache) GetDB(screenId string) (*sqlx.DB, error) {
entry := c.GetEntry(screenId)
return entry.GetDB()
}
func (c *FileDBCache) ReleaseDB(screenId string, db *sqlx.DB) {
entry := c.Cache[screenId]
entry.ReleaseDB()
}
// fulfills the txwrap DBGetter interface
type FileDBGetter struct {
ScreenId string
}
func (g FileDBGetter) GetDB(ctx context.Context) (*sqlx.DB, error) {
return globalFileDBCache.GetDB(g.ScreenId)
}
func (g FileDBGetter) ReleaseDB(db *sqlx.DB) {
globalFileDBCache.ReleaseDB(g.ScreenId, db)
}
func TryConvertPtyFile(ctx context.Context, screenId string, lineId string) error {
stat, err := StatCmdPtyFile(ctx, screenId, lineId)
if err != nil {
return fmt.Errorf("convert ptyfile, cannot stat: %w", err)
}
if stat.DataSize > MaxFileDBInlineFileSize {
return nil
}
return nil
}
func CreateCmdPtyFile(ctx context.Context, screenId string, lineId string, maxSize int64) error {
ptyOutFileName, err := scbase.PtyOutFile(screenId, lineId)
ptyOutFileName, err := PtyOutFile(screenId, lineId)
if err != nil {
return err
}
@@ -33,7 +201,7 @@ func CreateCmdPtyFile(ctx context.Context, screenId string, lineId string, maxSi
}
func StatCmdPtyFile(ctx context.Context, screenId string, lineId string) (*cirfile.Stat, error) {
ptyOutFileName, err := scbase.PtyOutFile(screenId, lineId)
ptyOutFileName, err := PtyOutFile(screenId, lineId)
if err != nil {
return nil, err
}
@@ -41,7 +209,7 @@ func StatCmdPtyFile(ctx context.Context, screenId string, lineId string) (*cirfi
}
func ClearCmdPtyFile(ctx context.Context, screenId string, lineId string) error {
ptyOutFileName, err := scbase.PtyOutFile(screenId, lineId)
ptyOutFileName, err := PtyOutFile(screenId, lineId)
if err != nil {
return err
}
@@ -68,7 +236,7 @@ func AppendToCmdPtyBlob(ctx context.Context, screenId string, lineId string, dat
if pos < 0 {
return nil, fmt.Errorf("invalid seek pos '%d' in AppendToCmdPtyBlob", pos)
}
ptyOutFileName, err := scbase.PtyOutFile(screenId, lineId)
ptyOutFileName, err := PtyOutFile(screenId, lineId)
if err != nil {
return nil, err
}
@@ -99,7 +267,7 @@ func AppendToCmdPtyBlob(ctx context.Context, screenId string, lineId string, dat
// returns (real-offset, data, err)
func ReadFullPtyOutFile(ctx context.Context, screenId string, lineId string) (int64, []byte, error) {
ptyOutFileName, err := scbase.PtyOutFile(screenId, lineId)
ptyOutFileName, err := PtyOutFile(screenId, lineId)
if err != nil {
return 0, nil, err
}
@@ -113,7 +281,7 @@ func ReadFullPtyOutFile(ctx context.Context, screenId string, lineId string) (in
// returns (real-offset, data, err)
func ReadPtyOutFile(ctx context.Context, screenId string, lineId string, offset int64, maxSize int64) (int64, []byte, error) {
ptyOutFileName, err := scbase.PtyOutFile(screenId, lineId)
ptyOutFileName, err := PtyOutFile(screenId, lineId)
if err != nil {
return 0, nil, err
}
@@ -189,7 +357,7 @@ func FullSessionDiskSize() (map[string]SessionDiskSizeType, error) {
}
func DeletePtyOutFile(ctx context.Context, screenId string, lineId string) error {
ptyOutFileName, err := scbase.PtyOutFile(screenId, lineId)
ptyOutFileName, err := PtyOutFile(screenId, lineId)
if err != nil {
return err
}
@@ -218,10 +386,46 @@ func deleteScreenDirMakeCtx(screenId string) {
}
func DeleteScreenDir(ctx context.Context, screenId string) error {
screenDir, err := scbase.EnsureScreenDir(screenId)
screenDir, err := EnsureScreenDir(screenId)
if err != nil {
return fmt.Errorf("error getting screendir: %w", err)
}
log.Printf("delete screen dir, remove-all %s\n", screenDir)
return os.RemoveAll(screenDir)
}
func EnsureScreenDir(screenId string) (string, error) {
if screenId == "" {
return "", fmt.Errorf("cannot get screen dir for blank sessionid")
}
screenDirLock.Lock()
sdir, ok := screenDirCache[screenId]
screenDirLock.Unlock()
if ok {
return sdir, nil
}
scHome := scbase.GetWaveHomeDir()
sdir = path.Join(scHome, scbase.ScreensDirBaseName, screenId)
err := scbase.EnsureDir(sdir)
if err != nil {
return "", err
}
screenDirLock.Lock()
screenDirCache[screenId] = sdir
screenDirLock.Unlock()
return sdir, nil
}
func PtyOutFile(screenId string, lineId string) (string, error) {
sdir, err := EnsureScreenDir(screenId)
if err != nil {
return "", err
}
if screenId == "" {
return "", fmt.Errorf("cannot get ptyout file for blank screenid")
}
if lineId == "" {
return "", fmt.Errorf("cannot get ptyout file for blank lineid")
}
return fmt.Sprintf("%s/%s.ptyout.cf", sdir, lineId), nil
}
+58 -41
View File
@@ -22,27 +22,41 @@ import (
"github.com/golang-migrate/migrate/v4"
)
const MaxMigration = 31
const MaxMainDBMigration = 31
const MigratePrimaryScreenVersion = 9
const CmdScreenSpecialMigration = 13
const CmdLineSpecialMigration = 20
const RISpecialMigration = 30
func MakeMigrate() (*migrate.Migrate, error) {
func MakeMainDBMigrate() (*migrate.Migrate, error) {
fsVar, err := iofs.New(sh2db.MigrationFS, "migrations")
if err != nil {
return nil, fmt.Errorf("opening iofs: %w", err)
}
// migrationPathUrl := fmt.Sprintf("file://%s", path.Join(wd, "db", "migrations"))
dbUrl := fmt.Sprintf("sqlite3://%s", GetDBName())
m, err := migrate.NewWithSourceInstance("iofs", fsVar, dbUrl)
// m, err := migrate.New(migrationPathUrl, dbUrl)
if err != nil {
return nil, fmt.Errorf("making migration db[%s]: %w", GetDBName(), err)
}
return m, nil
}
func MakeFileDBMigrate(screenId string) (*migrate.Migrate, error) {
fsVar, err := iofs.New(sh2db.MigrationFS, "filedb-migrations")
if err != nil {
return nil, fmt.Errorf("opening iofs: %w", err)
}
dbUrl, err := MakeFileDBUrl(screenId)
if err != nil {
return nil, fmt.Errorf("making file db url for screenid %s: %w", screenId, err)
}
m, err := migrate.NewWithSourceInstance("iofs", fsVar, dbUrl)
if err != nil {
return nil, fmt.Errorf("making migration db[%s]: %w", dbUrl, err)
}
return m, nil
}
func copyFile(srcFile string, dstFile string, notFoundOk bool) error {
if srcFile == dstFile {
return fmt.Errorf("cannot copy %s to itself", srcFile)
@@ -95,11 +109,7 @@ func MigrateUpStep(m *migrate.Migrate, newVersion uint) error {
return nil
}
func MigrateUp(targetVersion uint) error {
m, err := MakeMigrate()
if err != nil {
return err
}
func MigrateUp(m *migrate.Migrate, targetVersion uint) error {
curVersion, dirty, err := MigrateVersion(m)
if dirty {
return fmt.Errorf("cannot migrate up, database is dirty")
@@ -135,11 +145,7 @@ func MigrateUp(targetVersion uint) error {
// returns curVersion, dirty, error
func MigrateVersion(m *migrate.Migrate) (uint, bool, error) {
if m == nil {
var err error
m, err = MakeMigrate()
if err != nil {
return 0, false, err
}
return 0, false, fmt.Errorf("migrate object is nil")
}
curVersion, dirty, err := m.Version()
if err == migrate.ErrNilVersion {
@@ -148,52 +154,60 @@ func MigrateVersion(m *migrate.Migrate) (uint, bool, error) {
return curVersion, dirty, err
}
func MigrateDown() error {
m, err := MakeMigrate()
if err != nil {
return err
}
err = m.Down()
func MigrateDown(m *migrate.Migrate) error {
err := m.Down()
if err != nil {
return err
}
return nil
}
func MigrateGoto(n uint) error {
curVersion, _, _ := MigrateVersion(nil)
func MigrateGoto(m *migrate.Migrate, n uint) error {
curVersion, _, _ := MigrateVersion(m)
if curVersion == n {
return nil
}
if curVersion < n {
return MigrateUp(n)
return MigrateUp(m, n)
}
m, err := MakeMigrate()
if err != nil {
return err
}
err = m.Migrate(n)
err := m.Migrate(n)
if err != nil {
return err
}
return nil
}
func TryMigrateUp() error {
curVersion, _, _ := MigrateVersion(nil)
func MainDBTryMigrateUp() error {
m, err := MakeMainDBMigrate()
if err != nil {
return fmt.Errorf("error trying to run main-db migrations: %w", err)
}
return TryMigrateUp(m)
}
func FileDBMigrateUp(screenId string) error {
m, err := MakeFileDBMigrate(screenId)
if err != nil {
return fmt.Errorf("error trying to run file-db migrations for screenid %s: %w", screenId, err)
}
return m.Up()
}
func TryMigrateUp(m *migrate.Migrate) error {
curVersion, _, _ := MigrateVersion(m)
log.Printf("[db] db version = %d\n", curVersion)
if curVersion >= MaxMigration {
if curVersion >= MaxMainDBMigration {
return nil
}
err := MigrateUp(MaxMigration)
err := MigrateUp(m, MaxMainDBMigration)
if err != nil {
return err
}
return MigratePrintVersion()
return MigratePrintVersion(m)
}
func MigratePrintVersion() error {
version, dirty, err := MigrateVersion(nil)
func MigratePrintVersion(m *migrate.Migrate) error {
version, dirty, err := MigrateVersion(m)
if err != nil {
return fmt.Errorf("error getting db version: %v", err)
}
@@ -204,22 +218,25 @@ func MigratePrintVersion() error {
return nil
}
func MigrateCommandOpts(opts []string) error {
var err error
func MainDBMigrateCommandOpts(opts []string) error {
m, err := MakeMainDBMigrate()
if err != nil {
return fmt.Errorf("error trying to run main-db migrations: %w", err)
}
if opts[0] == "--migrate-up" {
fmt.Printf("migrate-up %v\n", GetDBName())
time.Sleep(3 * time.Second)
err = MigrateUp(MaxMigration)
err = MigrateUp(m, MaxMainDBMigration)
} else if opts[0] == "--migrate-down" {
fmt.Printf("migrate-down %v\n", GetDBName())
time.Sleep(3 * time.Second)
err = MigrateDown()
err = MigrateDown(m)
} else if opts[0] == "--migrate-goto" {
n, err := strconv.Atoi(opts[1])
if err == nil {
fmt.Printf("migrate-goto %v => %d\n", GetDBName(), n)
time.Sleep(3 * time.Second)
err = MigrateGoto(uint(n))
err = MigrateGoto(m, uint(n))
}
} else {
err = fmt.Errorf("invalid migration command")
@@ -230,5 +247,5 @@ func MigrateCommandOpts(opts []string) error {
if err != nil {
return err
}
return MigratePrintVersion()
return MigratePrintVersion(m)
}
+4 -4
View File
@@ -91,12 +91,12 @@ func RunMigration20() error {
func processMigration20Chunk(ctx context.Context, mchunk []cmdMigration20Type) error {
for _, mig := range mchunk {
newFile, err := scbase.PtyOutFile(mig.ScreenId, mig.LineId)
newFile, err := PtyOutFile(mig.ScreenId, mig.LineId)
if err != nil {
log.Printf("ptyoutfile(lineid) error: %v\n", err)
continue
}
oldFile, err := scbase.PtyOutFile(mig.ScreenId, mig.CmdId)
oldFile, err := PtyOutFile(mig.ScreenId, mig.CmdId)
if err != nil {
log.Printf("ptyoutfile(cmdid) error: %v\n", err)
continue
@@ -142,7 +142,7 @@ func RunMigration13() error {
}
err := os.RemoveAll(scbase.GetSessionsDir())
if err != nil {
return fmt.Errorf("cannot remove old sessions dir %s: %w\n", scbase.GetSessionsDir(), err)
return fmt.Errorf("cannot remove old sessions dir %s: %w", scbase.GetSessionsDir(), err)
}
txErr = WithTx(ctx, func(tx *TxWrap) error {
query := `UPDATE client SET cmdstoretype = 'screen'`
@@ -158,7 +158,7 @@ func RunMigration13() error {
func processMigration13Chunk(ctx context.Context, mchunk []cmdMigration13Type) error {
for _, mig := range mchunk {
newFile, err := scbase.PtyOutFile(mig.ScreenId, mig.CmdId)
newFile, err := PtyOutFile(mig.ScreenId, mig.CmdId)
if err != nil {
log.Printf("ptyoutfile error: %v\n", err)
continue