2022-06-13 20:11:56 +02:00
|
|
|
package sstore
|
|
|
|
|
|
|
|
import (
|
2022-07-01 21:17:19 +02:00
|
|
|
"context"
|
2022-08-16 03:42:25 +02:00
|
|
|
"crypto/ecdsa"
|
|
|
|
"crypto/elliptic"
|
|
|
|
"crypto/rand"
|
|
|
|
"crypto/x509"
|
2022-07-06 01:54:49 +02:00
|
|
|
"database/sql/driver"
|
|
|
|
"fmt"
|
2022-07-01 23:07:13 +02:00
|
|
|
"log"
|
2022-07-01 02:02:19 +02:00
|
|
|
"path"
|
2022-07-07 22:26:46 +02:00
|
|
|
"strings"
|
2022-06-21 06:57:23 +02:00
|
|
|
"sync"
|
2022-06-13 20:11:56 +02:00
|
|
|
"time"
|
|
|
|
|
2022-08-16 03:42:25 +02:00
|
|
|
"github.com/google/uuid"
|
2022-07-01 19:48:14 +02:00
|
|
|
"github.com/jmoiron/sqlx"
|
2022-07-01 21:17:19 +02:00
|
|
|
"github.com/scripthaus-dev/mshell/pkg/base"
|
2022-07-01 19:48:14 +02:00
|
|
|
"github.com/scripthaus-dev/mshell/pkg/packet"
|
2022-07-01 02:02:19 +02:00
|
|
|
"github.com/scripthaus-dev/sh2-server/pkg/scbase"
|
2022-07-01 19:48:14 +02:00
|
|
|
|
|
|
|
_ "github.com/mattn/go-sqlite3"
|
2022-06-13 20:11:56 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
const LineTypeCmd = "cmd"
|
|
|
|
const LineTypeText = "text"
|
2022-07-01 19:48:14 +02:00
|
|
|
const DBFileName = "sh2.db"
|
2022-07-01 02:02:19 +02:00
|
|
|
|
2022-07-01 21:17:19 +02:00
|
|
|
const DefaultSessionName = "default"
|
|
|
|
const DefaultWindowName = "default"
|
|
|
|
const LocalRemoteName = "local"
|
2022-07-13 06:51:17 +02:00
|
|
|
const DefaultScreenWindowName = "w1"
|
2022-07-01 21:17:19 +02:00
|
|
|
|
2022-07-01 23:07:13 +02:00
|
|
|
const DefaultCwd = "~"
|
|
|
|
|
2022-08-16 03:42:25 +02:00
|
|
|
const (
|
|
|
|
CmdStatusRunning = "running"
|
|
|
|
CmdStatusDetached = "detached"
|
|
|
|
CmdStatusError = "error"
|
|
|
|
CmdStatusDone = "done"
|
|
|
|
CmdStatusHangup = "hangup"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
ShareModeLocal = "local"
|
|
|
|
ShareModePrivate = "private"
|
|
|
|
ShareModeView = "view"
|
|
|
|
ShareModeShared = "shared"
|
|
|
|
ShareModeSharedView = "shared-view"
|
|
|
|
)
|
2022-07-08 01:29:14 +02:00
|
|
|
|
2022-07-01 21:17:19 +02:00
|
|
|
var globalDBLock = &sync.Mutex{}
|
|
|
|
var globalDB *sqlx.DB
|
|
|
|
var globalDBErr error
|
|
|
|
|
2022-07-01 19:48:14 +02:00
|
|
|
func GetSessionDBName() string {
|
2022-07-01 02:02:19 +02:00
|
|
|
scHome := scbase.GetScHomeDir()
|
|
|
|
return path.Join(scHome, DBFileName)
|
|
|
|
}
|
2022-06-13 20:11:56 +02:00
|
|
|
|
2022-07-13 01:10:46 +02:00
|
|
|
func GetDB(ctx context.Context) (*sqlx.DB, error) {
|
|
|
|
if IsTxWrapContext(ctx) {
|
|
|
|
return nil, fmt.Errorf("cannot call GetDB from within a running transaction")
|
|
|
|
}
|
2022-07-01 21:17:19 +02:00
|
|
|
globalDBLock.Lock()
|
|
|
|
defer globalDBLock.Unlock()
|
|
|
|
if globalDB == nil && globalDBErr == nil {
|
2022-07-12 22:50:44 +02:00
|
|
|
globalDB, globalDBErr = sqlx.Open("sqlite3", fmt.Sprintf("file:%s?cache=shared&mode=rwc&_journal_mode=WAL&_busy_timeout=5000", GetSessionDBName()))
|
2022-07-01 19:48:14 +02:00
|
|
|
}
|
2022-07-01 21:17:19 +02:00
|
|
|
return globalDB, globalDBErr
|
2022-07-01 19:48:14 +02:00
|
|
|
}
|
|
|
|
|
2022-08-16 03:42:25 +02:00
|
|
|
type UserData struct {
|
|
|
|
UserId string `json:"userid"`
|
|
|
|
UserPrivateKeyBytes []byte `json:"-"`
|
|
|
|
UserPublicKeyBytes []byte `json:"-"`
|
|
|
|
UserPrivateKey *ecdsa.PrivateKey
|
|
|
|
UserPublicKey *ecdsa.PublicKey
|
|
|
|
}
|
|
|
|
|
2022-06-21 06:57:23 +02:00
|
|
|
type SessionType struct {
|
2022-07-13 06:51:17 +02:00
|
|
|
SessionId string `json:"sessionid"`
|
|
|
|
Name string `json:"name"`
|
|
|
|
SessionIdx int64 `json:"sessionidx"`
|
|
|
|
ActiveScreenId string `json:"activescreenid"`
|
|
|
|
NotifyNum int64 `json:"notifynum"`
|
|
|
|
Screens []*ScreenType `json:"screens"`
|
|
|
|
Remotes []*RemoteInstance `json:"remotes"`
|
2022-07-15 03:39:40 +02:00
|
|
|
|
|
|
|
// only for updates
|
|
|
|
Remove bool `json:"remove,omitempty"`
|
|
|
|
Full bool `json:"full,omitempty"`
|
2022-07-12 23:27:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
type WindowOptsType struct {
|
|
|
|
}
|
|
|
|
|
|
|
|
func (opts *WindowOptsType) Scan(val interface{}) error {
|
|
|
|
return quickScanJson(opts, val)
|
|
|
|
}
|
|
|
|
|
2022-07-13 01:10:46 +02:00
|
|
|
func (opts WindowOptsType) Value() (driver.Value, error) {
|
2022-07-12 23:27:16 +02:00
|
|
|
return quickValueJson(opts)
|
2022-07-05 07:18:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
type WindowType struct {
|
2022-08-11 21:07:41 +02:00
|
|
|
SessionId string `json:"sessionid"`
|
|
|
|
WindowId string `json:"windowid"`
|
|
|
|
CurRemote string `json:"curremote"`
|
|
|
|
WinOpts WindowOptsType `json:"winopts"`
|
|
|
|
Lines []*LineType `json:"lines"`
|
|
|
|
Cmds []*CmdType `json:"cmds"`
|
|
|
|
Remotes []*RemoteInstance `json:"remotes"`
|
2022-07-15 03:39:40 +02:00
|
|
|
|
|
|
|
// only for updates
|
|
|
|
Remove bool `json:"remove,omitempty"`
|
2022-07-13 06:51:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
type ScreenOptsType struct {
|
|
|
|
TabColor string `json:"tabcolor"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (opts *ScreenOptsType) Scan(val interface{}) error {
|
|
|
|
return quickScanJson(opts, val)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (opts ScreenOptsType) Value() (driver.Value, error) {
|
|
|
|
return quickValueJson(opts)
|
2022-07-12 23:27:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
type ScreenType struct {
|
2022-07-13 06:51:17 +02:00
|
|
|
SessionId string `json:"sessionid"`
|
|
|
|
ScreenId string `json:"screenid"`
|
|
|
|
ScreenIdx int64 `json:"screenidx"`
|
|
|
|
Name string `json:"name"`
|
|
|
|
ActiveWindowId string `json:"activewindowid"`
|
|
|
|
ScreenOpts ScreenOptsType `json:"screenopts"`
|
|
|
|
Windows []*ScreenWindowType `json:"windows"`
|
2022-07-15 03:39:40 +02:00
|
|
|
|
|
|
|
// only for updates
|
|
|
|
Remove bool `json:"remove,omitempty"`
|
|
|
|
Full bool `json:"full,omitempty"`
|
2022-07-12 23:27:16 +02:00
|
|
|
}
|
|
|
|
|
2022-07-13 06:51:17 +02:00
|
|
|
const (
|
|
|
|
LayoutFull = "full"
|
|
|
|
)
|
|
|
|
|
2022-07-12 23:27:16 +02:00
|
|
|
type LayoutType struct {
|
2022-07-13 06:51:17 +02:00
|
|
|
Type string `json:"type"`
|
|
|
|
Parent string `json:"parent,omitempty"`
|
|
|
|
ZIndex int64 `json:"zindex,omitempty"`
|
|
|
|
Float bool `json:"float,omitempty"`
|
|
|
|
Top string `json:"top,omitempty"`
|
|
|
|
Bottom string `json:"bottom,omitempty"`
|
|
|
|
Left string `json:"left,omitempty"`
|
|
|
|
Right string `json:"right,omitempty"`
|
|
|
|
Width string `json:"width,omitempty"`
|
|
|
|
Height string `json:"height,omitempty"`
|
2022-07-12 23:27:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (l *LayoutType) Scan(val interface{}) error {
|
|
|
|
return quickScanJson(l, val)
|
|
|
|
}
|
|
|
|
|
2022-07-13 01:10:46 +02:00
|
|
|
func (l LayoutType) Value() (driver.Value, error) {
|
2022-07-12 23:27:16 +02:00
|
|
|
return quickValueJson(l)
|
|
|
|
}
|
|
|
|
|
|
|
|
type ScreenWindowType struct {
|
|
|
|
SessionId string `json:"sessionid"`
|
|
|
|
ScreenId string `json:"screenid"`
|
|
|
|
WindowId string `json:"windowid"`
|
2022-07-13 06:51:17 +02:00
|
|
|
Name string `json:"name"`
|
2022-07-12 23:27:16 +02:00
|
|
|
Layout LayoutType `json:"layout"`
|
2022-07-15 03:39:40 +02:00
|
|
|
|
|
|
|
// only for updates
|
|
|
|
Remove bool `json:"remove,omitempty"`
|
2022-07-12 22:50:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
type HistoryItemType struct {
|
2022-08-11 21:07:41 +02:00
|
|
|
HistoryId string `json:"historyid"`
|
|
|
|
Ts int64 `json:"ts"`
|
|
|
|
UserId string `json:"userid"`
|
|
|
|
SessionId string `json:"sessionid"`
|
|
|
|
ScreenId string `json:"screenid"`
|
|
|
|
WindowId string `json:"windowid"`
|
|
|
|
LineId int64 `json:"lineid"`
|
2022-08-12 08:45:15 +02:00
|
|
|
HadError bool `json:"haderror"`
|
2022-08-11 21:07:41 +02:00
|
|
|
CmdId string `json:"cmdid"`
|
|
|
|
CmdStr string `json:"cmdstr"`
|
|
|
|
|
|
|
|
// only for updates
|
|
|
|
Remove bool `json:"remove"`
|
2022-07-01 19:48:14 +02:00
|
|
|
}
|
|
|
|
|
2022-07-06 01:54:49 +02:00
|
|
|
type RemoteState struct {
|
|
|
|
Cwd string `json:"cwd"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *RemoteState) Scan(val interface{}) error {
|
2022-07-12 23:27:16 +02:00
|
|
|
return quickScanJson(s, val)
|
2022-07-06 01:54:49 +02:00
|
|
|
}
|
|
|
|
|
2022-07-13 01:10:46 +02:00
|
|
|
func (s RemoteState) Value() (driver.Value, error) {
|
2022-07-12 23:27:16 +02:00
|
|
|
return quickValueJson(s)
|
2022-07-06 01:54:49 +02:00
|
|
|
}
|
|
|
|
|
2022-07-07 09:10:37 +02:00
|
|
|
type TermOpts struct {
|
2022-07-12 22:50:44 +02:00
|
|
|
Rows int64 `json:"rows"`
|
|
|
|
Cols int64 `json:"cols"`
|
|
|
|
FlexRows bool `json:"flexrows,omitempty"`
|
2022-08-09 01:21:46 +02:00
|
|
|
CmdSize int64 `json:"cmdsize,omitempty"`
|
2022-07-07 09:10:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (opts *TermOpts) Scan(val interface{}) error {
|
2022-07-12 23:27:16 +02:00
|
|
|
return quickScanJson(opts, val)
|
2022-07-07 09:10:37 +02:00
|
|
|
}
|
|
|
|
|
2022-07-13 01:10:46 +02:00
|
|
|
func (opts TermOpts) Value() (driver.Value, error) {
|
2022-07-12 23:27:16 +02:00
|
|
|
return quickValueJson(opts)
|
2022-07-07 09:10:37 +02:00
|
|
|
}
|
|
|
|
|
2022-07-06 01:54:49 +02:00
|
|
|
type RemoteInstance struct {
|
|
|
|
RIId string `json:"riid"`
|
|
|
|
Name string `json:"name"`
|
|
|
|
SessionId string `json:"sessionid"`
|
|
|
|
WindowId string `json:"windowid"`
|
2022-08-11 03:33:32 +02:00
|
|
|
RemoteId string `json:"remoteid"`
|
2022-07-06 01:54:49 +02:00
|
|
|
SessionScope bool `json:"sessionscope"`
|
|
|
|
State RemoteState `json:"state"`
|
2022-08-11 03:33:32 +02:00
|
|
|
|
|
|
|
// only for updates
|
|
|
|
Remove bool `json:"remove,omitempty"`
|
2022-06-21 06:57:23 +02:00
|
|
|
}
|
|
|
|
|
2022-06-13 20:11:56 +02:00
|
|
|
type LineType struct {
|
2022-06-17 00:51:41 +02:00
|
|
|
SessionId string `json:"sessionid"`
|
|
|
|
WindowId string `json:"windowid"`
|
2022-07-12 22:50:44 +02:00
|
|
|
LineId int64 `json:"lineid"`
|
2022-06-13 20:11:56 +02:00
|
|
|
Ts int64 `json:"ts"`
|
|
|
|
UserId string `json:"userid"`
|
|
|
|
LineType string `json:"linetype"`
|
|
|
|
Text string `json:"text,omitempty"`
|
|
|
|
CmdId string `json:"cmdid,omitempty"`
|
2022-07-13 23:16:08 +02:00
|
|
|
Remove bool `json:"remove,omitempty"`
|
2022-07-01 19:48:14 +02:00
|
|
|
}
|
|
|
|
|
2022-07-07 22:26:46 +02:00
|
|
|
type SSHOpts struct {
|
2022-07-02 02:38:36 +02:00
|
|
|
SSHHost string `json:"sshhost"`
|
2022-07-07 22:26:46 +02:00
|
|
|
SSHOptsStr string `json:"sshopts"`
|
2022-07-02 02:38:36 +02:00
|
|
|
SSHIdentity string `json:"sshidentity"`
|
|
|
|
SSHUser string `json:"sshuser"`
|
2022-07-07 22:26:46 +02:00
|
|
|
}
|
2022-07-02 02:38:36 +02:00
|
|
|
|
2022-07-07 22:26:46 +02:00
|
|
|
type RemoteType struct {
|
|
|
|
RemoteId string `json:"remoteid"`
|
|
|
|
RemoteType string `json:"remotetype"`
|
|
|
|
RemoteName string `json:"remotename"`
|
|
|
|
AutoConnect bool `json:"autoconnect"`
|
|
|
|
InitPk *packet.InitPacketType `json:"inipk"`
|
|
|
|
SSHOpts *SSHOpts `json:"sshopts"`
|
|
|
|
LastConnectTs int64 `json:"lastconnectts"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *RemoteType) GetUserHost() (string, string) {
|
|
|
|
if r.SSHOpts == nil {
|
|
|
|
return "", ""
|
|
|
|
}
|
|
|
|
if r.SSHOpts.SSHUser != "" {
|
|
|
|
return r.SSHOpts.SSHUser, r.SSHOpts.SSHHost
|
|
|
|
}
|
|
|
|
atIdx := strings.Index(r.SSHOpts.SSHHost, "@")
|
|
|
|
if atIdx == -1 {
|
|
|
|
return "", r.SSHOpts.SSHHost
|
|
|
|
}
|
|
|
|
return r.SSHOpts.SSHHost[0:atIdx], r.SSHOpts.SSHHost[atIdx+1:]
|
2022-07-01 19:48:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
type CmdType struct {
|
2022-07-07 09:10:37 +02:00
|
|
|
SessionId string `json:"sessionid"`
|
|
|
|
CmdId string `json:"cmdid"`
|
|
|
|
RemoteId string `json:"remoteid"`
|
|
|
|
CmdStr string `json:"cmdstr"`
|
|
|
|
RemoteState RemoteState `json:"remotestate"`
|
|
|
|
TermOpts TermOpts `json:"termopts"`
|
|
|
|
Status string `json:"status"`
|
|
|
|
StartPk *packet.CmdStartPacketType `json:"startpk"`
|
|
|
|
DonePk *packet.CmdDonePacketType `json:"donepk"`
|
2022-07-12 22:50:44 +02:00
|
|
|
UsedRows int64 `json:"usedrows"`
|
2022-07-07 09:10:37 +02:00
|
|
|
RunOut []packet.PacketType `json:"runout"`
|
2022-07-13 23:16:08 +02:00
|
|
|
Remove bool `json:"remove"`
|
2022-07-07 09:10:37 +02:00
|
|
|
}
|
|
|
|
|
2022-07-07 22:26:46 +02:00
|
|
|
func (r *RemoteType) ToMap() map[string]interface{} {
|
|
|
|
rtn := make(map[string]interface{})
|
|
|
|
rtn["remoteid"] = r.RemoteId
|
|
|
|
rtn["remotetype"] = r.RemoteType
|
|
|
|
rtn["remotename"] = r.RemoteName
|
|
|
|
rtn["autoconnect"] = r.AutoConnect
|
|
|
|
rtn["initpk"] = quickJson(r.InitPk)
|
|
|
|
rtn["sshopts"] = quickJson(r.SSHOpts)
|
|
|
|
rtn["lastconnectts"] = r.LastConnectTs
|
|
|
|
return rtn
|
|
|
|
}
|
|
|
|
|
|
|
|
func RemoteFromMap(m map[string]interface{}) *RemoteType {
|
|
|
|
if len(m) == 0 {
|
|
|
|
return nil
|
2022-07-07 09:10:37 +02:00
|
|
|
}
|
2022-07-07 22:26:46 +02:00
|
|
|
var r RemoteType
|
|
|
|
quickSetStr(&r.RemoteId, m, "remoteid")
|
|
|
|
quickSetStr(&r.RemoteType, m, "remotetype")
|
|
|
|
quickSetStr(&r.RemoteName, m, "remotename")
|
|
|
|
quickSetBool(&r.AutoConnect, m, "autoconnect")
|
|
|
|
quickSetJson(&r.InitPk, m, "initpk")
|
|
|
|
quickSetJson(&r.SSHOpts, m, "sshopts")
|
|
|
|
quickSetInt64(&r.LastConnectTs, m, "lastconnectts")
|
|
|
|
return &r
|
2022-07-07 09:10:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (cmd *CmdType) ToMap() map[string]interface{} {
|
|
|
|
rtn := make(map[string]interface{})
|
|
|
|
rtn["sessionid"] = cmd.SessionId
|
|
|
|
rtn["cmdid"] = cmd.CmdId
|
|
|
|
rtn["remoteid"] = cmd.RemoteId
|
|
|
|
rtn["cmdstr"] = cmd.CmdStr
|
|
|
|
rtn["remotestate"] = quickJson(cmd.RemoteState)
|
|
|
|
rtn["termopts"] = quickJson(cmd.TermOpts)
|
|
|
|
rtn["status"] = cmd.Status
|
|
|
|
rtn["startpk"] = quickJson(cmd.StartPk)
|
|
|
|
rtn["donepk"] = quickJson(cmd.DonePk)
|
|
|
|
rtn["runout"] = quickJson(cmd.RunOut)
|
2022-07-12 22:50:44 +02:00
|
|
|
rtn["usedrows"] = cmd.UsedRows
|
2022-07-07 09:10:37 +02:00
|
|
|
return rtn
|
|
|
|
}
|
|
|
|
|
|
|
|
func CmdFromMap(m map[string]interface{}) *CmdType {
|
2022-07-07 22:26:46 +02:00
|
|
|
if len(m) == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
2022-07-07 09:10:37 +02:00
|
|
|
var cmd CmdType
|
|
|
|
quickSetStr(&cmd.SessionId, m, "sessionid")
|
|
|
|
quickSetStr(&cmd.CmdId, m, "cmdid")
|
|
|
|
quickSetStr(&cmd.RemoteId, m, "remoteid")
|
|
|
|
quickSetStr(&cmd.CmdStr, m, "cmdstr")
|
|
|
|
quickSetJson(&cmd.RemoteState, m, "remotestate")
|
|
|
|
quickSetJson(&cmd.TermOpts, m, "termopts")
|
|
|
|
quickSetStr(&cmd.Status, m, "status")
|
|
|
|
quickSetJson(&cmd.StartPk, m, "startpk")
|
|
|
|
quickSetJson(&cmd.DonePk, m, "donepk")
|
|
|
|
quickSetJson(&cmd.RunOut, m, "runout")
|
2022-07-12 22:50:44 +02:00
|
|
|
quickSetInt64(&cmd.UsedRows, m, "usedrows")
|
2022-07-07 09:10:37 +02:00
|
|
|
return &cmd
|
2022-06-13 20:11:56 +02:00
|
|
|
}
|
|
|
|
|
2022-07-08 06:39:25 +02:00
|
|
|
func makeNewLineCmd(sessionId string, windowId string, userId string, cmdId string) *LineType {
|
2022-06-13 20:11:56 +02:00
|
|
|
rtn := &LineType{}
|
2022-06-17 00:51:41 +02:00
|
|
|
rtn.SessionId = sessionId
|
|
|
|
rtn.WindowId = windowId
|
2022-06-13 20:11:56 +02:00
|
|
|
rtn.Ts = time.Now().UnixMilli()
|
2022-07-05 19:51:47 +02:00
|
|
|
rtn.UserId = userId
|
2022-06-13 20:11:56 +02:00
|
|
|
rtn.LineType = LineTypeCmd
|
2022-07-08 06:39:25 +02:00
|
|
|
rtn.CmdId = cmdId
|
2022-06-13 20:11:56 +02:00
|
|
|
return rtn
|
|
|
|
}
|
|
|
|
|
2022-07-05 19:51:47 +02:00
|
|
|
func makeNewLineText(sessionId string, windowId string, userId string, text string) *LineType {
|
2022-06-13 20:11:56 +02:00
|
|
|
rtn := &LineType{}
|
2022-06-17 00:51:41 +02:00
|
|
|
rtn.SessionId = sessionId
|
|
|
|
rtn.WindowId = windowId
|
2022-06-13 20:11:56 +02:00
|
|
|
rtn.Ts = time.Now().UnixMilli()
|
2022-07-05 19:51:47 +02:00
|
|
|
rtn.UserId = userId
|
2022-06-13 20:11:56 +02:00
|
|
|
rtn.LineType = LineTypeText
|
|
|
|
rtn.Text = text
|
|
|
|
return rtn
|
|
|
|
}
|
2022-06-21 06:57:23 +02:00
|
|
|
|
2022-07-05 19:51:47 +02:00
|
|
|
func AddCommentLine(ctx context.Context, sessionId string, windowId string, userId string, commentText string) (*LineType, error) {
|
|
|
|
rtnLine := makeNewLineText(sessionId, windowId, userId, commentText)
|
2022-07-08 06:39:25 +02:00
|
|
|
err := InsertLine(ctx, rtnLine, nil)
|
2022-07-05 19:51:47 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return rtnLine, nil
|
|
|
|
}
|
|
|
|
|
2022-07-08 06:39:25 +02:00
|
|
|
func AddCmdLine(ctx context.Context, sessionId string, windowId string, userId string, cmd *CmdType) (*LineType, error) {
|
|
|
|
rtnLine := makeNewLineCmd(sessionId, windowId, userId, cmd.CmdId)
|
|
|
|
err := InsertLine(ctx, rtnLine, cmd)
|
2022-07-05 19:51:47 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return rtnLine, nil
|
|
|
|
}
|
|
|
|
|
2022-07-01 21:17:19 +02:00
|
|
|
func EnsureLocalRemote(ctx context.Context) error {
|
|
|
|
remoteId, err := base.GetRemoteId()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
remote, err := GetRemoteById(ctx, remoteId)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if remote != nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
// create the local remote
|
|
|
|
localRemote := &RemoteType{
|
2022-07-02 02:38:36 +02:00
|
|
|
RemoteId: remoteId,
|
|
|
|
RemoteType: "ssh",
|
|
|
|
RemoteName: LocalRemoteName,
|
|
|
|
AutoConnect: true,
|
2022-07-01 21:17:19 +02:00
|
|
|
}
|
|
|
|
err = InsertRemote(ctx, localRemote)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2022-07-01 23:07:13 +02:00
|
|
|
log.Printf("[db] added remote '%s', id=%s\n", localRemote.RemoteName, localRemote.RemoteId)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-07-01 23:45:33 +02:00
|
|
|
func EnsureDefaultSession(ctx context.Context) (*SessionType, error) {
|
2022-07-01 23:07:13 +02:00
|
|
|
session, err := GetSessionByName(ctx, DefaultSessionName)
|
|
|
|
if err != nil {
|
2022-07-01 23:45:33 +02:00
|
|
|
return nil, err
|
2022-07-01 23:07:13 +02:00
|
|
|
}
|
|
|
|
if session != nil {
|
2022-07-01 23:45:33 +02:00
|
|
|
return session, nil
|
2022-07-01 23:07:13 +02:00
|
|
|
}
|
2022-08-09 01:21:46 +02:00
|
|
|
_, err = InsertSessionWithName(ctx, DefaultSessionName, true)
|
2022-07-01 23:07:13 +02:00
|
|
|
if err != nil {
|
2022-07-01 23:45:33 +02:00
|
|
|
return nil, err
|
2022-07-01 23:07:13 +02:00
|
|
|
}
|
2022-07-01 23:45:33 +02:00
|
|
|
return GetSessionByName(ctx, DefaultSessionName)
|
2022-07-01 21:17:19 +02:00
|
|
|
}
|
2022-08-16 03:42:25 +02:00
|
|
|
|
|
|
|
func createUserData(tx *TxWrap) error {
|
|
|
|
userId := uuid.New().String()
|
|
|
|
curve := elliptic.P384()
|
|
|
|
pkey, err := ecdsa.GenerateKey(curve, rand.Reader)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("generating P-834 key: %w", err)
|
|
|
|
}
|
|
|
|
pkBytes, err := x509.MarshalECPrivateKey(pkey)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("marshaling (pkcs8) private key bytes: %w", err)
|
|
|
|
}
|
|
|
|
pubBytes, err := x509.MarshalPKIXPublicKey(&pkey.PublicKey)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("marshaling (pkix) public key bytes: %w", err)
|
|
|
|
}
|
|
|
|
query := `INSERT INTO client (userid, userpublickeybytes, userprivatekeybytes) VALUES (?, ?, ?)`
|
|
|
|
tx.ExecWrap(query, userId, pubBytes, pkBytes)
|
|
|
|
fmt.Printf("create new userid[%s] with public/private keypair\n", userId)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func EnsureUserData(ctx context.Context) (*UserData, error) {
|
|
|
|
var rtn UserData
|
|
|
|
err := WithTx(ctx, func(tx *TxWrap) error {
|
|
|
|
query := `SELECT count(*) FROM client`
|
|
|
|
count := tx.GetInt(query)
|
|
|
|
if count > 1 {
|
|
|
|
return fmt.Errorf("invalid client database, multiple (%d) rows in client table", count)
|
|
|
|
}
|
|
|
|
if count == 0 {
|
|
|
|
createErr := createUserData(tx)
|
|
|
|
if createErr != nil {
|
|
|
|
return createErr
|
|
|
|
}
|
|
|
|
}
|
|
|
|
found := tx.GetWrap(&rtn, "SELECT * FROM client")
|
|
|
|
if !found {
|
|
|
|
return fmt.Errorf("invalid client data")
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if rtn.UserId == "" {
|
|
|
|
return nil, fmt.Errorf("invalid client data (no userid)")
|
|
|
|
}
|
|
|
|
if len(rtn.UserPrivateKeyBytes) == 0 || len(rtn.UserPublicKeyBytes) == 0 {
|
|
|
|
return nil, fmt.Errorf("invalid client data (no public/private keypair)")
|
|
|
|
}
|
|
|
|
rtn.UserPrivateKey, err = x509.ParseECPrivateKey(rtn.UserPrivateKeyBytes)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("invalid client data, cannot parse private key: %w", err)
|
|
|
|
}
|
|
|
|
pubKey, err := x509.ParsePKIXPublicKey(rtn.UserPublicKeyBytes)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("invalid client data, cannot parse public key: %w", err)
|
|
|
|
}
|
|
|
|
var ok bool
|
|
|
|
rtn.UserPublicKey, ok = pubKey.(*ecdsa.PublicKey)
|
|
|
|
if !ok {
|
|
|
|
return nil, fmt.Errorf("invalid client data, wrong public key type: %T", pubKey)
|
|
|
|
}
|
|
|
|
return &rtn, nil
|
|
|
|
}
|