merge branch 'main' into sylvie/preview-dir

This commit is contained in:
Sylvia Crowe 2024-05-21 13:49:49 -07:00
commit 259bbf44db
25 changed files with 335 additions and 125 deletions

View File

@ -346,7 +346,8 @@ tasks:
method: timestamp
cmds:
# Generates both .ico and .icns files
- wails3 generate icons -input appicon.png
# commented out for now
# - wails3 generate icons -input appicon.png
install:frontend:deps:
summary: Install frontend dependencies

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 387 KiB

BIN
build/icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

View File

@ -0,0 +1,8 @@
DROP TABLE db_client;
DROP TABLE db_workspace;
DROP TABLE db_tab;
DROP TABLE db_block;

View File

@ -0,0 +1,20 @@
CREATE TABLE db_client (
clientid varchar(36) PRIMARY KEY, -- unnecessary, but useful to have a PK
data json NOT NULL
);
CREATE TABLE db_workspace (
workspaceid varchar(36) PRIMARY KEY,
data json NOT NULL
);
CREATE TABLE db_tab (
tabid varchar(36) PRIMARY KEY,
data json NOT NULL
);
CREATE TABLE db_block (
blockid varchar(36) PRIMARY KEY,
tabid varchar(36) NOT NULL, -- the tab this block belongs to
data json NOT NULL
);

View File

@ -8,7 +8,7 @@ import * as rxjs from "rxjs";
import type { WailsEvent } from "@wailsio/runtime/types/events";
import { Events } from "@wailsio/runtime";
import { produce } from "immer";
import * as BlockService from "@/bindings/pkg/service/blockservice/BlockService";
import { BlockService } from "@/bindings/blockservice";
const globalStore = jotai.createStore();

View File

@ -5,7 +5,7 @@ import * as React from "react";
import * as jotai from "jotai";
import { atoms, blockDataMap, useBlockAtom } from "@/store/global";
import { Markdown } from "@/element/markdown";
import * as FileService from "@/bindings/pkg/service/fileservice/FileService";
import { FileService, FileInfo, FullFile } from "@/bindings/fileservice";
import * as util from "@/util/util";
import { CenteredDiv } from "../element/quickelems";
import { DirectoryTable } from "@/element/directorytable";

View File

@ -7,7 +7,7 @@ import { Terminal } from "@xterm/xterm";
import type { ITheme } from "@xterm/xterm";
import { FitAddon } from "@xterm/addon-fit";
import { Button } from "@/element/button";
import * as BlockService from "@/bindings/pkg/service/blockservice/BlockService";
import { BlockService } from "@/bindings/blockservice";
import { getBlockSubject } from "@/store/global";
import { base64ToArray } from "@/util/util";

View File

@ -7,7 +7,7 @@ import { TabContent } from "@/app/tab/tab";
import { clsx } from "clsx";
import { atoms, addBlockIdToTab, blockDataMap } from "@/store/global";
import { v4 as uuidv4 } from "uuid";
import * as BlockService from "@/bindings/pkg/service/blockservice/BlockService";
import { BlockService } from "@/bindings/blockservice";
import "./workspace.less";
@ -49,7 +49,7 @@ function Widgets() {
async function createBlock(blockDef: BlockDef) {
const rtOpts = { termsize: { rows: 25, cols: 80 } };
const rtnBlock: BlockData = await BlockService.CreateBlock(blockDef, rtOpts);
const rtnBlock: BlockData = (await BlockService.CreateBlock(blockDef, rtOpts)) as BlockData;
const newBlockAtom = jotai.atom(rtnBlock);
blockDataMap.set(rtnBlock.blockid, newBlockAtom);
addBlockIdToTab(activeTabId, rtnBlock.blockid);

View File

@ -33,21 +33,6 @@ declare global {
files?: FileDef[];
meta?: MetaDataType;
};
type FileInfo = {
path: string;
notfound: boolean;
size: number;
mode: number;
modtime: number;
isdir: boolean;
mimetype: string;
};
type FullFile = {
info: FileInfo;
data64: string;
};
}
export {};

11
go.mod
View File

@ -13,6 +13,7 @@ require (
github.com/sawka/txwrap v0.2.0
github.com/wailsapp/wails/v3 v3.0.0-alpha.0
github.com/wavetermdev/waveterm/wavesrv v0.0.0-20240508181017-d07068c09d94
golang.org/x/sys v0.20.0
)
require (
@ -51,12 +52,12 @@ require (
github.com/wailsapp/mimetype v1.4.1 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
go.uber.org/atomic v1.7.0 // indirect
golang.org/x/crypto v0.21.0 // indirect
golang.org/x/crypto v0.23.0 // indirect
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df // indirect
golang.org/x/mod v0.12.0 // indirect
golang.org/x/net v0.21.0 // indirect
golang.org/x/sys v0.19.0 // indirect
golang.org/x/tools v0.13.0 // indirect
golang.org/x/mod v0.17.0 // indirect
golang.org/x/net v0.25.0 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/tools v0.21.0 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
)

28
go.sum
View File

@ -137,14 +137,14 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME=
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
@ -153,8 +153,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -177,15 +177,15 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q=
golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
@ -193,14 +193,14 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw=
golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

16
main.go
View File

@ -9,6 +9,7 @@ import (
"embed"
"log"
"net/http"
"runtime"
"strings"
"github.com/wavetermdev/thenextwave/pkg/blockstore"
@ -24,7 +25,7 @@ import (
//go:embed dist
var assets embed.FS
//go:embed build/appicon.png
//go:embed build/icons.icns
var appIcon []byte
func createAppMenu(app *application.App) *application.Menu {
@ -91,6 +92,12 @@ func main() {
log.Printf("error ensuring wave home dir: %v\n", err)
return
}
waveLock, err := wavebase.AcquireWaveLock()
if err != nil {
log.Printf("error acquiring wave lock (another instance of Wave is likely running): %v\n", err)
return
}
log.Printf("wave home dir: %s\n", wavebase.GetWaveHomeDir())
err = blockstore.InitBlockstore()
if err != nil {
@ -101,9 +108,9 @@ func main() {
app := application.New(application.Options{
Name: "NextWave",
Description: "The Next Wave Terminal",
Bind: []any{
&fileservice.FileService{},
&blockservice.BlockService{},
Services: []application.Service{
application.NewService(&fileservice.FileService{}),
application.NewService(&blockservice.BlockService{}),
},
Icon: appIcon,
Assets: application.AssetOptions{
@ -129,4 +136,5 @@ func main() {
if err != nil {
log.Printf("run error: %v\n", err)
}
runtime.KeepAlive(waveLock)
}

View File

@ -13,7 +13,7 @@
"@types/react": "^18.3.2",
"@types/uuid": "^9.0.8",
"@vitejs/plugin-react": "^4.2.1",
"@wailsio/runtime": "latest",
"@wailsio/runtime": "^3.0.0-alpha.24",
"less": "^4.2.0",
"vite": "^5.0.0",
"vite-tsconfig-paths": "^4.3.2"

View File

@ -16,6 +16,7 @@ import (
"github.com/wailsapp/wails/v3/pkg/application"
"github.com/wavetermdev/thenextwave/pkg/eventbus"
"github.com/wavetermdev/thenextwave/pkg/shellexec"
"github.com/wavetermdev/thenextwave/pkg/wstore"
)
const (
@ -25,48 +26,11 @@ const (
var globalLock = &sync.Mutex{}
var blockControllerMap = make(map[string]*BlockController)
var blockDataMap = make(map[string]*BlockData)
type BlockData struct {
Lock *sync.Mutex `json:"-"`
BlockId string `json:"blockid"`
BlockDef *BlockDef `json:"blockdef"`
Controller string `json:"controller"`
ControllerStatus string `json:"controllerstatus"`
View string `json:"view"`
Meta map[string]any `json:"meta,omitempty"`
RuntimeOpts *RuntimeOpts `json:"runtimeopts,omitempty"`
}
type FileDef struct {
FileType string `json:"filetype,omitempty"`
Path string `json:"path,omitempty"`
Url string `json:"url,omitempty"`
Content string `json:"content,omitempty"`
Meta map[string]any `json:"meta,omitempty"`
}
type BlockDef struct {
Controller string `json:"controller"`
View string `json:"view,omitempty"`
Files map[string]*FileDef `json:"files,omitempty"`
Meta map[string]any `json:"meta,omitempty"`
}
type WinSize struct {
Width int `json:"width"`
Height int `json:"height"`
}
type RuntimeOpts struct {
TermSize shellexec.TermSize `json:"termsize,omitempty"`
WinSize WinSize `json:"winsize,omitempty"`
}
type BlockController struct {
Lock *sync.Mutex
BlockId string
BlockDef *BlockDef
BlockDef *wstore.BlockDef
InputCh chan BlockCommand
ShellProc *shellexec.ShellProc
@ -86,9 +50,9 @@ func jsonDeepCopy(val map[string]any) (map[string]any, error) {
return rtn, nil
}
func CreateBlock(bdef *BlockDef, rtOpts *RuntimeOpts) (*BlockData, error) {
func CreateBlock(bdef *wstore.BlockDef, rtOpts *wstore.RuntimeOpts) (*wstore.Block, error) {
blockId := uuid.New().String()
blockData := &BlockData{
blockData := &wstore.Block{
Lock: &sync.Mutex{},
BlockId: blockId,
BlockDef: bdef,
@ -101,7 +65,7 @@ func CreateBlock(bdef *BlockDef, rtOpts *RuntimeOpts) (*BlockData, error) {
if err != nil {
return nil, fmt.Errorf("error copying meta: %w", err)
}
setBlockData(blockData)
wstore.BlockMap.Set(blockId, blockData)
if blockData.Controller != "" {
StartBlockController(blockId, blockData)
}
@ -115,25 +79,7 @@ func CloseBlock(blockId string) {
}
bc.Close()
close(bc.InputCh)
removeBlockData(blockId)
}
func GetBlockData(blockId string) *BlockData {
globalLock.Lock()
defer globalLock.Unlock()
return blockDataMap[blockId]
}
func setBlockData(bd *BlockData) {
globalLock.Lock()
defer globalLock.Unlock()
blockDataMap[bd.BlockId] = bd
}
func removeBlockData(blockId string) {
globalLock.Lock()
defer globalLock.Unlock()
delete(blockDataMap, blockId)
wstore.BlockMap.Delete(blockId)
}
func (bc *BlockController) setShellProc(shellProc *shellexec.ShellProc) error {
@ -231,7 +177,7 @@ func (bc *BlockController) DoRunShellCommand(rc *RunShellOpts) error {
return nil
}
func (bc *BlockController) Run(bdata *BlockData) {
func (bc *BlockController) Run(bdata *wstore.Block) {
defer func() {
bdata.WithLock(func() {
// if the controller had an error status, don't change it
@ -272,13 +218,7 @@ func (bc *BlockController) Run(bdata *BlockData) {
}
}
func (b *BlockData) WithLock(f func()) {
b.Lock.Lock()
defer b.Lock.Unlock()
f()
}
func StartBlockController(blockId string, bdata *BlockData) {
func StartBlockController(blockId string, bdata *wstore.Block) {
if bdata.Controller != BlockController_Shell {
log.Printf("unknown controller %q\n", bdata.Controller)
bdata.WithLock(func() {
@ -312,7 +252,7 @@ func ProcessStaticCommand(blockId string, cmdGen BlockCommand) {
log.Printf("MESSAGE: %s | %q\n", blockId, cmd.Message)
case *SetViewCommand:
log.Printf("SETVIEW: %s | %q\n", blockId, cmd.View)
block := GetBlockData(blockId)
block := wstore.BlockMap.Get(blockId)
if block != nil {
block.WithLock(func() {
block.View = cmd.View
@ -320,7 +260,7 @@ func ProcessStaticCommand(blockId string, cmdGen BlockCommand) {
}
case *SetMetaCommand:
log.Printf("SETMETA: %s | %v\n", blockId, cmd.Meta)
block := GetBlockData(blockId)
block := wstore.BlockMap.Get(blockId)
if block != nil {
block.WithLock(func() {
for k, v := range cmd.Meta {

View File

@ -1,3 +1,6 @@
// Copyright 2024, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
package blockstore
import (

View File

@ -1,3 +1,6 @@
// Copyright 2024, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
package blockstore
// setup for blockstore db
@ -22,7 +25,7 @@ import (
dbfs "github.com/wavetermdev/thenextwave/db"
)
const BlockstoreDbName = "blockstore.db"
const BlockstoreDBName = "blockstore.db"
type TxWrap = txwrap.TxWrap
@ -46,8 +49,8 @@ func InitBlockstore() error {
}
func GetDBName() string {
scHome := wavebase.GetWaveHomeDir()
return path.Join(scHome, BlockstoreDbName)
waveHome := wavebase.GetWaveHomeDir()
return path.Join(waveHome, BlockstoreDBName)
}
func MakeDB(ctx context.Context) (*sqlx.DB, error) {
@ -60,7 +63,7 @@ func MakeDB(ctx context.Context) (*sqlx.DB, error) {
} else {
dbName := GetDBName()
log.Printf("[db] opening db %s\n", dbName)
rtn, err = sqlx.Open("sqlite3", fmt.Sprintf("file:%s?cache=shared&mode=rwc&_journal_mode=WAL&_busy_timeout=5000", dbName))
rtn, err = sqlx.Open("sqlite3", fmt.Sprintf("file:%s?mode=rwc&_journal_mode=WAL&_busy_timeout=5000", dbName))
}
if err != nil {
return nil, fmt.Errorf("opening db: %w", err)

View File

@ -9,17 +9,18 @@ import (
"github.com/wavetermdev/thenextwave/pkg/blockcontroller"
"github.com/wavetermdev/thenextwave/pkg/util/utilfn"
"github.com/wavetermdev/thenextwave/pkg/wstore"
)
type BlockService struct{}
func (bs *BlockService) CreateBlock(bdefMap map[string]any, rtOptsMap map[string]any) (map[string]any, error) {
var bdef blockcontroller.BlockDef
var bdef wstore.BlockDef
err := utilfn.JsonMapToStruct(bdefMap, &bdef)
if err != nil {
return nil, fmt.Errorf("error unmarshalling BlockDef: %w", err)
}
var rtOpts blockcontroller.RuntimeOpts
var rtOpts wstore.RuntimeOpts
err = utilfn.JsonMapToStruct(rtOptsMap, &rtOpts)
if err != nil {
return nil, fmt.Errorf("error unmarshalling RuntimeOpts: %w", err)
@ -40,7 +41,7 @@ func (bs *BlockService) CloseBlock(blockId string) {
}
func (bs *BlockService) GetBlockData(blockId string) (map[string]any, error) {
blockData := blockcontroller.GetBlockData(blockId)
blockData := wstore.BlockMap.Get(blockId)
if blockData == nil {
return nil, nil
}

43
pkg/util/ds/syncmap.go Normal file
View File

@ -0,0 +1,43 @@
// Copyright 2024, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
package ds
import "sync"
type SyncMap[T any] struct {
lock *sync.Mutex
m map[string]T
}
func NewSyncMap[T any]() *SyncMap[T] {
return &SyncMap[T]{
lock: &sync.Mutex{},
m: make(map[string]T),
}
}
func (sm *SyncMap[T]) Set(key string, value T) {
sm.lock.Lock()
defer sm.lock.Unlock()
sm.m[key] = value
}
func (sm *SyncMap[T]) Get(key string) T {
sm.lock.Lock()
defer sm.lock.Unlock()
return sm.m[key]
}
func (sm *SyncMap[T]) GetEx(key string) (T, bool) {
sm.lock.Lock()
defer sm.lock.Unlock()
v, ok := sm.m[key]
return v, ok
}
func (sm *SyncMap[T]) Delete(key string) {
sm.lock.Lock()
defer sm.lock.Unlock()
delete(sm.m, key)
}

View File

@ -12,10 +12,13 @@ import (
"os"
"os/exec"
"path"
"path/filepath"
"runtime"
"strings"
"sync"
"time"
"golang.org/x/sys/unix"
)
const WaveVersion = "v0.1.0"
@ -23,6 +26,7 @@ const DefaultWaveHome = "~/.w2"
const WaveHomeVarName = "WAVETERM_HOME"
const WaveDevVarName = "WAVETERM_DEV"
const HomeVarName = "HOME"
const WaveLockFile = "waveterm.lock"
var baseLock = &sync.Mutex{}
var ensureDirCache = map[string]bool{}
@ -137,3 +141,19 @@ func DetermineLang() string {
})
return osLang
}
func AcquireWaveLock() (*os.File, error) {
homeDir := GetWaveHomeDir()
lockFileName := filepath.Join(homeDir, WaveLockFile)
log.Printf("[base] acquiring lock on %s\n", lockFileName)
fd, err := os.OpenFile(lockFileName, os.O_RDWR|os.O_CREATE, 0600)
if err != nil {
return nil, err
}
err = unix.Flock(int(fd.Fd()), unix.LOCK_EX|unix.LOCK_NB)
if err != nil {
fd.Close()
return nil, err
}
return fd, nil
}

120
pkg/wstore/wstore.go Normal file
View File

@ -0,0 +1,120 @@
// Copyright 2024, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
package wstore
import (
"fmt"
"sync"
"github.com/google/uuid"
"github.com/wavetermdev/thenextwave/pkg/shellexec"
"github.com/wavetermdev/thenextwave/pkg/util/ds"
)
var WorkspaceMap = ds.NewSyncMap[*Workspace]()
var TabMap = ds.NewSyncMap[*Tab]()
var BlockMap = ds.NewSyncMap[*Block]()
type Client struct {
DefaultWorkspaceId string `json:"defaultworkspaceid"`
}
type Workspace struct {
Lock *sync.Mutex `json:"-"`
WorkspaceId string `json:"workspaceid"`
TabIds []string `json:"tabids"`
}
func (ws *Workspace) WithLock(f func()) {
ws.Lock.Lock()
defer ws.Lock.Unlock()
f()
}
type Tab struct {
Lock *sync.Mutex `json:"-"`
TabId string `json:"tabid"`
Name string `json:"name"`
BlockIds []string `json:"blockids"`
}
func (tab *Tab) WithLock(f func()) {
tab.Lock.Lock()
defer tab.Lock.Unlock()
f()
}
type FileDef struct {
FileType string `json:"filetype,omitempty"`
Path string `json:"path,omitempty"`
Url string `json:"url,omitempty"`
Content string `json:"content,omitempty"`
Meta map[string]any `json:"meta,omitempty"`
}
type BlockDef struct {
Controller string `json:"controller"`
View string `json:"view,omitempty"`
Files map[string]*FileDef `json:"files,omitempty"`
Meta map[string]any `json:"meta,omitempty"`
}
type RuntimeOpts struct {
TermSize shellexec.TermSize `json:"termsize,omitempty"`
WinSize WinSize `json:"winsize,omitempty"`
}
type WinSize struct {
Width int `json:"width"`
Height int `json:"height"`
}
type Block struct {
Lock *sync.Mutex `json:"-"`
BlockId string `json:"blockid"`
BlockDef *BlockDef `json:"blockdef"`
Controller string `json:"controller"`
ControllerStatus string `json:"controllerstatus"`
View string `json:"view"`
Meta map[string]any `json:"meta,omitempty"`
RuntimeOpts *RuntimeOpts `json:"runtimeopts,omitempty"`
}
func (b *Block) WithLock(f func()) {
b.Lock.Lock()
defer b.Lock.Unlock()
f()
}
func CreateTab(workspaceId string, name string) (*Tab, error) {
tab := &Tab{
Lock: &sync.Mutex{},
TabId: uuid.New().String(),
Name: name,
BlockIds: []string{},
}
TabMap.Set(tab.TabId, tab)
ws := WorkspaceMap.Get(workspaceId)
if ws == nil {
return nil, fmt.Errorf("workspace not found: %q", workspaceId)
}
ws.WithLock(func() {
ws.TabIds = append(ws.TabIds, tab.TabId)
})
return tab, nil
}
func CreateWorkspace() (*Workspace, error) {
ws := &Workspace{
Lock: &sync.Mutex{},
WorkspaceId: uuid.New().String(),
TabIds: []string{},
}
WorkspaceMap.Set(ws.WorkspaceId, ws)
_, err := CreateTab(ws.WorkspaceId, "Tab 1")
if err != nil {
return nil, err
}
return ws, nil
}

View File

@ -0,0 +1,57 @@
// Copyright 2024, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
package wstore
import (
"context"
"fmt"
"log"
"path"
"time"
"github.com/jmoiron/sqlx"
"github.com/sawka/txwrap"
"github.com/wavetermdev/thenextwave/pkg/wavebase"
)
const WStoreDBName = "waveterm.db"
type TxWrap = txwrap.TxWrap
var globalDB *sqlx.DB
func InitWStore() error {
ctx, cancelFn := context.WithTimeout(context.Background(), 2*time.Second)
defer cancelFn()
var err error
globalDB, err = MakeDB(ctx)
if err != nil {
return err
}
err = MigrateWStore()
if err != nil {
return err
}
log.Printf("wstore initialized\n")
return nil
}
func GetDBName() string {
waveHome := wavebase.GetWaveHomeDir()
return path.Join(waveHome, WStoreDBName)
}
func MakeDB(ctx context.Context) (*sqlx.DB, error) {
dbName := GetDBName()
rtn, err := sqlx.Open("sqlite3", fmt.Sprintf("file:%s?mode=rwc&_journal_mode=WAL&_busy_timeout=5000", dbName))
if err != nil {
return nil, err
}
rtn.DB.SetMaxOpenConns(1)
return rtn, nil
}
func MigrateWStore() error {
return nil
}

View File

@ -19,7 +19,7 @@
"@/util/*": ["frontend/util/*"],
"@/store/*": ["frontend/app/store/*"],
"@/element/*": ["frontend/app/element/*"],
"@/bindings/*": ["frontend/bindings/*"],
"@/bindings/*": ["frontend/bindings/github.com/wavetermdev/thenextwave/pkg/service/*"],
}
}
}

View File

@ -572,9 +572,9 @@
"@types/babel__core" "^7.20.5"
react-refresh "^0.14.0"
"@wailsio/runtime@latest":
"@wailsio/runtime@^3.0.0-alpha.24":
version "3.0.0-alpha.24"
resolved "https://registry.npmjs.org/@wailsio/runtime/-/runtime-3.0.0-alpha.24.tgz"
resolved "https://registry.yarnpkg.com/@wailsio/runtime/-/runtime-3.0.0-alpha.24.tgz#87d8465a08fbfc7e3131cd8f8832601fb2a2c99c"
integrity sha512-bd36Qj6CfIMPeUd+fhLpcb9O/XEUEOgLzelkHw45lk0OWHMRlTV5QYdAT3cBncvztH7xYijoN5v2+O3U/Jwreg==
dependencies:
nanoid "^5.0.7"