mirror of
https://github.com/wavetermdev/backup.git
synced 2026-04-22 15:26:58 -07:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e2ff745f55 | |||
| eab8e3be02 | |||
| e589d9c4fa | |||
| 94de0460b4 | |||
| 71b09cb44e | |||
| 07abaa92fa |
@@ -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
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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)
|
||||
);
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user