From f9589c54b0689b129bbecd8388b334585e4ae274 Mon Sep 17 00:00:00 2001 From: Paras Waykole Date: Sun, 9 Apr 2023 23:00:01 +0530 Subject: [PATCH 01/29] seperating go code into common, desktop and server --- Makefile | 4 +- go.mod | 30 +++++--- go.sum | 76 ++++++++++++++----- internal/{ => common}/analytics/analytics.go | 6 +- internal/{ => common}/config/config.go | 12 +-- internal/common/config/constants.go | 20 +++++ internal/{ => common}/config/model.go | 8 +- internal/{ => common}/console/console.go | 2 +- internal/{ => common}/controllers/console.go | 4 +- .../{ => common}/controllers/dbconnection.go | 4 +- .../controllers/helper_functions.go | 4 +- internal/{ => common}/controllers/project.go | 4 +- internal/{ => common}/controllers/query.go | 6 +- internal/{ => common}/controllers/setting.go | 8 +- internal/{ => common}/controllers/tabs.go | 6 +- internal/{ => common}/dao/dbconnection.go | 4 +- internal/{ => common}/dao/dbquerylog.go | 6 +- internal/{ => common}/dao/project.go | 4 +- internal/{ => common}/dao/query.go | 4 +- internal/{ => common}/dao/setting.go | 4 +- internal/{ => common}/dao/tabs.go | 4 +- internal/{ => common}/db/db.go | 2 +- internal/{ => common}/models/dbconnection.go | 4 +- internal/{ => common}/models/dbquery.go | 0 internal/{ => common}/models/dbquerylog.go | 0 internal/{ => common}/models/project.go | 0 internal/{ => common}/models/setting.go | 0 internal/{ => common}/models/tabs.go | 2 +- internal/{ => common}/tasks/cron.go | 4 +- internal/{ => common}/utils/utils.go | 0 internal/{ => common}/views/db_connection.go | 2 +- internal/{ => common}/views/project.go | 2 +- internal/{ => common}/views/query.go | 2 +- internal/{ => common}/views/tabs.go | 2 +- internal/config/constants.go | 14 ---- internal/{ => desktop}/app/app.go | 6 +- internal/{ => desktop}/app/events.go | 4 +- internal/{ => desktop}/events/console.go | 4 +- internal/{ => desktop}/events/dbconnection.go | 4 +- internal/{ => desktop}/events/helper.go | 0 internal/{ => desktop}/events/project.go | 4 +- internal/{ => desktop}/events/query.go | 8 +- internal/{ => desktop}/events/setting.go | 2 +- internal/{ => desktop}/events/tabs.go | 4 +- internal/{ => desktop}/setup/setup.go | 6 +- internal/desktop/start.go | 35 +++++++++ internal/server/app/app.go | 15 ++++ internal/server/start.go | 12 +++ main.go | 46 +++-------- .../mongoqueryengine/mongoutils/utils.go | 2 +- pkg/sbsql/cypted_data.go | 2 +- 51 files changed, 252 insertions(+), 156 deletions(-) rename internal/{ => common}/analytics/analytics.go (91%) rename internal/{ => common}/config/config.go (86%) create mode 100644 internal/common/config/constants.go rename internal/{ => common}/config/model.go (63%) rename internal/{ => common}/console/console.go (98%) rename internal/{ => common}/controllers/console.go (75%) rename internal/{ => common}/controllers/dbconnection.go (95%) rename internal/{ => common}/controllers/helper_functions.go (77%) rename internal/{ => common}/controllers/project.go (91%) rename internal/{ => common}/controllers/query.go (97%) rename internal/{ => common}/controllers/setting.go (84%) rename internal/{ => common}/controllers/tabs.go (92%) rename internal/{ => common}/dao/dbconnection.go (93%) rename internal/{ => common}/dao/dbquerylog.go (81%) rename internal/{ => common}/dao/project.go (90%) rename internal/{ => common}/dao/query.go (89%) rename internal/{ => common}/dao/setting.go (87%) rename internal/{ => common}/dao/tabs.go (89%) rename internal/{ => common}/db/db.go (86%) rename internal/{ => common}/models/dbconnection.go (96%) rename internal/{ => common}/models/dbquery.go (100%) rename internal/{ => common}/models/dbquerylog.go (100%) rename internal/{ => common}/models/project.go (100%) rename internal/{ => common}/models/setting.go (100%) rename internal/{ => common}/models/tabs.go (97%) rename internal/{ => common}/tasks/cron.go (87%) rename internal/{ => common}/utils/utils.go (100%) rename internal/{ => common}/views/db_connection.go (90%) rename internal/{ => common}/views/project.go (88%) rename internal/{ => common}/views/query.go (93%) rename internal/{ => common}/views/tabs.go (91%) delete mode 100644 internal/config/constants.go rename internal/{ => desktop}/app/app.go (76%) rename internal/{ => desktop}/app/events.go (94%) rename internal/{ => desktop}/events/console.go (85%) rename internal/{ => desktop}/events/dbconnection.go (97%) rename internal/{ => desktop}/events/helper.go (100%) rename internal/{ => desktop}/events/project.go (94%) rename internal/{ => desktop}/events/query.go (98%) rename internal/{ => desktop}/events/setting.go (95%) rename internal/{ => desktop}/events/tabs.go (96%) rename internal/{ => desktop}/setup/setup.go (82%) create mode 100644 internal/desktop/start.go create mode 100644 internal/server/app/app.go create mode 100644 internal/server/start.go diff --git a/Makefile b/Makefile index 58eccc99..4b61a201 100644 --- a/Makefile +++ b/Makefile @@ -7,13 +7,13 @@ WAILS ?= $(GOPATH)/bin/wails .PHONY: build build: - env CGO_ENABLED=1 $(WAILS) build -trimpath -ldflags="-s -w -X 'main.build=production' -X 'main.version=$(VERSION)'" + env CGO_ENABLED=1 $(WAILS) build -trimpath -ldflags="-s -w -X 'main.envName=production' -X 'main.version=$(VERSION)'" # DO NOT USE THE FOLLOWING PHONY RECIPIES, THEY ARE ONLY FOR DISTRIBUTION .PHONY: build-win build-win: - env GOOS=windows GOARCH=amd64 CGO_ENABLED=1 CC="x86_64-w64-mingw32-gcc" $(WAILS) build -trimpath -ldflags="-s -w -X 'main.build=production' -X 'main.version=$(VERSION)'" -skipbindings + env GOOS=windows GOARCH=amd64 CGO_ENABLED=1 CC="x86_64-w64-mingw32-gcc" $(WAILS) build -trimpath -ldflags="-s -w -X 'main.envName=production' -X 'main.version=$(VERSION)'" -skipbindings .PHONY: sign sign: diff --git a/go.mod b/go.mod index 7ee7cfa3..079f59be 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,8 @@ require ( github.com/auxten/postgresql-parser v1.0.1 github.com/go-co-op/gocron v1.18.0 github.com/go-sql-driver/mysql v1.7.0 - github.com/google/uuid v1.1.2 + github.com/gofiber/fiber/v2 v2.43.0 + github.com/google/uuid v1.3.0 github.com/jackc/pgproto3/v2 v2.3.1 github.com/jackc/pgtype v1.13.0 github.com/jackc/pgx/v4 v4.17.2 @@ -16,20 +17,27 @@ require ( github.com/wailsapp/wails/v2 v2.4.1 github.com/xwb1989/sqlparser v0.0.0-20180606152119-120387863bf2 go.mongodb.org/mongo-driver v1.11.1 - golang.org/x/crypto v0.1.0 + golang.org/x/crypto v0.7.0 gopkg.in/yaml.v2 v2.4.0 gorm.io/driver/sqlite v1.4.4 gorm.io/gorm v1.24.3 ) require ( + github.com/andybalholm/brotli v1.0.5 // indirect github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef // indirect github.com/go-openapi/errors v0.20.2 // indirect github.com/go-openapi/strfmt v0.21.3 // indirect - github.com/mattn/go-runewidth v0.0.13 // indirect + github.com/mattn/go-runewidth v0.0.14 // indirect github.com/mitchellh/mapstructure v1.3.3 // indirect github.com/oklog/ulid v1.3.1 // indirect - github.com/rivo/uniseg v0.2.0 // indirect + github.com/philhofer/fwd v1.1.2 // indirect + github.com/rivo/uniseg v0.4.4 // indirect + github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94 // indirect + github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee // indirect + github.com/tinylib/msgp v1.1.8 // indirect + github.com/valyala/fasthttp v1.45.0 // indirect + github.com/valyala/tcplisten v1.0.0 // indirect ) require ( @@ -57,7 +65,7 @@ require ( github.com/jedib0t/go-pretty v4.3.0+incompatible github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect - github.com/klauspost/compress v1.13.6 // indirect + github.com/klauspost/compress v1.16.4 // indirect github.com/konsorten/go-windows-terminal-sequences v1.0.3 // indirect github.com/kr/pretty v0.3.0 // indirect github.com/kr/text v0.2.0 // indirect @@ -67,8 +75,8 @@ require ( github.com/leaanthony/gosod v1.0.3 // indirect github.com/leaanthony/slicer v1.5.0 // indirect github.com/lib/pq v1.10.2 // indirect - github.com/mattn/go-colorable v0.1.11 // indirect - github.com/mattn/go-isatty v0.0.16 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.18 // indirect github.com/mattn/go-sqlite3 v1.14.15 // indirect github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2 // indirect @@ -88,10 +96,10 @@ require ( github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c // indirect github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 // indirect - golang.org/x/net v0.7.0 // indirect - golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect - golang.org/x/sys v0.5.0 // indirect - golang.org/x/text v0.7.0 // indirect + golang.org/x/net v0.8.0 // indirect + golang.org/x/sync v0.1.0 // indirect + golang.org/x/sys v0.7.0 // indirect + golang.org/x/text v0.8.0 // indirect google.golang.org/genproto v0.0.0-20200911024640-645f7a48b24f // indirect google.golang.org/grpc v1.33.1 // indirect google.golang.org/protobuf v1.28.1 // indirect diff --git a/go.sum b/go.sum index 49160636..d0a8c87e 100644 --- a/go.sum +++ b/go.sum @@ -9,6 +9,8 @@ github.com/Joker/jade v1.0.1-0.20190614124447-d475f43051e7/go.mod h1:6E6s8o2AE4K github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= +github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= +github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef h1:46PFijGLmAjMPwCCCo7Jf0W6f9slllCkkv7vyc1yOSg= @@ -91,6 +93,8 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= +github.com/gofiber/fiber/v2 v2.43.0 h1:yit3E4kHf178B60p5CQBa/3v+WVuziWMa/G2ZNyLJB0= +github.com/gofiber/fiber/v2 v2.43.0/go.mod h1:mpS1ZNE5jU+u+BA4FbM+KKnUzJ4wzTK+FT2tG3tU+6I= github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/googleapis v0.0.0-20180223154316-0cd9801be74a/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= @@ -130,8 +134,9 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= @@ -221,8 +226,9 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.9.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/compress v1.16.4 h1:91KN02FnsOYhuunwU4ssRe8lc2JosWmizWa91B5v1PU= +github.com/klauspost/compress v1.16.4/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -264,18 +270,20 @@ github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwM github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.11 h1:nQ+aFkoE2TMGc0b68U2OKSexC+eq46+XwZzWXHRmPYs= github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= -github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= +github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= +github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI= github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= @@ -306,6 +314,9 @@ github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1y github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 h1:q2e307iGHPdTGp0hoxKjt1H5pDo6utceo3dQVK3I5XQ= github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o= +github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= +github.com/philhofer/fwd v1.1.2 h1:bnDivRJ1EWPjUIRXV5KfORO897HTbpFAQddBdE8t7Gw= +github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0= github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2 h1:acNfDZXmm28D2Yg/c3ALnZStzNaZMSagpbr96vY6Zjc= @@ -320,8 +331,9 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/posthog/posthog-go v0.0.0-20221221115252-24dfed35d71a h1:Ey0XWvrg6u6hyIn1Kd/jCCmL+bMv9El81tvuGBbxZGg= github.com/posthog/posthog-go v0.0.0-20221221115252-24dfed35d71a/go.mod h1:oa2sAs9tGai3VldabTV0eWejt/O4/OOD7azP8GaikqU= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= +github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= @@ -338,6 +350,11 @@ github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFo github.com/samber/lo v1.27.1 h1:sTXwkRiIFIQG+G0HeAvOEnGjqWeWtI9cg5/n51KrxPg= github.com/samber/lo v1.27.1/go.mod h1:it33p9UtPMS7z72fP4gw/EIfQB2eI8ke7GR2wc6+Rhg= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94 h1:rmMl4fXJhKMNWl+K+r/fq4FbbKI+Ia2m9hYBLm2h4G4= +github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94/go.mod h1:90zrgN3D/WJsDd1iXHT96alCoN2KJo6/4x1DZC3wZs8= +github.com/savsgio/gotils v0.0.0-20220530130905-52f3993e8d6d/go.mod h1:Gy+0tqhJvgGlqnTF8CVGP0AaGRjwBtXs/a5PA0Y3+A4= +github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee h1:8Iv5m6xEo1NR1AvpV+7XmhI4r39LGNzwUL4YpMuL5vk= +github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee/go.mod h1:qwtSXrKuJh/zsFQ12yEE89xfCrGKK63Rr7ctU/uCo4g= github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= @@ -378,6 +395,9 @@ github.com/tdewolff/test v1.0.7/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYq github.com/thoas/go-funk v0.9.1 h1:O549iLZqPpTUQ10ykd26sZhzD+rmR5pWhuElrhbC20M= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= +github.com/tinylib/msgp v1.1.6/go.mod h1:75BAfg2hauQhs3qedfdDZmWAPcFMAvJE5b9rGOMufyw= +github.com/tinylib/msgp v1.1.8 h1:FCXC1xanKO4I8plpHGH2P7koL/RzZs12l/+r7vakfm0= +github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw= github.com/tkrajina/go-reflector v0.5.5 h1:gwoQFNye30Kk7NrExj8zm3zFtrGPqOkzFMLuQZg1DtQ= github.com/tkrajina/go-reflector v0.5.5/go.mod h1:ECbqLgccecY5kPmPmXg1MrHW585yMcDkVl6IvJe64T4= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= @@ -387,10 +407,14 @@ github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKn github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasthttp v1.6.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w= +github.com/valyala/fasthttp v1.45.0 h1:zPkkzpIn8tdHZUrVa6PzYd0i5verqiPSkgTd3bSUcpA= +github.com/valyala/fasthttp v1.45.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA= github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52WA1u4= github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= +github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= +github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhwHs= github.com/wailsapp/mimetype v1.4.1/go.mod h1:9aV5k31bBOv5z6u+QP8TltzvNGJPmNJD4XlAL3U+j3o= github.com/wailsapp/wails/v2 v2.4.1 h1:Ns7MOKWQM6l0ttBxpd5VcgYrH+GNPOnoDfnsBpbDnzM= @@ -417,6 +441,7 @@ github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDf github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= go.mongodb.org/mongo-driver v1.10.0/go.mod h1:wsihk0Kdgv8Kqu1Anit4sfK+22vSFbUrAVEYRhCXrA8= go.mongodb.org/mongo-driver v1.11.1 h1:QP0znIRTuL0jf1oBQoAoM0C6ZJfBK4kx0Uumtv1A7w8= @@ -443,10 +468,11 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU= -golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= +golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 h1:3MTrJm4PyNL9NBqvYDSj3DHl46qQakyfqfWo4jgfaEM= golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE= @@ -458,6 +484,8 @@ golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKG golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -479,8 +507,10 @@ golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwY 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= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= +golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -490,8 +520,10 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/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 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -522,20 +554,27 @@ golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= +golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= +golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -553,7 +592,10 @@ golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/internal/analytics/analytics.go b/internal/common/analytics/analytics.go similarity index 91% rename from internal/analytics/analytics.go rename to internal/common/analytics/analytics.go index f8aaea10..42dfa248 100644 --- a/internal/analytics/analytics.go +++ b/internal/common/analytics/analytics.go @@ -2,9 +2,9 @@ package analytics import ( "github.com/posthog/posthog-go" - "github.com/slashbaseide/slashbase/internal/config" - "github.com/slashbaseide/slashbase/internal/dao" - "github.com/slashbaseide/slashbase/internal/models" + "github.com/slashbaseide/slashbase/internal/common/config" + "github.com/slashbaseide/slashbase/internal/common/dao" + "github.com/slashbaseide/slashbase/internal/common/models" ) const ( diff --git a/internal/config/config.go b/internal/common/config/config.go similarity index 86% rename from internal/config/config.go rename to internal/common/config/config.go index 4c84529a..becce613 100644 --- a/internal/config/config.go +++ b/internal/common/config/config.go @@ -9,28 +9,28 @@ import ( "runtime" "github.com/joho/godotenv" - "github.com/slashbaseide/slashbase/internal/utils" + "github.com/slashbaseide/slashbase/internal/common/utils" ) var config AppConfig -func Init(buildName, version string) { - if buildName == BUILD_DEVELOPMENT { +func Init(build, envName, version string) { + if envName == ENV_NAME_DEVELOPMENT { err := godotenv.Load("development.env") if err != nil { log.Fatal("Error loading development.env file") } - } else if buildName == BUILD_PRODUCTION { + } else if envName == ENV_NAME_PRODUCTION { err := godotenv.Load(GetAppEnvFilePath()) if err != nil { log.Fatal("Error loading .env file") } } - config = newConfig(buildName, version) + config = newConfig(build, envName, version) } func IsLive() bool { - return config.BuildName == BUILD_PRODUCTION + return config.EnvName == ENV_NAME_PRODUCTION } func GetConfig() *AppConfig { diff --git a/internal/common/config/constants.go b/internal/common/config/constants.go new file mode 100644 index 00000000..5fa65a80 --- /dev/null +++ b/internal/common/config/constants.go @@ -0,0 +1,20 @@ +package config + +const ( + PAGINATION_COUNT = 20 + + app_name = "slashbase" + app_db_file = "app.db" + app_env_file = ".env" + + ENV_NAME_PRODUCTION = "production" + ENV_NAME_DEVELOPMENT = "development" + + BUILD_DESKTOP = "desktop" + BUILD_SERVER = "server" + + SESSION_COOKIE_NAME = "session" + SESSION_COOKIE_MAX_AGE = 30 * 24 * 60 * 60 * 1000 + + DEFAULT_SERVER_PORT = "3000" +) diff --git a/internal/config/model.go b/internal/common/config/model.go similarity index 63% rename from internal/config/model.go rename to internal/common/config/model.go index 7ed7a2fc..055eacd7 100644 --- a/internal/config/model.go +++ b/internal/common/config/model.go @@ -6,15 +6,17 @@ import ( type AppConfig struct { Version string - BuildName string + Build string + EnvName string Port string CryptedDataSecret string } -func newConfig(buildName, version string) AppConfig { +func newConfig(build, envName, version string) AppConfig { return AppConfig{ Version: version, - BuildName: buildName, + Build: build, + EnvName: envName, Port: DEFAULT_SERVER_PORT, CryptedDataSecret: os.Getenv("CRYPTED_DATA_SECRET"), } diff --git a/internal/console/console.go b/internal/common/console/console.go similarity index 98% rename from internal/console/console.go rename to internal/common/console/console.go index 75142a2c..a201a0c8 100644 --- a/internal/console/console.go +++ b/internal/common/console/console.go @@ -8,7 +8,7 @@ import ( "github.com/jedib0t/go-pretty/table" "github.com/jedib0t/go-pretty/text" - "github.com/slashbaseide/slashbase/internal/models" + "github.com/slashbaseide/slashbase/internal/common/models" "github.com/slashbaseide/slashbase/pkg/queryengines" qemodels "github.com/slashbaseide/slashbase/pkg/queryengines/models" ) diff --git a/internal/controllers/console.go b/internal/common/controllers/console.go similarity index 75% rename from internal/controllers/console.go rename to internal/common/controllers/console.go index f9575ce9..f37c7a00 100644 --- a/internal/controllers/console.go +++ b/internal/common/controllers/console.go @@ -1,8 +1,8 @@ package controllers import ( - "github.com/slashbaseide/slashbase/internal/console" - "github.com/slashbaseide/slashbase/internal/dao" + "github.com/slashbaseide/slashbase/internal/common/console" + "github.com/slashbaseide/slashbase/internal/common/dao" ) type ConsoleController struct{} diff --git a/internal/controllers/dbconnection.go b/internal/common/controllers/dbconnection.go similarity index 95% rename from internal/controllers/dbconnection.go rename to internal/common/controllers/dbconnection.go index bee52060..ea82d36c 100644 --- a/internal/controllers/dbconnection.go +++ b/internal/common/controllers/dbconnection.go @@ -3,8 +3,8 @@ package controllers import ( "errors" - "github.com/slashbaseide/slashbase/internal/dao" - "github.com/slashbaseide/slashbase/internal/models" + "github.com/slashbaseide/slashbase/internal/common/dao" + "github.com/slashbaseide/slashbase/internal/common/models" "github.com/slashbaseide/slashbase/pkg/queryengines" qemodels "github.com/slashbaseide/slashbase/pkg/queryengines/models" ) diff --git a/internal/controllers/helper_functions.go b/internal/common/controllers/helper_functions.go similarity index 77% rename from internal/controllers/helper_functions.go rename to internal/common/controllers/helper_functions.go index 01e28e21..4b096ebe 100644 --- a/internal/controllers/helper_functions.go +++ b/internal/common/controllers/helper_functions.go @@ -1,8 +1,8 @@ package controllers import ( - "github.com/slashbaseide/slashbase/internal/dao" - "github.com/slashbaseide/slashbase/internal/models" + "github.com/slashbaseide/slashbase/internal/common/dao" + "github.com/slashbaseide/slashbase/internal/common/models" qemodels "github.com/slashbaseide/slashbase/pkg/queryengines/models" ) diff --git a/internal/controllers/project.go b/internal/common/controllers/project.go similarity index 91% rename from internal/controllers/project.go rename to internal/common/controllers/project.go index 9c6cc02c..744dbb0f 100644 --- a/internal/controllers/project.go +++ b/internal/common/controllers/project.go @@ -3,8 +3,8 @@ package controllers import ( "errors" - "github.com/slashbaseide/slashbase/internal/dao" - "github.com/slashbaseide/slashbase/internal/models" + "github.com/slashbaseide/slashbase/internal/common/dao" + "github.com/slashbaseide/slashbase/internal/common/models" ) type ProjectController struct{} diff --git a/internal/controllers/query.go b/internal/common/controllers/query.go similarity index 97% rename from internal/controllers/query.go rename to internal/common/controllers/query.go index 07650456..75faadbb 100644 --- a/internal/controllers/query.go +++ b/internal/common/controllers/query.go @@ -4,9 +4,9 @@ import ( "errors" "time" - "github.com/slashbaseide/slashbase/internal/config" - "github.com/slashbaseide/slashbase/internal/dao" - "github.com/slashbaseide/slashbase/internal/models" + "github.com/slashbaseide/slashbase/internal/common/config" + "github.com/slashbaseide/slashbase/internal/common/dao" + "github.com/slashbaseide/slashbase/internal/common/models" "github.com/slashbaseide/slashbase/pkg/queryengines" qemodels "github.com/slashbaseide/slashbase/pkg/queryengines/models" ) diff --git a/internal/controllers/setting.go b/internal/common/controllers/setting.go similarity index 84% rename from internal/controllers/setting.go rename to internal/common/controllers/setting.go index dd484718..ab173905 100644 --- a/internal/controllers/setting.go +++ b/internal/common/controllers/setting.go @@ -4,10 +4,10 @@ import ( "errors" "strconv" - "github.com/slashbaseide/slashbase/internal/analytics" - "github.com/slashbaseide/slashbase/internal/dao" - "github.com/slashbaseide/slashbase/internal/models" - "github.com/slashbaseide/slashbase/internal/utils" + "github.com/slashbaseide/slashbase/internal/common/analytics" + "github.com/slashbaseide/slashbase/internal/common/dao" + "github.com/slashbaseide/slashbase/internal/common/models" + "github.com/slashbaseide/slashbase/internal/common/utils" ) type SettingController struct{} diff --git a/internal/controllers/tabs.go b/internal/common/controllers/tabs.go similarity index 92% rename from internal/controllers/tabs.go rename to internal/common/controllers/tabs.go index 8be785a1..b25beada 100644 --- a/internal/controllers/tabs.go +++ b/internal/common/controllers/tabs.go @@ -4,9 +4,9 @@ import ( "encoding/json" "errors" - "github.com/slashbaseide/slashbase/internal/dao" - "github.com/slashbaseide/slashbase/internal/models" - "github.com/slashbaseide/slashbase/internal/utils" + "github.com/slashbaseide/slashbase/internal/common/dao" + "github.com/slashbaseide/slashbase/internal/common/models" + "github.com/slashbaseide/slashbase/internal/common/utils" ) type TabsController struct{} diff --git a/internal/dao/dbconnection.go b/internal/common/dao/dbconnection.go similarity index 93% rename from internal/dao/dbconnection.go rename to internal/common/dao/dbconnection.go index bf0a6d9e..160be267 100644 --- a/internal/dao/dbconnection.go +++ b/internal/common/dao/dbconnection.go @@ -1,8 +1,8 @@ package dao import ( - "github.com/slashbaseide/slashbase/internal/db" - "github.com/slashbaseide/slashbase/internal/models" + "github.com/slashbaseide/slashbase/internal/common/db" + "github.com/slashbaseide/slashbase/internal/common/models" ) type dbConnectionDao struct{} diff --git a/internal/dao/dbquerylog.go b/internal/common/dao/dbquerylog.go similarity index 81% rename from internal/dao/dbquerylog.go rename to internal/common/dao/dbquerylog.go index 9ece2360..87fa82c3 100644 --- a/internal/dao/dbquerylog.go +++ b/internal/common/dao/dbquerylog.go @@ -3,9 +3,9 @@ package dao import ( "time" - "github.com/slashbaseide/slashbase/internal/config" - "github.com/slashbaseide/slashbase/internal/db" - "github.com/slashbaseide/slashbase/internal/models" + "github.com/slashbaseide/slashbase/internal/common/config" + "github.com/slashbaseide/slashbase/internal/common/db" + "github.com/slashbaseide/slashbase/internal/common/models" ) type dbQueryLogDao struct{} diff --git a/internal/dao/project.go b/internal/common/dao/project.go similarity index 90% rename from internal/dao/project.go rename to internal/common/dao/project.go index c5332c81..9da77e15 100644 --- a/internal/dao/project.go +++ b/internal/common/dao/project.go @@ -1,8 +1,8 @@ package dao import ( - "github.com/slashbaseide/slashbase/internal/db" - "github.com/slashbaseide/slashbase/internal/models" + "github.com/slashbaseide/slashbase/internal/common/db" + "github.com/slashbaseide/slashbase/internal/common/models" ) type projectDao struct{} diff --git a/internal/dao/query.go b/internal/common/dao/query.go similarity index 89% rename from internal/dao/query.go rename to internal/common/dao/query.go index 9b01991e..e3732873 100644 --- a/internal/dao/query.go +++ b/internal/common/dao/query.go @@ -1,8 +1,8 @@ package dao import ( - "github.com/slashbaseide/slashbase/internal/db" - "github.com/slashbaseide/slashbase/internal/models" + "github.com/slashbaseide/slashbase/internal/common/db" + "github.com/slashbaseide/slashbase/internal/common/models" ) type dbQueryDao struct{} diff --git a/internal/dao/setting.go b/internal/common/dao/setting.go similarity index 87% rename from internal/dao/setting.go rename to internal/common/dao/setting.go index ac411077..73b4631d 100644 --- a/internal/dao/setting.go +++ b/internal/common/dao/setting.go @@ -1,8 +1,8 @@ package dao import ( - "github.com/slashbaseide/slashbase/internal/db" - "github.com/slashbaseide/slashbase/internal/models" + "github.com/slashbaseide/slashbase/internal/common/db" + "github.com/slashbaseide/slashbase/internal/common/models" "gorm.io/gorm/clause" ) diff --git a/internal/dao/tabs.go b/internal/common/dao/tabs.go similarity index 89% rename from internal/dao/tabs.go rename to internal/common/dao/tabs.go index e39fa3b6..adf6a2f5 100644 --- a/internal/dao/tabs.go +++ b/internal/common/dao/tabs.go @@ -1,8 +1,8 @@ package dao import ( - "github.com/slashbaseide/slashbase/internal/db" - "github.com/slashbaseide/slashbase/internal/models" + "github.com/slashbaseide/slashbase/internal/common/db" + "github.com/slashbaseide/slashbase/internal/common/models" ) type tabsDao struct{} diff --git a/internal/db/db.go b/internal/common/db/db.go similarity index 86% rename from internal/db/db.go rename to internal/common/db/db.go index 2968766d..3cc92f41 100644 --- a/internal/db/db.go +++ b/internal/common/db/db.go @@ -4,7 +4,7 @@ import ( "fmt" "os" - "github.com/slashbaseide/slashbase/internal/config" + "github.com/slashbaseide/slashbase/internal/common/config" "gorm.io/driver/sqlite" "gorm.io/gorm" "gorm.io/gorm/logger" diff --git a/internal/models/dbconnection.go b/internal/common/models/dbconnection.go similarity index 96% rename from internal/models/dbconnection.go rename to internal/common/models/dbconnection.go index f5c91ae1..eb7dc4c8 100644 --- a/internal/models/dbconnection.go +++ b/internal/common/models/dbconnection.go @@ -6,8 +6,8 @@ import ( "time" "github.com/google/uuid" - "github.com/slashbaseide/slashbase/internal/db" - "github.com/slashbaseide/slashbase/internal/utils" + "github.com/slashbaseide/slashbase/internal/common/db" + "github.com/slashbaseide/slashbase/internal/common/utils" qemodels "github.com/slashbaseide/slashbase/pkg/queryengines/models" "github.com/slashbaseide/slashbase/pkg/sbsql" ) diff --git a/internal/models/dbquery.go b/internal/common/models/dbquery.go similarity index 100% rename from internal/models/dbquery.go rename to internal/common/models/dbquery.go diff --git a/internal/models/dbquerylog.go b/internal/common/models/dbquerylog.go similarity index 100% rename from internal/models/dbquerylog.go rename to internal/common/models/dbquerylog.go diff --git a/internal/models/project.go b/internal/common/models/project.go similarity index 100% rename from internal/models/project.go rename to internal/common/models/project.go diff --git a/internal/models/setting.go b/internal/common/models/setting.go similarity index 100% rename from internal/models/setting.go rename to internal/common/models/setting.go diff --git a/internal/models/tabs.go b/internal/common/models/tabs.go similarity index 97% rename from internal/models/tabs.go rename to internal/common/models/tabs.go index 0a035e5b..5abdcd2d 100644 --- a/internal/models/tabs.go +++ b/internal/common/models/tabs.go @@ -5,7 +5,7 @@ import ( "time" "github.com/google/uuid" - "github.com/slashbaseide/slashbase/internal/db" + "github.com/slashbaseide/slashbase/internal/common/db" ) type Tab struct { diff --git a/internal/tasks/cron.go b/internal/common/tasks/cron.go similarity index 87% rename from internal/tasks/cron.go rename to internal/common/tasks/cron.go index 37991099..cd26628a 100644 --- a/internal/tasks/cron.go +++ b/internal/common/tasks/cron.go @@ -4,8 +4,8 @@ import ( "time" "github.com/go-co-op/gocron" - "github.com/slashbaseide/slashbase/internal/dao" - "github.com/slashbaseide/slashbase/internal/models" + "github.com/slashbaseide/slashbase/internal/common/dao" + "github.com/slashbaseide/slashbase/internal/common/models" "github.com/slashbaseide/slashbase/pkg/queryengines" "github.com/slashbaseide/slashbase/pkg/sshtunnel" ) diff --git a/internal/utils/utils.go b/internal/common/utils/utils.go similarity index 100% rename from internal/utils/utils.go rename to internal/common/utils/utils.go diff --git a/internal/views/db_connection.go b/internal/common/views/db_connection.go similarity index 90% rename from internal/views/db_connection.go rename to internal/common/views/db_connection.go index 8727f903..327a2077 100644 --- a/internal/views/db_connection.go +++ b/internal/common/views/db_connection.go @@ -3,7 +3,7 @@ package views import ( "time" - "github.com/slashbaseide/slashbase/internal/models" + "github.com/slashbaseide/slashbase/internal/common/models" ) type DBConnectionView struct { diff --git a/internal/views/project.go b/internal/common/views/project.go similarity index 88% rename from internal/views/project.go rename to internal/common/views/project.go index a8f2ffa5..39470321 100644 --- a/internal/views/project.go +++ b/internal/common/views/project.go @@ -3,7 +3,7 @@ package views import ( "time" - "github.com/slashbaseide/slashbase/internal/models" + "github.com/slashbaseide/slashbase/internal/common/models" ) type ProjectView struct { diff --git a/internal/views/query.go b/internal/common/views/query.go similarity index 93% rename from internal/views/query.go rename to internal/common/views/query.go index 97f20122..276e1df1 100644 --- a/internal/views/query.go +++ b/internal/common/views/query.go @@ -3,7 +3,7 @@ package views import ( "time" - "github.com/slashbaseide/slashbase/internal/models" + "github.com/slashbaseide/slashbase/internal/common/models" ) type DBQueryView struct { diff --git a/internal/views/tabs.go b/internal/common/views/tabs.go similarity index 91% rename from internal/views/tabs.go rename to internal/common/views/tabs.go index 8a18ecfa..d6af9a25 100644 --- a/internal/views/tabs.go +++ b/internal/common/views/tabs.go @@ -3,7 +3,7 @@ package views import ( "time" - "github.com/slashbaseide/slashbase/internal/models" + "github.com/slashbaseide/slashbase/internal/common/models" ) type TabView struct { diff --git a/internal/config/constants.go b/internal/config/constants.go deleted file mode 100644 index 99ccc939..00000000 --- a/internal/config/constants.go +++ /dev/null @@ -1,14 +0,0 @@ -package config - -const ( - PAGINATION_COUNT = 20 - - app_name = "slashbase" - app_db_file = "app.db" - app_env_file = ".env" - - BUILD_PRODUCTION = "production" - BUILD_DEVELOPMENT = "development" - - DEFAULT_SERVER_PORT = "22022" -) diff --git a/internal/app/app.go b/internal/desktop/app/app.go similarity index 76% rename from internal/app/app.go rename to internal/desktop/app/app.go index 4d1a344a..5fe216ea 100644 --- a/internal/app/app.go +++ b/internal/desktop/app/app.go @@ -3,9 +3,9 @@ package app import ( "context" - "github.com/slashbaseide/slashbase/internal/analytics" - "github.com/slashbaseide/slashbase/internal/dao" - "github.com/slashbaseide/slashbase/internal/models" + "github.com/slashbaseide/slashbase/internal/common/analytics" + "github.com/slashbaseide/slashbase/internal/common/dao" + "github.com/slashbaseide/slashbase/internal/common/models" ) // App struct diff --git a/internal/app/events.go b/internal/desktop/app/events.go similarity index 94% rename from internal/app/events.go rename to internal/desktop/app/events.go index 2e7cc1a8..59010983 100644 --- a/internal/app/events.go +++ b/internal/desktop/app/events.go @@ -3,8 +3,8 @@ package app import ( "context" - "github.com/slashbaseide/slashbase/internal/config" - "github.com/slashbaseide/slashbase/internal/events" + "github.com/slashbaseide/slashbase/internal/common/config" + "github.com/slashbaseide/slashbase/internal/desktop/events" "github.com/wailsapp/wails/v2/pkg/runtime" ) diff --git a/internal/events/console.go b/internal/desktop/events/console.go similarity index 85% rename from internal/events/console.go rename to internal/desktop/events/console.go index 6831c340..e260b242 100644 --- a/internal/events/console.go +++ b/internal/desktop/events/console.go @@ -3,8 +3,8 @@ package events import ( "context" - "github.com/slashbaseide/slashbase/internal/analytics" - "github.com/slashbaseide/slashbase/internal/controllers" + "github.com/slashbaseide/slashbase/internal/common/analytics" + "github.com/slashbaseide/slashbase/internal/common/controllers" "github.com/wailsapp/wails/v2/pkg/runtime" ) diff --git a/internal/events/dbconnection.go b/internal/desktop/events/dbconnection.go similarity index 97% rename from internal/events/dbconnection.go rename to internal/desktop/events/dbconnection.go index 35e38e49..c55a1a4c 100644 --- a/internal/events/dbconnection.go +++ b/internal/desktop/events/dbconnection.go @@ -3,8 +3,8 @@ package events import ( "context" - "github.com/slashbaseide/slashbase/internal/controllers" - "github.com/slashbaseide/slashbase/internal/views" + "github.com/slashbaseide/slashbase/internal/common/controllers" + "github.com/slashbaseide/slashbase/internal/common/views" "github.com/wailsapp/wails/v2/pkg/runtime" ) diff --git a/internal/events/helper.go b/internal/desktop/events/helper.go similarity index 100% rename from internal/events/helper.go rename to internal/desktop/events/helper.go diff --git a/internal/events/project.go b/internal/desktop/events/project.go similarity index 94% rename from internal/events/project.go rename to internal/desktop/events/project.go index eeb12253..fe7f9b6c 100644 --- a/internal/events/project.go +++ b/internal/desktop/events/project.go @@ -3,8 +3,8 @@ package events import ( "context" - "github.com/slashbaseide/slashbase/internal/controllers" - "github.com/slashbaseide/slashbase/internal/views" + "github.com/slashbaseide/slashbase/internal/common/controllers" + "github.com/slashbaseide/slashbase/internal/common/views" "github.com/wailsapp/wails/v2/pkg/runtime" ) diff --git a/internal/events/query.go b/internal/desktop/events/query.go similarity index 98% rename from internal/events/query.go rename to internal/desktop/events/query.go index ce451892..6aaf6eae 100644 --- a/internal/events/query.go +++ b/internal/desktop/events/query.go @@ -4,10 +4,10 @@ import ( "context" "time" - "github.com/slashbaseide/slashbase/internal/analytics" - "github.com/slashbaseide/slashbase/internal/controllers" - "github.com/slashbaseide/slashbase/internal/utils" - "github.com/slashbaseide/slashbase/internal/views" + "github.com/slashbaseide/slashbase/internal/common/analytics" + "github.com/slashbaseide/slashbase/internal/common/controllers" + "github.com/slashbaseide/slashbase/internal/common/utils" + "github.com/slashbaseide/slashbase/internal/common/views" "github.com/wailsapp/wails/v2/pkg/runtime" ) diff --git a/internal/events/setting.go b/internal/desktop/events/setting.go similarity index 95% rename from internal/events/setting.go rename to internal/desktop/events/setting.go index e0f37fb8..a31e666e 100644 --- a/internal/events/setting.go +++ b/internal/desktop/events/setting.go @@ -3,7 +3,7 @@ package events import ( "context" - "github.com/slashbaseide/slashbase/internal/controllers" + "github.com/slashbaseide/slashbase/internal/common/controllers" "github.com/wailsapp/wails/v2/pkg/runtime" ) diff --git a/internal/events/tabs.go b/internal/desktop/events/tabs.go similarity index 96% rename from internal/events/tabs.go rename to internal/desktop/events/tabs.go index b76c7c3f..ea5236eb 100644 --- a/internal/events/tabs.go +++ b/internal/desktop/events/tabs.go @@ -3,8 +3,8 @@ package events import ( "context" - "github.com/slashbaseide/slashbase/internal/controllers" - "github.com/slashbaseide/slashbase/internal/views" + "github.com/slashbaseide/slashbase/internal/common/controllers" + "github.com/slashbaseide/slashbase/internal/common/views" "github.com/wailsapp/wails/v2/pkg/runtime" ) diff --git a/internal/setup/setup.go b/internal/desktop/setup/setup.go similarity index 82% rename from internal/setup/setup.go rename to internal/desktop/setup/setup.go index c109b49e..6466d44d 100644 --- a/internal/setup/setup.go +++ b/internal/desktop/setup/setup.go @@ -2,9 +2,9 @@ package setup import ( "github.com/google/uuid" - "github.com/slashbaseide/slashbase/internal/dao" - "github.com/slashbaseide/slashbase/internal/db" - "github.com/slashbaseide/slashbase/internal/models" + "github.com/slashbaseide/slashbase/internal/common/dao" + "github.com/slashbaseide/slashbase/internal/common/db" + "github.com/slashbaseide/slashbase/internal/common/models" "gorm.io/gorm" ) diff --git a/internal/desktop/start.go b/internal/desktop/start.go new file mode 100644 index 00000000..abeb1256 --- /dev/null +++ b/internal/desktop/start.go @@ -0,0 +1,35 @@ +package desktop + +import ( + "embed" + + "github.com/slashbaseide/slashbase/internal/desktop/app" + "github.com/wailsapp/wails/v2" + "github.com/wailsapp/wails/v2/pkg/options" + "github.com/wailsapp/wails/v2/pkg/options/assetserver" +) + +func Start(assets embed.FS) { + // Create an instance of the app structure + app := app.NewApp() + + // Create application with options + err := wails.Run(&options.App{ + Title: "Slashbase", + Width: 1024, + Height: 768, + WindowStartState: options.Maximised, + AssetServer: &assetserver.Options{ + Assets: assets, + }, + BackgroundColour: &options.RGBA{R: 27, G: 38, B: 54, A: 1}, + OnStartup: app.Startup, + Bind: []interface{}{ + app, + }, + }) + + if err != nil { + println("Error:", err.Error()) + } +} diff --git a/internal/server/app/app.go b/internal/server/app/app.go new file mode 100644 index 00000000..7611af6c --- /dev/null +++ b/internal/server/app/app.go @@ -0,0 +1,15 @@ +package app + +import ( + "time" + + "github.com/gofiber/fiber/v2" +) + +func CreateFiberApp() *fiber.App { + app := fiber.New(fiber.Config{ + AppName: "Slashbase Server", + ReadTimeout: time.Second * time.Duration(60), + }) + return app +} diff --git a/internal/server/start.go b/internal/server/start.go new file mode 100644 index 00000000..5a76de5e --- /dev/null +++ b/internal/server/start.go @@ -0,0 +1,12 @@ +package server + +import ( + "github.com/slashbaseide/slashbase/internal/common/config" + "github.com/slashbaseide/slashbase/internal/server/app" +) + +func Start() { + app := app.CreateFiberApp() + + app.Listen(":" + config.DEFAULT_SERVER_PORT) +} diff --git a/main.go b/main.go index 1f916632..340da1a6 100644 --- a/main.go +++ b/main.go @@ -3,52 +3,28 @@ package main import ( "embed" - "github.com/slashbaseide/slashbase/internal/analytics" - "github.com/slashbaseide/slashbase/internal/app" - "github.com/slashbaseide/slashbase/internal/config" - "github.com/slashbaseide/slashbase/internal/db" - "github.com/slashbaseide/slashbase/internal/setup" - "github.com/slashbaseide/slashbase/internal/tasks" + "github.com/slashbaseide/slashbase/internal/common/analytics" + "github.com/slashbaseide/slashbase/internal/common/config" + "github.com/slashbaseide/slashbase/internal/common/db" + "github.com/slashbaseide/slashbase/internal/common/tasks" + "github.com/slashbaseide/slashbase/internal/desktop" + "github.com/slashbaseide/slashbase/internal/desktop/setup" "github.com/slashbaseide/slashbase/pkg/queryengines" - "github.com/wailsapp/wails/v2" - "github.com/wailsapp/wails/v2/pkg/options" - "github.com/wailsapp/wails/v2/pkg/options/assetserver" ) //go:embed all:frontend/dist var assets embed.FS -var build = config.BUILD_DEVELOPMENT -var version = config.BUILD_DEVELOPMENT +var build = config.BUILD_DESKTOP +var envName = config.ENV_NAME_DEVELOPMENT +var version = config.ENV_NAME_DEVELOPMENT func main() { - config.Init(build, version) + config.Init(build, envName, version) db.InitGormDB() setup.SetupApp() queryengines.Init() tasks.InitCron() analytics.InitAnalytics() - - // Create an instance of the app structure - app := app.NewApp() - - // Create application with options - err := wails.Run(&options.App{ - Title: "Slashbase", - Width: 1024, - Height: 768, - WindowStartState: options.Maximised, - AssetServer: &assetserver.Options{ - Assets: assets, - }, - BackgroundColour: &options.RGBA{R: 27, G: 38, B: 54, A: 1}, - OnStartup: app.Startup, - Bind: []interface{}{ - app, - }, - }) - - if err != nil { - println("Error:", err.Error()) - } + desktop.Start(assets) } diff --git a/pkg/queryengines/mongoqueryengine/mongoutils/utils.go b/pkg/queryengines/mongoqueryengine/mongoutils/utils.go index 5a49935f..562536d2 100644 --- a/pkg/queryengines/mongoqueryengine/mongoutils/utils.go +++ b/pkg/queryengines/mongoqueryengine/mongoutils/utils.go @@ -7,7 +7,7 @@ import ( "strconv" "strings" - "github.com/slashbaseide/slashbase/internal/utils" + "github.com/slashbaseide/slashbase/internal/common/utils" "github.com/tdewolff/parse/v2" "github.com/tdewolff/parse/v2/js" "go.mongodb.org/mongo-driver/bson" diff --git a/pkg/sbsql/cypted_data.go b/pkg/sbsql/cypted_data.go index ff585d59..2ee81a99 100644 --- a/pkg/sbsql/cypted_data.go +++ b/pkg/sbsql/cypted_data.go @@ -3,7 +3,7 @@ package sbsql import ( "database/sql/driver" - "github.com/slashbaseide/slashbase/internal/config" + "github.com/slashbaseide/slashbase/internal/common/config" ) type CryptedData string From 577c6c0534120653a214d8c5e27115b874a2447a Mon Sep 17 00:00:00 2001 From: Paras Waykole Date: Mon, 10 Apr 2023 16:20:00 +0530 Subject: [PATCH 02/29] added server routes, handlers, controllers for users --- .gitignore | 3 +- go.mod | 8 +- go.sum | 21 ++- internal/common/analytics/analytics.go | 1 + internal/common/config/config.go | 98 +++-------- internal/common/config/desktop.go | 69 ++++++++ internal/common/config/model.go | 18 ++ internal/common/db/db.go | 19 +++ internal/common/utils/utils.go | 14 ++ internal/desktop/start.go | 8 + internal/server/app/router.go | 35 ++++ internal/server/controllers/user.go | 134 +++++++++++++++ internal/server/dao/session.go | 50 ++++++ internal/server/dao/user.go | 86 ++++++++++ internal/server/handlers/user.go | 189 +++++++++++++++++++++ internal/server/middlewares/middlewares.go | 74 ++++++++ internal/server/models/session.go | 49 ++++++ internal/server/models/user.go | 59 +++++++ internal/server/setup/setup.go | 47 +++++ internal/server/start.go | 12 +- internal/server/views/user.go | 53 ++++++ main.go | 13 +- 22 files changed, 972 insertions(+), 88 deletions(-) create mode 100644 internal/common/config/desktop.go create mode 100644 internal/server/app/router.go create mode 100644 internal/server/controllers/user.go create mode 100644 internal/server/dao/session.go create mode 100644 internal/server/dao/user.go create mode 100644 internal/server/handlers/user.go create mode 100644 internal/server/middlewares/middlewares.go create mode 100644 internal/server/models/session.go create mode 100644 internal/server/models/user.go create mode 100644 internal/server/setup/setup.go create mode 100644 internal/server/views/user.go diff --git a/.gitignore b/.gitignore index 2ec0d4e0..b9d4d3ce 100644 --- a/.gitignore +++ b/.gitignore @@ -20,8 +20,9 @@ __debug_bin *.out # Environment -local.env +.env development.env +development.server.env production.env # Database diff --git a/go.mod b/go.mod index 079f59be..4ab937d6 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,7 @@ require ( golang.org/x/crypto v0.7.0 gopkg.in/yaml.v2 v2.4.0 gorm.io/driver/sqlite v1.4.4 - gorm.io/gorm v1.24.3 + gorm.io/gorm v1.24.7-0.20230306060331-85eaf9eeda11 ) require ( @@ -28,6 +28,7 @@ require ( github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef // indirect github.com/go-openapi/errors v0.20.2 // indirect github.com/go-openapi/strfmt v0.21.3 // indirect + github.com/jackc/pgx/v5 v5.3.0 // indirect github.com/mattn/go-runewidth v0.0.14 // indirect github.com/mitchellh/mapstructure v1.3.3 // indirect github.com/oklog/ulid v1.3.1 // indirect @@ -52,6 +53,7 @@ require ( github.com/getsentry/raven-go v0.2.0 // indirect github.com/go-ole/go-ole v1.2.6 // indirect github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang-jwt/jwt/v4 v4.5.0 github.com/golang/protobuf v1.5.0 // indirect github.com/golang/snappy v0.0.1 // indirect github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect @@ -59,7 +61,7 @@ require ( github.com/jackc/pgconn v1.13.0 // indirect github.com/jackc/pgio v1.0.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect - github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect + github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect github.com/jackc/puddle v1.3.0 // indirect github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect github.com/jedib0t/go-pretty v4.3.0+incompatible @@ -103,5 +105,5 @@ require ( google.golang.org/genproto v0.0.0-20200911024640-645f7a48b24f // indirect google.golang.org/grpc v1.33.1 // indirect google.golang.org/protobuf v1.28.1 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect + gorm.io/driver/postgres v1.5.0 ) diff --git a/go.sum b/go.sum index d0a8c87e..45dd72f3 100644 --- a/go.sum +++ b/go.sum @@ -103,6 +103,8 @@ github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXP github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/gogo/status v1.1.0/go.mod h1:BFv9nrluPLmrS0EmGVvLaPNmRosr9KapBYd5/hpY1WM= +github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= +github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -180,8 +182,9 @@ github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwX github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgproto3/v2 v2.3.1 h1:nwj7qwf0S+Q7ISFfBndqeLwSwxs+4DPsbRFjECT1Y4Y= github.com/jackc/pgproto3/v2 v2.3.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg= github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= @@ -195,11 +198,14 @@ github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQ github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= github.com/jackc/pgx/v4 v4.17.2 h1:0Ut0rpeKwvIVbMQ1KbMBU4h6wxehBI535LK6Flheh8E= github.com/jackc/pgx/v4 v4.17.2/go.mod h1:lcxIZN44yMIrWI78a5CpucdD14hX0SBDbNRvjDBItsw= +github.com/jackc/pgx/v5 v5.3.0 h1:/NQi8KHMpKWHInxXesC8yD4DhkXPrVhmnwYkjp9AmBA= +github.com/jackc/pgx/v5 v5.3.0/go.mod h1:t3JDKnCBlYIc0ewLF0Q7B8MXmoIaBOZj/ic7iHozM/8= github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.3.0 h1:eHK/5clGOatcjX3oWGBO/MpxpbHzSwud5EWTSCI+MX0= github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle/v2 v2.2.0/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e h1:Q3+PugElBCf4PFpxhErSzU3/PY5sFL5Z6rfv4AbGAck= github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e/go.mod h1:alcuEEnZsY1WQsagKhZDsoPCRoOijYqhZvPwLG0kzVs= github.com/jedib0t/go-pretty v4.3.0+incompatible h1:CGs8AVhEKg/n9YbUenWmNStRW2PHJzaeDodcfvRAbIo= @@ -379,6 +385,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -388,6 +395,7 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/tdewolff/parse/v2 v2.6.5 h1:lYvWBk55GkqKl0JJenGpmrgu/cPHQQ6/Mm1hBGswoGQ= github.com/tdewolff/parse/v2 v2.6.5/go.mod h1:woz0cgbLwFdtbjJu8PIKxhW05KplTFQkOdX78o+Jgrs= github.com/tdewolff/test v1.0.7 h1:8Vs0142DmPFW/bQeHRP3MV19m1gvndjUb1sn8yy74LM= @@ -471,6 +479,7 @@ golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -509,6 +518,7 @@ golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -558,6 +568,7 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/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.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -565,6 +576,7 @@ golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXR 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.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= @@ -573,6 +585,7 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.5.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 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -658,11 +671,13 @@ gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/driver/postgres v1.5.0 h1:u2FXTy14l45qc3UeCJ7QaAXZmZfDDv0YrthvmRq1l0U= +gorm.io/driver/postgres v1.5.0/go.mod h1:FUZXzO+5Uqg5zzwzv4KK49R8lvGIyscBOqYrtI1Ce9A= gorm.io/driver/sqlite v1.4.4 h1:gIufGoR0dQzjkyqDyYSCvsYR6fba1Gw5YKDqKeChxFc= gorm.io/driver/sqlite v1.4.4/go.mod h1:0Aq3iPO+v9ZKbcdiz8gLWRw5VOPcBOPUQJFLq5e2ecI= gorm.io/gorm v1.24.0/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA= -gorm.io/gorm v1.24.3 h1:WL2ifUmzR/SLp85CSURAfybcHnGZ+yLSGSxgYXlFBHg= -gorm.io/gorm v1.24.3/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA= +gorm.io/gorm v1.24.7-0.20230306060331-85eaf9eeda11 h1:9qNbmu21nNThCNnF5i2R3kw2aL27U8ZwbzccNjOmW0g= +gorm.io/gorm v1.24.7-0.20230306060331-85eaf9eeda11/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= diff --git a/internal/common/analytics/analytics.go b/internal/common/analytics/analytics.go index 42dfa248..efa4cb9f 100644 --- a/internal/common/analytics/analytics.go +++ b/internal/common/analytics/analytics.go @@ -39,6 +39,7 @@ func sendEvent(eventName string, properties map[string]interface{}) { return } properties["version"] = config.GetConfig().Version + properties["build"] = config.GetConfig().Build client.Enqueue(posthog.Capture{ DistinctId: setting.UUID().String(), Event: eventName, diff --git a/internal/common/config/config.go b/internal/common/config/config.go index becce613..fd223453 100644 --- a/internal/common/config/config.go +++ b/internal/common/config/config.go @@ -1,29 +1,37 @@ package config import ( - "errors" - "fmt" "log" - "os" - "path/filepath" - "runtime" "github.com/joho/godotenv" - "github.com/slashbaseide/slashbase/internal/common/utils" ) var config AppConfig func Init(build, envName, version string) { - if envName == ENV_NAME_DEVELOPMENT { - err := godotenv.Load("development.env") - if err != nil { - log.Fatal("Error loading development.env file") + if build == BUILD_DESKTOP { + if envName == ENV_NAME_DEVELOPMENT { + err := godotenv.Load("development.env") + if err != nil { + log.Fatal("Error loading development.env file") + } + } else if envName == ENV_NAME_PRODUCTION { + err := godotenv.Load(GetAppEnvFilePath()) + if err != nil { + log.Fatal("Error loading .env file") + } } - } else if envName == ENV_NAME_PRODUCTION { - err := godotenv.Load(GetAppEnvFilePath()) - if err != nil { - log.Fatal("Error loading .env file") + } else { + if envName == ENV_NAME_DEVELOPMENT { + err := godotenv.Load("development.server.env") + if err != nil { + log.Fatal("Error loading development.server.env file") + } + } else if envName == ENV_NAME_PRODUCTION { + err := godotenv.Load(".env") + if err != nil { + log.Fatal("Error loading .env file") + } } } config = newConfig(build, envName, version) @@ -33,64 +41,10 @@ func IsLive() bool { return config.EnvName == ENV_NAME_PRODUCTION } -func GetConfig() *AppConfig { - return &config +func IsDesktop() bool { + return config.Build == BUILD_DESKTOP } -func getAppDataPath() string { - var filePath string - homeDir, err := os.UserHomeDir() - if err != nil { - panic(err) - } - if runtime.GOOS == "windows" { - filePath = filepath.Join(homeDir, "AppData", "Local", app_name) - } else if runtime.GOOS == "darwin" { - filePath = filepath.Join(homeDir, "Library", "Application Support", app_name) - } else if runtime.GOOS == "linux" { - filePath = filepath.Join(homeDir, "."+app_name) - } else { - panic(errors.New("not implemented")) - } - return filePath -} - -func GetAppEnvFilePath() string { - filePath := filepath.Join(getAppDataPath(), app_env_file) - err := os.MkdirAll(filepath.Dir(filePath), 0700) - if err != nil { - panic(err) - } - if _, err := os.Stat(filePath); os.IsNotExist(err) { - err = createEnvFile(filePath) - if err != nil { - panic(err) - } - } - return filePath -} - -func GetAppDatabaseFilePath() string { - if !IsLive() { - return app_db_file - } - filePath := filepath.Join(getAppDataPath(), app_db_file) - err := os.MkdirAll(filepath.Dir(filePath), 0700) - if err != nil { - panic(err) - } - return filePath -} - -func createEnvFile(filePath string) error { - hex, err := utils.RandomHex(32) - if err != nil { - return err - } - envFileData := fmt.Sprintf(`CRYPTED_DATA_SECRET=%s`, hex) - err = os.WriteFile(filePath, []byte(envFileData), 0700) - if err != nil { - return err - } - return nil +func GetConfig() *AppConfig { + return &config } diff --git a/internal/common/config/desktop.go b/internal/common/config/desktop.go new file mode 100644 index 00000000..84d2a7a7 --- /dev/null +++ b/internal/common/config/desktop.go @@ -0,0 +1,69 @@ +package config + +import ( + "errors" + "fmt" + "os" + "path/filepath" + "runtime" + + "github.com/slashbaseide/slashbase/internal/common/utils" +) + +func getAppDataPath() string { + var filePath string + homeDir, err := os.UserHomeDir() + if err != nil { + panic(err) + } + if runtime.GOOS == "windows" { + filePath = filepath.Join(homeDir, "AppData", "Local", app_name) + } else if runtime.GOOS == "darwin" { + filePath = filepath.Join(homeDir, "Library", "Application Support", app_name) + } else if runtime.GOOS == "linux" { + filePath = filepath.Join(homeDir, "."+app_name) + } else { + panic(errors.New("not implemented")) + } + return filePath +} + +func GetAppEnvFilePath() string { + filePath := filepath.Join(getAppDataPath(), app_env_file) + err := os.MkdirAll(filepath.Dir(filePath), 0700) + if err != nil { + panic(err) + } + if _, err := os.Stat(filePath); os.IsNotExist(err) { + err = createEnvFile(filePath) + if err != nil { + panic(err) + } + } + return filePath +} + +func GetAppDatabaseFilePath() string { + if !IsLive() { + return app_db_file + } + filePath := filepath.Join(getAppDataPath(), app_db_file) + err := os.MkdirAll(filepath.Dir(filePath), 0700) + if err != nil { + panic(err) + } + return filePath +} + +func createEnvFile(filePath string) error { + hex, err := utils.RandomHex(32) + if err != nil { + return err + } + envFileData := fmt.Sprintf(`CRYPTED_DATA_SECRET=%s`, hex) + err = os.WriteFile(filePath, []byte(envFileData), 0700) + if err != nil { + return err + } + return nil +} diff --git a/internal/common/config/model.go b/internal/common/config/model.go index 055eacd7..202db3d6 100644 --- a/internal/common/config/model.go +++ b/internal/common/config/model.go @@ -9,7 +9,17 @@ type AppConfig struct { Build string EnvName string Port string + AuthTokenSecret string CryptedDataSecret string + AppDB AppDBConfig +} + +type AppDBConfig struct { + Host string + Port string + User string + Pass string + Name string } func newConfig(build, envName, version string) AppConfig { @@ -18,6 +28,14 @@ func newConfig(build, envName, version string) AppConfig { Build: build, EnvName: envName, Port: DEFAULT_SERVER_PORT, + AuthTokenSecret: os.Getenv("AUTH_TOKEN_SECRET"), CryptedDataSecret: os.Getenv("CRYPTED_DATA_SECRET"), + AppDB: AppDBConfig{ + Host: os.Getenv("APP_DB_HOST"), + Port: os.Getenv("APP_DB_PORT"), + User: os.Getenv("APP_DB_USER"), + Pass: os.Getenv("APP_DB_PASS"), + Name: os.Getenv("APP_DB_NAME"), + }, } } diff --git a/internal/common/db/db.go b/internal/common/db/db.go index 3cc92f41..ff1a0afd 100644 --- a/internal/common/db/db.go +++ b/internal/common/db/db.go @@ -5,6 +5,7 @@ import ( "os" "github.com/slashbaseide/slashbase/internal/common/config" + "gorm.io/driver/postgres" "gorm.io/driver/sqlite" "gorm.io/gorm" "gorm.io/gorm/logger" @@ -18,6 +19,14 @@ func GetDB() *gorm.DB { } func InitGormDB() { + if config.IsDesktop() { + initGormDBDesktop() + } else { + initGormDBServer() + } +} + +func initGormDBDesktop() { dbPath := config.GetAppDatabaseFilePath() db, err = gorm.Open(sqlite.Open(dbPath), &gorm.Config{ Logger: logger.Default.LogMode(logger.Silent), @@ -27,3 +36,13 @@ func InitGormDB() { os.Exit(1) } } + +func initGormDBServer() { + dbConfigData := config.GetConfig().AppDB + dsn := fmt.Sprintf("host=%v user=%v password=%v dbname=%v port=%v sslmode=disable TimeZone=Asia/Kolkata", dbConfigData.Host, dbConfigData.User, dbConfigData.Pass, dbConfigData.Name, dbConfigData.Port) + db, err = gorm.Open(postgres.Open(dsn), &gorm.Config{}) + if err != nil { + fmt.Println(err) + os.Exit(1) + } +} diff --git a/internal/common/utils/utils.go b/internal/common/utils/utils.go index 2b212807..f6496ada 100644 --- a/internal/common/utils/utils.go +++ b/internal/common/utils/utils.go @@ -6,6 +6,8 @@ import ( "strings" "time" "unsafe" + + "github.com/gofiber/fiber/v2" ) func ContainsString(s []string, str string) bool { @@ -83,3 +85,15 @@ func FileExtensionFromPath(path string) string { } return "" } + +func GetRequestCookieHost(c *fiber.Ctx) string { + protocol := "http" + if c.Context().IsTLS() { + protocol = "https" + } + host := string(c.Request().Host()) + if strings.HasPrefix(host, "localhost:") { + return "localhost" + } + return protocol + "://" + host +} diff --git a/internal/desktop/start.go b/internal/desktop/start.go index abeb1256..1834da9f 100644 --- a/internal/desktop/start.go +++ b/internal/desktop/start.go @@ -3,13 +3,21 @@ package desktop import ( "embed" + "github.com/slashbaseide/slashbase/internal/common/analytics" + "github.com/slashbaseide/slashbase/internal/common/tasks" "github.com/slashbaseide/slashbase/internal/desktop/app" + "github.com/slashbaseide/slashbase/internal/desktop/setup" "github.com/wailsapp/wails/v2" "github.com/wailsapp/wails/v2/pkg/options" "github.com/wailsapp/wails/v2/pkg/options/assetserver" ) func Start(assets embed.FS) { + + setup.SetupApp() + analytics.InitAnalytics() + tasks.InitCron() + // Create an instance of the app structure app := app.NewApp() diff --git a/internal/server/app/router.go b/internal/server/app/router.go new file mode 100644 index 00000000..406649ca --- /dev/null +++ b/internal/server/app/router.go @@ -0,0 +1,35 @@ +package app + +import ( + "github.com/gofiber/fiber/v2" + "github.com/slashbaseide/slashbase/internal/common/config" + "github.com/slashbaseide/slashbase/internal/server/handlers" + "github.com/slashbaseide/slashbase/internal/server/middlewares" +) + +func SetupRoutes(app *fiber.App) { + api := app.Group("/api/v1") + { + api.Get("health", healthCheck) + userGroup := api.Group("user") + { + userHandlers := new(handlers.UserHandlers) + userGroup.Post("/login", userHandlers.LoginUser) + userGroup.Get("/checkauth", userHandlers.CheckAuth) + userGroup.Use(middlewares.FindUserMiddleware()) + userGroup.Use(middlewares.AuthUserMiddleware()) + userGroup.Post("/edit", userHandlers.EditAccount) + userGroup.Post("/password", userHandlers.ChangePassword) + userGroup.Post("/add", userHandlers.AddUsers) + userGroup.Get("/all", userHandlers.GetUsers) + userGroup.Get("/logout", userHandlers.Logout) + } + } +} + +func healthCheck(c *fiber.Ctx) error { + return c.JSON(map[string]interface{}{ + "success": true, + "version": config.GetConfig().Version, + }) +} diff --git a/internal/server/controllers/user.go b/internal/server/controllers/user.go new file mode 100644 index 00000000..d90580b3 --- /dev/null +++ b/internal/server/controllers/user.go @@ -0,0 +1,134 @@ +package controllers + +import ( + "database/sql" + "errors" + + "github.com/slashbaseide/slashbase/internal/common/config" + "github.com/slashbaseide/slashbase/internal/server/dao" + "github.com/slashbaseide/slashbase/internal/server/models" + "gorm.io/gorm" +) + +type UserController struct{} + +func (UserController) LoginUser(email, password string) (*models.UserSession, error) { + + usr, err := dao.User.GetUserByEmail(email) + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, errors.New("invalid user") + } + return nil, errors.New("there was some problem") + } + if usr.VerifyPassword(password) { + userSession, _ := models.NewUserSession(usr.ID) + err = dao.User.CreateUserSession(userSession) + userSession.User = *usr + if err != nil { + return nil, errors.New("there was some problem") + } + return userSession, nil + } + return nil, errors.New("invalid user") +} + +func (UserController) EditAccount(authUser *models.User, name, profileImageUrl string) error { + + err := dao.User.EditUser(authUser.ID, name, profileImageUrl) + if err != nil { + return errors.New("there was some problem") + } + authUser.FullName = sql.NullString{ + String: name, + Valid: name != "", + } + authUser.ProfileImageURL = sql.NullString{ + String: profileImageUrl, + Valid: profileImageUrl != "", + } + + return nil +} + +func (UserController) ChangePassword(authUser *models.User, oldPassword, newPassword string) error { + + isOldPaswordValid := authUser.VerifyPassword(oldPassword) + if !isOldPaswordValid { + return errors.New("old password is incorrect") + } + + err := authUser.SetPassword(newPassword) + if err != nil { + return errors.New("there was some problem") + } + + err = dao.User.UpdatePassword(authUser.ID, authUser.Password) + if err != nil { + return errors.New("there was some problem") + } + + return nil +} + +func (UserController) GetUsersPaginated(authUser *models.User, searchTerm string, offset int) (*[]models.User, int, error) { + + if !authUser.IsRoot { + return nil, 0, errors.New("not allowed") + } + + var users *[]models.User + var err error + if searchTerm == "" { + users, err = dao.User.GetUsersPaginated(offset) + } else { + users, err = dao.User.SearchUsersPaginated(searchTerm, offset) + } + + if err != nil { + return nil, 0, errors.New("there was some problem") + } + + next := -1 + if len(*users) == config.PAGINATION_COUNT { + next = offset + config.PAGINATION_COUNT + } + + return users, next, nil +} + +func (UserController) AddUser(authUser *models.User, email, password string) error { + if !authUser.IsRoot { + return errors.New("not allowed") + } + usr, err := dao.User.GetUserByEmail(email) + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + usr, err = models.NewUser(email, password) + if err != nil { + return err + } + } else { + return errors.New("there was some problem") + } + } + err = dao.User.CreateUser(usr) + if err != nil { + return errors.New("there was some problem") + } + return nil +} + +func (UserController) addNewRootUser(email, password string) (*models.User, error) { + usr, err := models.NewUser(email, password) + if err != nil { + return nil, err + } + usr.IsRoot = true + err = dao.User.CreateUser(usr) + if err != nil { + return nil, errors.New("there was some problem") + } + + return usr, nil +} diff --git a/internal/server/dao/session.go b/internal/server/dao/session.go new file mode 100644 index 00000000..074c5b35 --- /dev/null +++ b/internal/server/dao/session.go @@ -0,0 +1,50 @@ +package dao + +import ( + "errors" + "fmt" + + "github.com/golang-jwt/jwt/v4" + "github.com/slashbaseide/slashbase/internal/common/config" + "github.com/slashbaseide/slashbase/internal/common/db" + "github.com/slashbaseide/slashbase/internal/server/models" + "gorm.io/gorm" +) + +func (userDao) CreateUserSession(session *models.UserSession) error { + result := db.GetDB().Create(session) + return result.Error +} + +func (userDao) GetUserSessionByID(sessionID string) (*models.UserSession, error) { + var userSession models.UserSession + err := db.GetDB().Where(&models.UserSession{ID: sessionID}).Preload("User.Projects").First(&userSession).Error + return &userSession, err +} + +func (d userDao) GetUserSessionFromAuthToken(tokenString string) (*models.UserSession, error) { + token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { + if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { + return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"]) + } + return []byte(config.GetConfig().AuthTokenSecret), nil + }) + if err != nil { + return nil, errors.New("invalid token") + } + if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid { + sessionID := claims["sessionID"].(string) + session, err := d.GetUserSessionByID(sessionID) + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, errors.New("invalid token") + } + return nil, errors.New("there was some problem") + } + if !session.IsActive { + return nil, errors.New("invalid Token") + } + return session, nil + } + return nil, errors.New("invalid Token") +} diff --git a/internal/server/dao/user.go b/internal/server/dao/user.go new file mode 100644 index 00000000..7c73be4a --- /dev/null +++ b/internal/server/dao/user.go @@ -0,0 +1,86 @@ +package dao + +import ( + "database/sql" + "strings" + + "github.com/slashbaseide/slashbase/internal/common/config" + "github.com/slashbaseide/slashbase/internal/common/db" + "github.com/slashbaseide/slashbase/internal/server/models" +) + +type userDao struct{} + +var User userDao + +func (userDao) CreateUser(user *models.User) error { + result := db.GetDB().Create(user) + return result.Error +} + +func (userDao) CreateUsers(users *[]models.User) error { + result := db.GetDB().Create(users) + return result.Error +} + +func (userDao) GetUserByEmail(email string) (*models.User, error) { + var user models.User + err := db.GetDB().Where(&models.User{Email: email}).First(&user).Error + return &user, err +} + +func (userDao) GetUserByID(userID string) (*models.User, error) { + var user models.User + err := db.GetDB().Where(&models.User{ID: userID}).Preload("Projects").First(&user).Error + return &user, err +} + +func (userDao) GetUsersByEmails(emails []string) (*[]models.User, error) { + var users []models.User + err := db.GetDB().Where("email IN ?", emails).Find(&users).Error + return &users, err +} + +func (userDao) EditUser(userID string, name string, profileImageURL string) error { + err := db.GetDB().Where(&models.User{ID: userID}).Updates(&models.User{ + FullName: sql.NullString{ + String: name, + Valid: name != "", + }, + ProfileImageURL: sql.NullString{ + String: profileImageURL, + Valid: profileImageURL != "", + }, + }).Error + return err +} + +func (userDao) UpdatePassword(userID string, newPasswordHash string) error { + err := db.GetDB().Where(&models.User{ID: userID}).Updates(&models.User{ + Password: newPasswordHash, + }).Error + return err +} + +func (userDao) GetUsersPaginated(offset int) (*[]models.User, error) { + var users []models.User + err := db.GetDB(). + Model(&models.User{}). + Offset(offset).Limit(config.PAGINATION_COUNT). + Preload("Projects").Find(&users).Error + return &users, err +} + +func (userDao) SearchUsersPaginated(searchTerm string, offset int) (*[]models.User, error) { + + var users []models.User + query := db.GetDB().Debug().Model(&models.User{}) + + if searchTerm != "" { + searchTerm = "%" + strings.ToLower(searchTerm) + "%" + query.Where("lower(email) LIKE ? OR lower(full_name) LIKE ?", searchTerm, searchTerm) + } + + err := query.Offset(offset).Limit(config.PAGINATION_COUNT).Preload("Projects").Find(&users).Error + return &users, err +} diff --git a/internal/server/handlers/user.go b/internal/server/handlers/user.go new file mode 100644 index 00000000..5d48c337 --- /dev/null +++ b/internal/server/handlers/user.go @@ -0,0 +1,189 @@ +package handlers + +import ( + "strconv" + + "github.com/gofiber/fiber/v2" + "github.com/slashbaseide/slashbase/internal/common/config" + "github.com/slashbaseide/slashbase/internal/common/utils" + "github.com/slashbaseide/slashbase/internal/server/controllers" + "github.com/slashbaseide/slashbase/internal/server/middlewares" + "github.com/slashbaseide/slashbase/internal/server/views" +) + +type UserHandlers struct{} + +var userController controllers.UserController + +func (UserHandlers) LoginUser(c *fiber.Ctx) error { + var loginBody struct { + Email string `json:"email"` + Password string `json:"password"` + } + if err := c.BodyParser(&loginBody); err != nil { + return c.JSON(map[string]interface{}{ + "success": false, + "error": err.Error(), + }) + } + + userSession, err := userController.LoginUser(loginBody.Email, loginBody.Password) + if err != nil { + return c.JSON(map[string]interface{}{ + "success": false, + "error": err.Error(), + }) + } + + c.Cookie(&fiber.Cookie{ + Name: config.SESSION_COOKIE_NAME, + Value: userSession.GetAuthToken(), + MaxAge: config.SESSION_COOKIE_MAX_AGE, + Path: "/", + Domain: utils.GetRequestCookieHost(c), + Secure: c.Context().IsTLS(), + HTTPOnly: true, + SameSite: "strict", + }) + return c.JSON(map[string]interface{}{ + "success": true, + "data": views.BuildUserSession(userSession), + }) +} + +func (UserHandlers) CheckAuth(c *fiber.Ctx) error { + tokenString := c.Cookies("session", "") + if tokenString != "" { + return c.JSON(map[string]interface{}{ + "success": true, + }) + } + return c.JSON(map[string]interface{}{ + "success": false, + }) +} + +func (UserHandlers) EditAccount(c *fiber.Ctx) error { + authUser := middlewares.GetAuthUser(c) + var userBody struct { + Name string `json:"name"` + ProfileImageURL string `json:"profileImageUrl"` + } + if err := c.BodyParser(&userBody); err != nil { + return c.JSON(map[string]interface{}{ + "success": false, + "error": err.Error(), + }) + } + err := userController.EditAccount(authUser, userBody.Name, userBody.ProfileImageURL) + if err != nil { + return c.JSON(map[string]interface{}{ + "success": false, + "error": err.Error(), + }) + } + return c.JSON(map[string]interface{}{ + "success": true, + "data": views.BuildUser(authUser), + }) +} + +func (UserHandlers) ChangePassword(c *fiber.Ctx) error { + authUser := middlewares.GetAuthUser(c) + var body struct { + OldPassword string `json:"oldPassword"` + NewPassword string `json:"newPassword"` + } + if err := c.BodyParser(&body); err != nil { + return c.JSON(map[string]interface{}{ + "success": false, + "error": err.Error(), + }) + } + err := userController.ChangePassword(authUser, body.OldPassword, body.NewPassword) + if err != nil { + return c.JSON(map[string]interface{}{ + "success": false, + "error": err.Error(), + }) + } + return c.JSON(map[string]interface{}{ + "success": true, + }) +} + +func (UserHandlers) GetUsers(c *fiber.Ctx) error { + authUser := middlewares.GetAuthUser(c) + offset, err := strconv.Atoi(c.Query("offset", "0")) + if err != nil { + return c.JSON(map[string]interface{}{ + "success": false, + "error": "invalid offset", + }) + } + + searchTerm := c.Query("search", "") + + users, next, err := userController.GetUsersPaginated(authUser, searchTerm, offset) + if err != nil { + return c.JSON(map[string]interface{}{ + "success": false, + "error": err.Error(), + }) + } + + userViews := []views.UserView{} + for _, user := range *users { + userViews = append(userViews, views.BuildUser(&user)) + } + + return c.JSON(map[string]interface{}{ + "success": true, + "data": map[string]interface{}{ + "list": userViews, + "next": next, + }, + }) +} + +func (UserHandlers) AddUsers(c *fiber.Ctx) error { + authUser := middlewares.GetAuthUser(c) + var addUserBody struct { + Email string `json:"email"` + Password string `json:"password"` + } + if err := c.BodyParser(&addUserBody); err != nil { + return c.JSON(map[string]interface{}{ + "success": false, + "error": err.Error(), + }) + } + err := userController.AddUser(authUser, addUserBody.Email, addUserBody.Password) + if err != nil { + return c.JSON(map[string]interface{}{ + "success": false, + "error": err.Error(), + }) + } + return c.JSON(map[string]interface{}{ + "success": true, + }) +} + +func (UserHandlers) Logout(c *fiber.Ctx) error { + authUserSession := middlewares.GetAuthSession(c) + authUserSession.SetInactive() + c.Cookie(&fiber.Cookie{ + Name: config.SESSION_COOKIE_NAME, + Value: "", + MaxAge: -1, + Path: "/", + Domain: utils.GetRequestCookieHost(c), + Secure: c.Context().IsTLS(), + HTTPOnly: true, + SameSite: "strict", + }) + return c.JSON(map[string]interface{}{ + "success": true, + }) +} diff --git a/internal/server/middlewares/middlewares.go b/internal/server/middlewares/middlewares.go new file mode 100644 index 00000000..3c04a28e --- /dev/null +++ b/internal/server/middlewares/middlewares.go @@ -0,0 +1,74 @@ +package middlewares + +import ( + "strings" + + "github.com/gofiber/fiber/v2" + "github.com/slashbaseide/slashbase/internal/common/config" + "github.com/slashbaseide/slashbase/internal/server/dao" + "github.com/slashbaseide/slashbase/internal/server/models" +) + +const ( + USER_SESSION = "USER_SESSION" +) + +// FindUserMiddleware is find authenticated user before sending the request to next handler +func FindUserMiddleware() func(c *fiber.Ctx) error { + return func(c *fiber.Ctx) error { + tokenString := c.Cookies(config.SESSION_COOKIE_NAME, "") + if tokenString == "" { + auth := c.GetReqHeaders()["Authorization"] + if auth != "" && strings.HasPrefix(auth, "Bearer ") { + tokenString = strings.ReplaceAll(auth, "Bearer ", "") + } + } + if tokenString != "" { + userSession, err := dao.User.GetUserSessionFromAuthToken(tokenString) + if err != nil { + return c.Next() + } + c.Context().SetUserValue(USER_SESSION, userSession) + return c.Next() + } + return c.Next() + } +} + +// AuthUserMiddleware is checks if authUser is present else returns unauthorized error +func AuthUserMiddleware() func(c *fiber.Ctx) error { + return func(c *fiber.Ctx) error { + if value := c.Context().UserValue(USER_SESSION); value != nil { + return c.Next() + } + c.JSON(map[string]interface{}{ + "success": false, + "error": "Unauthorized", + }) + return c.Next() + } +} + +func GetAuthSession(c *fiber.Ctx) *models.UserSession { + if session := c.Context().UserValue(USER_SESSION); session.(*models.UserSession) != nil { + return session.(*models.UserSession) + } + return nil +} + +func GetAuthUser(c *fiber.Ctx) *models.User { + if session := c.Context().UserValue(USER_SESSION); session != nil { + authUserSession := session.(*models.UserSession) + return &authUserSession.User + } + return nil +} + +func GetAuthUserProjectIds(c *fiber.Ctx) *[]string { + authUserSession := c.Context().UserValue(USER_SESSION).(*models.UserSession) + projectIDs := []string{} + for _, project := range authUserSession.User.Projects { + projectIDs = append(projectIDs, project.ID) + } + return &projectIDs +} diff --git a/internal/server/models/session.go b/internal/server/models/session.go new file mode 100644 index 00000000..e6e39173 --- /dev/null +++ b/internal/server/models/session.go @@ -0,0 +1,49 @@ +package models + +import ( + "errors" + "time" + + "github.com/golang-jwt/jwt/v4" + "github.com/google/uuid" + "github.com/slashbaseide/slashbase/internal/common/config" + "github.com/slashbaseide/slashbase/internal/common/db" +) + +type UserSession struct { + ID string `gorm:"type:uuid;primaryKey"` + UserID string `gorm:"not null"` + IsActive bool + CreatedAt time.Time `gorm:"autoCreateTime"` + UpdatedAt time.Time `gorm:"autoUpdateTime"` + + User User `gorm:"foreignkey:user_id"` +} + +func NewUserSession(userID string) (*UserSession, error) { + var err error = nil + if userID == "" { + return nil, errors.New("user id cannot be empty") + } + return &UserSession{ + ID: uuid.NewString(), + UserID: userID, + IsActive: true, + }, err +} + +func (session UserSession) GetAuthToken() string { + token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ + "sessionID": session.ID, + }) + tokenString, err := token.SignedString([]byte(config.GetConfig().AuthTokenSecret)) + if err != nil { + panic(err) + } + return tokenString +} + +func (session *UserSession) SetInactive() error { + session.IsActive = false + return db.GetDB().Table("user_sessions").Where("id = ?", session.ID).Update("is_active", false).Error +} diff --git a/internal/server/models/user.go b/internal/server/models/user.go new file mode 100644 index 00000000..48b207cc --- /dev/null +++ b/internal/server/models/user.go @@ -0,0 +1,59 @@ +package models + +import ( + "database/sql" + "errors" + "regexp" + "time" + + "github.com/google/uuid" + "github.com/slashbaseide/slashbase/internal/common/models" + "golang.org/x/crypto/bcrypt" +) + +type User struct { + ID string `gorm:"type:uuid;primaryKey"` + Email string `gorm:"unique;not null"` + Password string `gorm:"not null"` + IsRoot bool `gorm:"not null;default:false"` + FullName sql.NullString + ProfileImageURL sql.NullString + CreatedAt time.Time `gorm:"autoCreateTime"` + UpdatedAt time.Time `gorm:"autoUpdateTime"` + + Projects []models.Project `gorm:"many2many:project_members;"` +} + +func NewUser(email, textPassword string) (*User, error) { + var err error = nil + if email == "" || textPassword == "" { + return nil, errors.New("fields cannot be empty") + } + re := regexp.MustCompile(`^\w+([\.\+-]?\w+)*@\w+([\.-]?\w+)*(\.\w+)+$`) + if !re.Match([]byte(email)) { + return nil, errors.New("email id is not valid") + } + user := User{ + ID: uuid.NewString(), + Email: email, + Password: textPassword, + } + user.hashPassword() + return &user, err +} + +func (u *User) SetPassword(textPassword string) error { + u.Password = textPassword + return u.hashPassword() +} + +func (u *User) VerifyPassword(textPassword string) bool { + err := bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(textPassword)) + return err == nil +} + +func (u *User) hashPassword() error { + bytes, err := bcrypt.GenerateFromPassword([]byte(u.Password), 10) + u.Password = string(bytes) + return err +} diff --git a/internal/server/setup/setup.go b/internal/server/setup/setup.go new file mode 100644 index 00000000..1b48972f --- /dev/null +++ b/internal/server/setup/setup.go @@ -0,0 +1,47 @@ +package setup + +import ( + "os" + + "github.com/google/uuid" + "github.com/slashbaseide/slashbase/internal/common/dao" + "github.com/slashbaseide/slashbase/internal/common/db" + common "github.com/slashbaseide/slashbase/internal/common/models" + "github.com/slashbaseide/slashbase/internal/server/models" + "gorm.io/gorm" +) + +func SetupServer() { + autoMigrate() + configureSettings() +} + +func autoMigrate() { + db.GetDB().AutoMigrate( + &models.User{}, + &models.UserSession{}, + &common.Project{}, + &models.Role{}, + &models.ProjectMember{}, + &models.RolePermission{}, + &common.DBConnection{}, + &common.DBQuery{}, + &common.DBQueryLog{}, + &common.Setting{}, + ) + err := db.GetDB().SetupJoinTable(&models.User{}, "Projects", &models.ProjectMember{}) + if err != nil { + os.Exit(1) + } +} + +func configureSettings() { + _, err := dao.Setting.GetSingleSetting(common.SETTING_NAME_APP_ID) + if err == gorm.ErrRecordNotFound { + settings := []common.Setting{} + settings = append(settings, *common.NewSetting(common.SETTING_NAME_APP_ID, uuid.New().String())) + settings = append(settings, *common.NewSetting(common.SETTING_NAME_TELEMETRY_ENABLED, "true")) + settings = append(settings, *common.NewSetting(common.SETTING_NAME_LOGS_EXPIRE, "30")) + dao.Setting.CreateSettings(&settings) + } +} diff --git a/internal/server/start.go b/internal/server/start.go index 5a76de5e..8fa59e6b 100644 --- a/internal/server/start.go +++ b/internal/server/start.go @@ -1,12 +1,20 @@ package server import ( + "github.com/slashbaseide/slashbase/internal/common/analytics" "github.com/slashbaseide/slashbase/internal/common/config" + "github.com/slashbaseide/slashbase/internal/common/tasks" "github.com/slashbaseide/slashbase/internal/server/app" + "github.com/slashbaseide/slashbase/internal/server/setup" ) func Start() { - app := app.CreateFiberApp() + setup.SetupServer() + tasks.InitCron() + analytics.InitAnalytics() + analytics.SendTelemetryEvent() - app.Listen(":" + config.DEFAULT_SERVER_PORT) + serverApp := app.CreateFiberApp() + app.SetupRoutes(serverApp) + serverApp.Listen(":" + config.DEFAULT_SERVER_PORT) } diff --git a/internal/server/views/user.go b/internal/server/views/user.go new file mode 100644 index 00000000..c899ed99 --- /dev/null +++ b/internal/server/views/user.go @@ -0,0 +1,53 @@ +package views + +import ( + "time" + + "github.com/slashbaseide/slashbase/internal/server/models" +) + +type UserView struct { + ID string `json:"id"` + Email string `json:"email"` + Name *string `json:"name"` + IsRoot bool `json:"isRoot"` + ProfileImageURL string `json:"profileImageUrl"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` +} + +type UserSessionView struct { + ID string `json:"id"` + User UserView `json:"user"` + IsActive bool `json:"isActive"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` +} + +func BuildUser(usr *models.User) UserView { + userView := UserView{ + ID: usr.ID, + Name: nil, + Email: usr.Email, + ProfileImageURL: usr.ProfileImageURL.String, + IsRoot: usr.IsRoot, + CreatedAt: usr.CreatedAt, + UpdatedAt: usr.UpdatedAt, + } + if usr.FullName.Valid { + name := usr.FullName.String + userView.Name = &name + } + return userView +} + +func BuildUserSession(userSession *models.UserSession) UserSessionView { + userSessView := UserSessionView{ + ID: userSession.ID, + User: BuildUser(&userSession.User), + IsActive: userSession.IsActive, + CreatedAt: userSession.CreatedAt, + UpdatedAt: userSession.UpdatedAt, + } + return userSessView +} diff --git a/main.go b/main.go index 340da1a6..f79bf610 100644 --- a/main.go +++ b/main.go @@ -3,12 +3,10 @@ package main import ( "embed" - "github.com/slashbaseide/slashbase/internal/common/analytics" "github.com/slashbaseide/slashbase/internal/common/config" "github.com/slashbaseide/slashbase/internal/common/db" - "github.com/slashbaseide/slashbase/internal/common/tasks" "github.com/slashbaseide/slashbase/internal/desktop" - "github.com/slashbaseide/slashbase/internal/desktop/setup" + "github.com/slashbaseide/slashbase/internal/server" "github.com/slashbaseide/slashbase/pkg/queryengines" ) @@ -22,9 +20,10 @@ var version = config.ENV_NAME_DEVELOPMENT func main() { config.Init(build, envName, version) db.InitGormDB() - setup.SetupApp() queryengines.Init() - tasks.InitCron() - analytics.InitAnalytics() - desktop.Start(assets) + if config.IsDesktop() { + desktop.Start(assets) + } else { + server.Start() + } } From 2bb56fc946b58a6f2fd368434c769a663e33f32d Mon Sep 17 00:00:00 2001 From: Paras Waykole Date: Tue, 11 Apr 2023 15:05:44 +0530 Subject: [PATCH 03/29] added other models --- internal/server/models/project_member.go | 27 ++++++++++++++++++++ internal/server/models/role.go | 29 +++++++++++++++++++++ internal/server/models/role_permission.go | 31 +++++++++++++++++++++++ 3 files changed, 87 insertions(+) create mode 100644 internal/server/models/project_member.go create mode 100644 internal/server/models/role.go create mode 100644 internal/server/models/role_permission.go diff --git a/internal/server/models/project_member.go b/internal/server/models/project_member.go new file mode 100644 index 00000000..60046e71 --- /dev/null +++ b/internal/server/models/project_member.go @@ -0,0 +1,27 @@ +package models + +import ( + "time" + + "github.com/slashbaseide/slashbase/internal/common/models" +) + +type ProjectMember struct { + UserID string `gorm:"primaryKey"` + ProjectID string `gorm:"primaryKey"` + RoleID string + CreatedAt time.Time `gorm:"autoCreateTime"` + UpdatedAt time.Time `gorm:"autoUpdateTime"` + + User User `gorm:"foreignkey:user_id"` + Project models.Project `gorm:"foreignkey:project_id"` + Role Role `gorm:"foreignkey:role_id;constraint:OnDelete:SET NULL;"` +} + +func NewProjectMember(userID string, projectID string, roleID string) *ProjectMember { + return &ProjectMember{ + UserID: userID, + ProjectID: projectID, + RoleID: roleID, + } +} diff --git a/internal/server/models/role.go b/internal/server/models/role.go new file mode 100644 index 00000000..67f07ca5 --- /dev/null +++ b/internal/server/models/role.go @@ -0,0 +1,29 @@ +package models + +import ( + "time" + + "github.com/google/uuid" +) + +type Role struct { + ID string `gorm:"type:uuid;primaryKey"` + Name string `gorm:"index:idx_name_teamid,unique"` + TeamID string `gorm:"index:idx_name_teamid,unique;not null"` + CreatedAt time.Time `gorm:"autoCreateTime"` + UpdatedAt time.Time `gorm:"autoUpdateTime"` + + Permissions []RolePermission +} + +const ( + ROLE_ADMIN = "Admin" +) + +func NewRole(name string, teamID string) *Role { + return &Role{ + ID: uuid.NewString(), + Name: name, + TeamID: teamID, + } +} diff --git a/internal/server/models/role_permission.go b/internal/server/models/role_permission.go new file mode 100644 index 00000000..cf12670b --- /dev/null +++ b/internal/server/models/role_permission.go @@ -0,0 +1,31 @@ +package models + +import ( + "time" + + "github.com/google/uuid" +) + +type RolePermission struct { + ID string `gorm:"type:uuid;primaryKey"` + RoleID string `gorm:"index:idx_roleid_name,unique"` + Name string `gorm:"index:idx_roleid_name,unique"` + Value bool + CreatedAt time.Time `gorm:"autoCreateTime"` + UpdatedAt time.Time `gorm:"autoUpdateTime"` + + Role Role `gorm:"foreignkey:role_id;constraint:OnDelete:SET NULL;"` +} + +const ( + ROLE_PERMISSION_NAME_READ_ONLY = "READ_ONLY" +) + +func NewRolePermission(roleID string, name string, value bool) *RolePermission { + return &RolePermission{ + ID: uuid.NewString(), + RoleID: roleID, + Name: name, + Value: value, + } +} From 0f3b18e3c447e48ffada5c6bddf2670f1bb5856a Mon Sep 17 00:00:00 2001 From: Paras Waykole Date: Tue, 11 Apr 2023 18:04:08 +0530 Subject: [PATCH 04/29] middlewares fixed --- internal/server/app/app.go | 7 +++++++ internal/server/middlewares/middlewares.go | 3 +-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/internal/server/app/app.go b/internal/server/app/app.go index 7611af6c..5d04ca83 100644 --- a/internal/server/app/app.go +++ b/internal/server/app/app.go @@ -4,6 +4,7 @@ import ( "time" "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/cors" ) func CreateFiberApp() *fiber.App { @@ -11,5 +12,11 @@ func CreateFiberApp() *fiber.App { AppName: "Slashbase Server", ReadTimeout: time.Second * time.Duration(60), }) + + app.Use(cors.New(cors.Config{ + AllowOrigins: "*", + AllowHeaders: "Origin, Content-Type, Accept", + })) + return app } diff --git a/internal/server/middlewares/middlewares.go b/internal/server/middlewares/middlewares.go index 3c04a28e..5f4470a1 100644 --- a/internal/server/middlewares/middlewares.go +++ b/internal/server/middlewares/middlewares.go @@ -41,11 +41,10 @@ func AuthUserMiddleware() func(c *fiber.Ctx) error { if value := c.Context().UserValue(USER_SESSION); value != nil { return c.Next() } - c.JSON(map[string]interface{}{ + return c.JSON(map[string]interface{}{ "success": false, "error": "Unauthorized", }) - return c.Next() } } From c79eccc7a43bba249d4f0c0c157037b57bae7f62 Mon Sep 17 00:00:00 2001 From: Paras Waykole Date: Tue, 11 Apr 2023 18:16:48 +0530 Subject: [PATCH 05/29] fixed cors --- internal/server/app/app.go | 5 +++-- internal/server/handlers/user.go | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/internal/server/app/app.go b/internal/server/app/app.go index 5d04ca83..495932f0 100644 --- a/internal/server/app/app.go +++ b/internal/server/app/app.go @@ -14,8 +14,9 @@ func CreateFiberApp() *fiber.App { }) app.Use(cors.New(cors.Config{ - AllowOrigins: "*", - AllowHeaders: "Origin, Content-Type, Accept", + AllowOrigins: "http://localhost:5173", + AllowCredentials: true, + AllowHeaders: "Origin, Content-Type, Accept, Authorization", })) return app diff --git a/internal/server/handlers/user.go b/internal/server/handlers/user.go index 5d48c337..fb34a20d 100644 --- a/internal/server/handlers/user.go +++ b/internal/server/handlers/user.go @@ -52,7 +52,7 @@ func (UserHandlers) LoginUser(c *fiber.Ctx) error { } func (UserHandlers) CheckAuth(c *fiber.Ctx) error { - tokenString := c.Cookies("session", "") + tokenString := c.Cookies(config.SESSION_COOKIE_NAME) if tokenString != "" { return c.JSON(map[string]interface{}{ "success": true, From 2e846aabb9973563fad8e8b6220a8662cc5cf610 Mon Sep 17 00:00:00 2001 From: Paras Waykole Date: Tue, 11 Apr 2023 18:37:55 +0530 Subject: [PATCH 06/29] duplicated frontend and updated frontend-server --- .gitignore | 5 +- frontend-server/.env.development | 0 frontend-server/.env.production | 0 frontend-server/index.html | 19 + frontend-server/package.json | 63 + frontend-server/src/App.tsx | 80 + .../assets/images/empty-state-database.svg | 100 + .../src/assets/images/logo-icon.svg | 68 + .../cards/dbconncard/dbconncard.module.scss | 15 + .../cards/dbconncard/dbconncard.tsx | 54 + .../dbconncard/newdbconnectionbutton.tsx | 26 + .../dbdatamodelcard.module.scss | 9 + .../cards/dbdatamodelcard/dbdatamodelcard.tsx | 52 + .../dbfragments/chart/chart.module.scss | 19 + .../components/dbfragments/chart/chart.tsx | 173 + .../cheatsheet/cheatsheet.module.scss | 0 .../dbfragments/cheatsheet/cheatsheet.tsx | 467 + .../dbfragments/cheatsheet/command.tsx | 23 + .../dbfragments/console.module.scss | 25 + .../src/components/dbfragments/console.tsx | 101 + .../dbfragments/datamodel/addfieldmodal.tsx | 75 + .../dbfragments/datamodel/addindexmodal.tsx | 85 + .../datamodel/datamodel.module.scss | 1 + .../dbfragments/datamodel/datamodel.tsx | 180 + .../src/components/dbfragments/history.tsx | 87 + .../components/dbfragments/home.module.scss | 6 + .../src/components/dbfragments/home.tsx | 62 + .../dbfragments/jsontable/addmodel.tsx | 76 + .../dbfragments/jsontable/jsoncell.tsx | 88 + .../jsontable/jsontable.module.scss | 6 + .../dbfragments/jsontable/jsontable.tsx | 278 + .../components/dbfragments/query.module.scss | 0 .../src/components/dbfragments/query.tsx | 146 + .../queryeditor/queryeditor.module.scss | 9 + .../dbfragments/queryeditor/queryeditor.tsx | 194 + .../dbfragments/showdata.module.scss | 9 + .../src/components/dbfragments/showdata.tsx | 159 + .../src/components/dbfragments/showmodel.tsx | 34 + .../components/dbfragments/table/addmodal.tsx | 94 + .../dbfragments/table/editablecell.tsx | 82 + .../dbfragments/table/table.module.scss | 14 + .../components/dbfragments/table/table.tsx | 356 + .../components/home/createprojectmodal.tsx | 62 + .../src/components/home/welcomecard.tsx | 129 + .../src/components/layouts/applayout.tsx | 43 + .../src/components/layouts/footer.module.scss | 33 + .../src/components/layouts/footer.tsx | 71 + .../src/components/layouts/header.module.scss | 44 + .../src/components/layouts/header.tsx | 130 + .../components/layouts/sidebar.module.scss | 48 + .../src/components/layouts/sidebar.tsx | 166 + .../layouts/sidebars/homesidebar.tsx | 64 + .../src/components/layouts/tabcontext.tsx | 6 + .../components/layouts/tabsbar.module.scss | 11 + .../src/components/layouts/tabsbar.tsx | 60 + .../components/settingfragments/general.tsx | 63 + .../src/components/ui/Input/InputField.tsx | 18 + .../ui/Input/PasswordInputField.tsx | 35 + .../src/components/widgets/confirmModal.tsx | 26 + frontend-server/src/constants.ts | 77 + frontend-server/src/data/defaults.ts | 26 + frontend-server/src/data/models.d.ts | 129 + frontend-server/src/data/storage.ts | 47 + frontend-server/src/main.tsx | 23 + frontend-server/src/network/apiService.ts | 251 + frontend-server/src/network/payloads.ts | 19 + frontend-server/src/network/request.ts | 33 + frontend-server/src/pages/db/index.tsx | 66 + frontend-server/src/pages/home.tsx | 13 + frontend-server/src/pages/project/index.tsx | 71 + frontend-server/src/pages/project/newdb.tsx | 301 + frontend-server/src/pages/settings/about.tsx | 30 + .../src/pages/settings/advanced.tsx | 13 + .../src/pages/settings/general.tsx | 13 + .../src/pages/settings/support.tsx | 42 + .../src/redux/allDBConnectionsSlice.ts | 89 + frontend-server/src/redux/apiSlice.ts | 43 + frontend-server/src/redux/configSlice.ts | 48 + frontend-server/src/redux/consoleSlice.ts | 72 + frontend-server/src/redux/currentUserSlice.ts | 129 + frontend-server/src/redux/dataModelSlice.ts | 185 + .../src/redux/dbConnectionSlice.ts | 196 + frontend-server/src/redux/dbHistorySlice.ts | 51 + frontend-server/src/redux/dbQuerySlice.ts | 67 + frontend-server/src/redux/hooks.ts | 8 + frontend-server/src/redux/projectsSlice.ts | 160 + frontend-server/src/redux/store.ts | 45 + frontend-server/src/redux/tabsSlice.ts | 171 + frontend-server/src/styles/globals.css | 177 + frontend-server/src/styles/index.scss | 33 + frontend-server/src/vite-env.d.ts | 1 + frontend-server/tsconfig.json | 31 + frontend-server/tsconfig.node.json | 11 + frontend-server/vite.config.ts | 10 + frontend-server/yarn.lock | 10260 ++++++++++++++++ 95 files changed, 17289 insertions(+), 1 deletion(-) create mode 100644 frontend-server/.env.development create mode 100644 frontend-server/.env.production create mode 100644 frontend-server/index.html create mode 100644 frontend-server/package.json create mode 100644 frontend-server/src/App.tsx create mode 100644 frontend-server/src/assets/images/empty-state-database.svg create mode 100644 frontend-server/src/assets/images/logo-icon.svg create mode 100644 frontend-server/src/components/cards/dbconncard/dbconncard.module.scss create mode 100644 frontend-server/src/components/cards/dbconncard/dbconncard.tsx create mode 100644 frontend-server/src/components/cards/dbconncard/newdbconnectionbutton.tsx create mode 100644 frontend-server/src/components/cards/dbdatamodelcard/dbdatamodelcard.module.scss create mode 100644 frontend-server/src/components/cards/dbdatamodelcard/dbdatamodelcard.tsx create mode 100644 frontend-server/src/components/dbfragments/chart/chart.module.scss create mode 100644 frontend-server/src/components/dbfragments/chart/chart.tsx create mode 100644 frontend-server/src/components/dbfragments/cheatsheet/cheatsheet.module.scss create mode 100644 frontend-server/src/components/dbfragments/cheatsheet/cheatsheet.tsx create mode 100644 frontend-server/src/components/dbfragments/cheatsheet/command.tsx create mode 100644 frontend-server/src/components/dbfragments/console.module.scss create mode 100644 frontend-server/src/components/dbfragments/console.tsx create mode 100644 frontend-server/src/components/dbfragments/datamodel/addfieldmodal.tsx create mode 100644 frontend-server/src/components/dbfragments/datamodel/addindexmodal.tsx create mode 100644 frontend-server/src/components/dbfragments/datamodel/datamodel.module.scss create mode 100644 frontend-server/src/components/dbfragments/datamodel/datamodel.tsx create mode 100644 frontend-server/src/components/dbfragments/history.tsx create mode 100644 frontend-server/src/components/dbfragments/home.module.scss create mode 100644 frontend-server/src/components/dbfragments/home.tsx create mode 100644 frontend-server/src/components/dbfragments/jsontable/addmodel.tsx create mode 100644 frontend-server/src/components/dbfragments/jsontable/jsoncell.tsx create mode 100644 frontend-server/src/components/dbfragments/jsontable/jsontable.module.scss create mode 100644 frontend-server/src/components/dbfragments/jsontable/jsontable.tsx create mode 100644 frontend-server/src/components/dbfragments/query.module.scss create mode 100644 frontend-server/src/components/dbfragments/query.tsx create mode 100644 frontend-server/src/components/dbfragments/queryeditor/queryeditor.module.scss create mode 100644 frontend-server/src/components/dbfragments/queryeditor/queryeditor.tsx create mode 100644 frontend-server/src/components/dbfragments/showdata.module.scss create mode 100644 frontend-server/src/components/dbfragments/showdata.tsx create mode 100644 frontend-server/src/components/dbfragments/showmodel.tsx create mode 100644 frontend-server/src/components/dbfragments/table/addmodal.tsx create mode 100644 frontend-server/src/components/dbfragments/table/editablecell.tsx create mode 100644 frontend-server/src/components/dbfragments/table/table.module.scss create mode 100644 frontend-server/src/components/dbfragments/table/table.tsx create mode 100644 frontend-server/src/components/home/createprojectmodal.tsx create mode 100644 frontend-server/src/components/home/welcomecard.tsx create mode 100644 frontend-server/src/components/layouts/applayout.tsx create mode 100644 frontend-server/src/components/layouts/footer.module.scss create mode 100644 frontend-server/src/components/layouts/footer.tsx create mode 100644 frontend-server/src/components/layouts/header.module.scss create mode 100644 frontend-server/src/components/layouts/header.tsx create mode 100644 frontend-server/src/components/layouts/sidebar.module.scss create mode 100644 frontend-server/src/components/layouts/sidebar.tsx create mode 100644 frontend-server/src/components/layouts/sidebars/homesidebar.tsx create mode 100644 frontend-server/src/components/layouts/tabcontext.tsx create mode 100644 frontend-server/src/components/layouts/tabsbar.module.scss create mode 100644 frontend-server/src/components/layouts/tabsbar.tsx create mode 100644 frontend-server/src/components/settingfragments/general.tsx create mode 100644 frontend-server/src/components/ui/Input/InputField.tsx create mode 100644 frontend-server/src/components/ui/Input/PasswordInputField.tsx create mode 100644 frontend-server/src/components/widgets/confirmModal.tsx create mode 100644 frontend-server/src/constants.ts create mode 100644 frontend-server/src/data/defaults.ts create mode 100644 frontend-server/src/data/models.d.ts create mode 100644 frontend-server/src/data/storage.ts create mode 100644 frontend-server/src/main.tsx create mode 100644 frontend-server/src/network/apiService.ts create mode 100644 frontend-server/src/network/payloads.ts create mode 100644 frontend-server/src/network/request.ts create mode 100644 frontend-server/src/pages/db/index.tsx create mode 100644 frontend-server/src/pages/home.tsx create mode 100644 frontend-server/src/pages/project/index.tsx create mode 100644 frontend-server/src/pages/project/newdb.tsx create mode 100644 frontend-server/src/pages/settings/about.tsx create mode 100644 frontend-server/src/pages/settings/advanced.tsx create mode 100644 frontend-server/src/pages/settings/general.tsx create mode 100644 frontend-server/src/pages/settings/support.tsx create mode 100644 frontend-server/src/redux/allDBConnectionsSlice.ts create mode 100644 frontend-server/src/redux/apiSlice.ts create mode 100644 frontend-server/src/redux/configSlice.ts create mode 100644 frontend-server/src/redux/consoleSlice.ts create mode 100644 frontend-server/src/redux/currentUserSlice.ts create mode 100644 frontend-server/src/redux/dataModelSlice.ts create mode 100644 frontend-server/src/redux/dbConnectionSlice.ts create mode 100644 frontend-server/src/redux/dbHistorySlice.ts create mode 100644 frontend-server/src/redux/dbQuerySlice.ts create mode 100644 frontend-server/src/redux/hooks.ts create mode 100644 frontend-server/src/redux/projectsSlice.ts create mode 100644 frontend-server/src/redux/store.ts create mode 100644 frontend-server/src/redux/tabsSlice.ts create mode 100644 frontend-server/src/styles/globals.css create mode 100644 frontend-server/src/styles/index.scss create mode 100644 frontend-server/src/vite-env.d.ts create mode 100644 frontend-server/tsconfig.json create mode 100644 frontend-server/tsconfig.node.json create mode 100644 frontend-server/vite.config.ts create mode 100644 frontend-server/yarn.lock diff --git a/.gitignore b/.gitignore index b9d4d3ce..419e3739 100644 --- a/.gitignore +++ b/.gitignore @@ -32,4 +32,7 @@ app.db build/bin node_modules frontend/dist -build/darwin/gon-notarize.json \ No newline at end of file +build/darwin/gon-notarize.json + +# Frontend Server +frontend-server/dist \ No newline at end of file diff --git a/frontend-server/.env.development b/frontend-server/.env.development new file mode 100644 index 00000000..e69de29b diff --git a/frontend-server/.env.production b/frontend-server/.env.production new file mode 100644 index 00000000..e69de29b diff --git a/frontend-server/index.html b/frontend-server/index.html new file mode 100644 index 00000000..5f867e1c --- /dev/null +++ b/frontend-server/index.html @@ -0,0 +1,19 @@ + + + + + + + + + Slashbase + + + +
+ + + + \ No newline at end of file diff --git a/frontend-server/package.json b/frontend-server/package.json new file mode 100644 index 00000000..9cdd98b1 --- /dev/null +++ b/frontend-server/package.json @@ -0,0 +1,63 @@ +{ + "name": "frontend", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "preview": "vite preview" + }, + "dependencies": { + "@codemirror/lang-javascript": "^6.1.2", + "@codemirror/lang-sql": "^6.3.3", + "@fortawesome/fontawesome-free": "^6.2.1", + "@reduxjs/toolkit": "^1.9.1", + "@testing-library/jest-dom": "^5.16.5", + "@testing-library/react": "^13.4.0", + "@testing-library/user-event": "^13.5.0", + "@types/jest": "^27.5.2", + "@types/node": "^16.18.10", + "@types/react": "^18.0.26", + "@types/react-dom": "^18.0.10", + "@uiw/codemirror-theme-duotone": "^4.19.4", + "@uiw/react-codemirror": "^4.19.4", + "axios": "^1.2.1", + "bowser": "^2.11.0", + "bulma": "^0.9.4", + "chart.js": "^4.1.1", + "dateformat": "^5.0.3", + "js-beautify": "^1.14.7", + "localforage": "^1.10.0", + "lodash": "^4.17.21", + "lunr": "^2.3.9", + "react": "^18.2.0", + "react-chartjs-2": "^5.1.0", + "react-dom": "^18.2.0", + "react-hot-toast": "^2.4.0", + "react-infinite-scroll-component": "^6.1.0", + "react-json-view": "^1.21.3", + "react-outside-click-handler": "^1.3.0", + "react-redux": "^8.0.5", + "react-router-dom": "^6.6.0", + "react-scripts": "5.0.1", + "react-table": "^7.8.0", + "react-tooltip": "^5.3.1", + "sass": "^1.57.1", + "sql-formatter": "^12.0.4", + "typescript": "^4.9.4" + }, + "devDependencies": { + "@types/dateformat": "^5.0.0", + "@types/js-beautify": "^1.13.3", + "@types/lodash": "^4.14.191", + "@types/lunr": "^2.3.4", + "@types/react": "^18.0.17", + "@types/react-dom": "^18.0.6", + "@types/react-outside-click-handler": "^1.3.1", + "@types/react-table": "^7.7.12", + "@vitejs/plugin-react": "^2.0.1", + "typescript": "^4.6.4", + "vite": "^3.0.7" + } +} diff --git a/frontend-server/src/App.tsx b/frontend-server/src/App.tsx new file mode 100644 index 00000000..eac95b04 --- /dev/null +++ b/frontend-server/src/App.tsx @@ -0,0 +1,80 @@ +import { useEffect } from "react" +import { Routes, Route, Link } from "react-router-dom" +import { Toaster } from 'react-hot-toast' +import Bowser from "bowser" +import { useAppDispatch } from "./redux/hooks" +import { getProjects } from "./redux/projectsSlice" +import { getAllDBConnections } from "./redux/allDBConnectionsSlice" +import { getConfig } from "./redux/configSlice" +import AppLayout from "./components/layouts/applayout" +import HomePage from "./pages/home" +import ProjectPage from "./pages/project" +import NewDBPage from "./pages/project/newdb" +import DBPage from "./pages/db" +import AdvancedSettingsPage from "./pages/settings/advanced" +import AboutPage from "./pages/settings/about" +import SupportPage from "./pages/settings/support" +import GeneralSettingsPage from "./pages/settings/general" + + +function App() { + + const isValidPlatform: boolean = Bowser.getParser(window.navigator.userAgent).getPlatformType(true) === "desktop" + + const dispatch = useAppDispatch() + + + useEffect(() => { + (async () => { + await dispatch(getProjects()) + await dispatch(getAllDBConnections({})) + await dispatch(getConfig()) + })() + }, [dispatch]) + + if (!isValidPlatform) { + return + } + + return ( +
+ + }> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + + + +
+ ); +} + +export default App + +function NoMatch() { + return ( +
+

Nothing to see here!

+

+ Go back to home +

+
+ ); +} + + +function NotSupportedPlatform() { + return ( +
+

Slashbase is desktop only application!

+

Please use a desktop or laptop to continue...

+
+ ); +} diff --git a/frontend-server/src/assets/images/empty-state-database.svg b/frontend-server/src/assets/images/empty-state-database.svg new file mode 100644 index 00000000..f64e2143 --- /dev/null +++ b/frontend-server/src/assets/images/empty-state-database.svg @@ -0,0 +1,100 @@ + +++++image/svg+xml diff --git a/frontend-server/src/assets/images/logo-icon.svg b/frontend-server/src/assets/images/logo-icon.svg new file mode 100644 index 00000000..f17f2372 --- /dev/null +++ b/frontend-server/src/assets/images/logo-icon.svg @@ -0,0 +1,68 @@ + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + diff --git a/frontend-server/src/components/cards/dbconncard/dbconncard.module.scss b/frontend-server/src/components/cards/dbconncard/dbconncard.module.scss new file mode 100644 index 00000000..57a995fd --- /dev/null +++ b/frontend-server/src/components/cards/dbconncard/dbconncard.module.scss @@ -0,0 +1,15 @@ +.cardContainer { + margin: 15px 0px; + max-width: 600px; +} + +.cardContent { + display: flex; + justify-content: space-between; + align-items: center; +} + +.cardLink { + color: inherit; + text-decoration: none; +} diff --git a/frontend-server/src/components/cards/dbconncard/dbconncard.tsx b/frontend-server/src/components/cards/dbconncard/dbconncard.tsx new file mode 100644 index 00000000..1b030bca --- /dev/null +++ b/frontend-server/src/components/cards/dbconncard/dbconncard.tsx @@ -0,0 +1,54 @@ +import styles from './dbconncard.module.scss' +import React, { useState } from 'react' +import { DBConnection } from '../../../data/models' +import Constants from '../../../constants' +import OutsideClickHandler from 'react-outside-click-handler' +import { Link } from 'react-router-dom' + +type DBConnCardPropType = { + dbConn: DBConnection + onDeleteDB: (dbConnId: string) => void +} + +const DBConnCard = ({ dbConn, onDeleteDB }: DBConnCardPropType) => { + + const [showDropdown, setShowDropdown] = useState(false) + + const toggleDropdown = () => { + setShowDropdown(!showDropdown) + } + + return ( +
+ +
+   {dbConn.name} +
{ e.preventDefault() }}> +
+ +
+ {showDropdown && + { setShowDropdown(false) }}> + + + } +
+
+ +
+ + ) +} + + +export default DBConnCard \ No newline at end of file diff --git a/frontend-server/src/components/cards/dbconncard/newdbconnectionbutton.tsx b/frontend-server/src/components/cards/dbconncard/newdbconnectionbutton.tsx new file mode 100644 index 00000000..f10480d0 --- /dev/null +++ b/frontend-server/src/components/cards/dbconncard/newdbconnectionbutton.tsx @@ -0,0 +1,26 @@ +import React from 'react' +import { Project } from '../../../data/models' +import Constants from '../../../constants' +import { Link } from 'react-router-dom' + +type NewDBConnButtonPropType = { + project: Project +} + +const NewDBConnButton = ({ project }: NewDBConnButtonPropType) => { + + return ( + + + + + + ) +} + + +export default NewDBConnButton diff --git a/frontend-server/src/components/cards/dbdatamodelcard/dbdatamodelcard.module.scss b/frontend-server/src/components/cards/dbdatamodelcard/dbdatamodelcard.module.scss new file mode 100644 index 00000000..eb85117d --- /dev/null +++ b/frontend-server/src/components/cards/dbdatamodelcard/dbdatamodelcard.module.scss @@ -0,0 +1,9 @@ +.cardContainer { + margin: 15px 0px; + max-width: 600px; +} + +.cardContent { + display: flex; + justify-content: space-between; +} \ No newline at end of file diff --git a/frontend-server/src/components/cards/dbdatamodelcard/dbdatamodelcard.tsx b/frontend-server/src/components/cards/dbdatamodelcard/dbdatamodelcard.tsx new file mode 100644 index 00000000..c8ab37c6 --- /dev/null +++ b/frontend-server/src/components/cards/dbdatamodelcard/dbdatamodelcard.tsx @@ -0,0 +1,52 @@ +import styles from './dbdatamodelcard.module.scss' +import { DBConnection, DBDataModel } from '../../../data/models' +import { DBConnType, TabType } from '../../../data/defaults' +import { useAppDispatch } from '../../../redux/hooks' +import { updateActiveTab } from '../../../redux/tabsSlice' + +type DBDataModelPropType = { + dbConnection: DBConnection + dataModel: DBDataModel +} + +const DBDataModelCard = ({ dataModel, dbConnection }: DBDataModelPropType) => { + + const dispatch = useAppDispatch() + + const updateActiveTabToData = () => { + dispatch(updateActiveTab({ tabType: TabType.DATA, metadata: { schema: dataModel.schemaName, name: dataModel.name } })) + } + + const updateActiveTabToModel = () => { + dispatch(updateActiveTab({ tabType: TabType.MODEL, metadata: { schema: dataModel.schemaName, name: dataModel.name } })) + } + + return ( +
+
+
+ {dbConnection.type === DBConnType.POSTGRES && + {dataModel.schemaName}.{dataModel.name}} + {dbConnection.type === DBConnType.MONGO && + {dataModel.name}} + {dbConnection.type === DBConnType.MYSQL && + {dataModel.name}} +
+
+ + +
+
+
+ + ) +} + + +export default DBDataModelCard \ No newline at end of file diff --git a/frontend-server/src/components/dbfragments/chart/chart.module.scss b/frontend-server/src/components/dbfragments/chart/chart.module.scss new file mode 100644 index 00000000..8746bf82 --- /dev/null +++ b/frontend-server/src/components/dbfragments/chart/chart.module.scss @@ -0,0 +1,19 @@ +.contentCenter { + text-align: center; +} + +.barChartWrapper { + max-width: 1024px; + margin: auto; +} + +.lineChartWrapper { + max-width: 1024px; + margin: auto; +} + +.pieChartWrapper { + max-width: 500px; + max-height: 500px; + margin: auto; +} \ No newline at end of file diff --git a/frontend-server/src/components/dbfragments/chart/chart.tsx b/frontend-server/src/components/dbfragments/chart/chart.tsx new file mode 100644 index 00000000..f5dd26e3 --- /dev/null +++ b/frontend-server/src/components/dbfragments/chart/chart.tsx @@ -0,0 +1,173 @@ +import styles from './chart.module.scss' +import React, { useRef, useState } from 'react' +import { DBConnection, DBQueryData } from '../../../data/models' +import { DBConnType } from '../../../data/defaults' +import { Bar, Line, Pie } from 'react-chartjs-2' +import { + Chart as ChartJS, + CategoryScale, + LinearScale, + BarElement, + LineElement, + PointElement, + ArcElement, + Tooltip +} from 'chart.js' + +ChartJS.register( + CategoryScale, + LinearScale, + BarElement, + LineElement, + PointElement, + ArcElement, + Tooltip +) + + +type ChartPropType = { + dbConn: DBConnection + queryData: DBQueryData, +} + +type ChartDataType = { + xaxis: string, + yaxis: string, + data: { + labels: string[] + datasets: Array<{ + label: string, + data: number[], + backgroundColor: string + }> + } +} + +enum ChartViewType { + NONE = "NONE", // default + BARCHART = "BARCHART", + LINECHART = "LINECHART", + PIECHART = "PIECHART" +} + +const Chart = ({ dbConn, queryData }: ChartPropType) => { + + const [chartViewType, setChartViewType] = useState(ChartViewType.NONE) + const [chartData, setChartData] = useState() + + const selectChartTypeRef = useRef(null) + const selectXAxisRef = useRef(null) + const selectYAxisRef = useRef(null) + + const keys = (dbConn.type === DBConnType.POSTGRES || dbConn.type === DBConnType.MYSQL) ? queryData.columns : queryData.keys + + const createChart = () => { + const xaxis = selectXAxisRef.current!.value + const yaxis = selectYAxisRef.current!.value + let labels: string[] + let data: number[] + if (dbConn.type === DBConnType.POSTGRES || dbConn.type === DBConnType.MYSQL) { + const xColIdx = keys.findIndex(x => x === xaxis) + const yColIdx = keys.findIndex(y => y === yaxis) + labels = queryData.rows.map(row => row[xColIdx]) + data = queryData.rows.map(row => row[yColIdx]) + } else { + labels = queryData.data.map(d => d[xaxis]) + data = queryData.data.map(d => d[yaxis]) + } + setChartViewType(ChartViewType[selectChartTypeRef.current!.value.toString() as keyof typeof ChartViewType]) + setChartData({ + xaxis: xaxis, + yaxis: yaxis, + data: { + labels, + datasets: [{ + label: yaxis, + data: data, + backgroundColor: '#615f9c', + }] + } + }) + } + + return +
+
+
+ {chartViewType === ChartViewType.NONE && + +

Create chart from the data

+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+
+
+
+ +
+ +
+ } + {chartViewType === ChartViewType.BARCHART && + +

Bar chart

+
+ +
+
+ } + {chartViewType === ChartViewType.LINECHART && + +

Line chart

+
+ +
+
+ } + {chartViewType === ChartViewType.PIECHART && + +

Pie chart

+
+ +
+
+ } +
+
+
+
+} + +export default Chart \ No newline at end of file diff --git a/frontend-server/src/components/dbfragments/cheatsheet/cheatsheet.module.scss b/frontend-server/src/components/dbfragments/cheatsheet/cheatsheet.module.scss new file mode 100644 index 00000000..e69de29b diff --git a/frontend-server/src/components/dbfragments/cheatsheet/cheatsheet.tsx b/frontend-server/src/components/dbfragments/cheatsheet/cheatsheet.tsx new file mode 100644 index 00000000..c407b828 --- /dev/null +++ b/frontend-server/src/components/dbfragments/cheatsheet/cheatsheet.tsx @@ -0,0 +1,467 @@ +import styles from './cheatsheet.module.scss' +import React, { useRef, useState } from 'react' +import { DBConnType } from '../../../data/defaults' +import CheatsheetCommand from './command' +import _ from 'lodash' +import lunr from 'lunr' + +type CheatSheetPropType = { + dbType: DBConnType + onClose: () => void +} + +const CheatSheetModal = ({ dbType, onClose }: CheatSheetPropType) => { + + const [search, setSearch] = useState() + + const searchInputRef = useRef(null) + + const cmdList = dbType === DBConnType.POSTGRES ? pgcommandList : dbType === DBConnType.MYSQL ? mysqlcommandList : mongoCommandList + + const searchIndex = lunr(function () { + this.field('title', { boost: 10 }) + this.field('description') + this.metadataWhitelist.push("where") + cmdList.forEach((cmd, i) => { + this.add({ ...cmd, id: i }) + }) + }) + + const doSearch = () => { + const queryString = searchInputRef.current!.value + if (queryString === "") { + setSearch(undefined) + return + } + setSearch(searchIndex.search(queryString)) + } + + return ( +
+
+
+
+

 {_.capitalize(dbType)} Cheatsheet

+ +
+
+
+
+ + + + +
+
+
+ {!search && (cmdList).map((cmd, i) => { + return + })} + {search && search.map((result, i) => { + return + })} + {search && search.length === 0 && <> +

No commands found

+ } +
+
+ +
+
+
+ +
) +} + + +export default CheatSheetModal + + +const mysqlcommandList = [ + { + "title": "Select all", + "description": "Gets all the columns and rows from the table.", + "command": "SELECT * FROM ;" + }, + { + "title": "Select only given column names", + "description": "Gets only the given columns and all rows from the table.", + "command": "SELECT , , FROM
;" + }, + { + "title": "Select with column alias", + "description": "Gets only the given columns and all rows from the table.", + "command": "SELECT AS FROM
;" + }, + { + "title": "Select Distinct", + "description": "Gets all rows with unique values in given column from the table", + "command": "SELECT DISTINCT FROM
;" + }, + { + "title": "Select with limit", + "description": "Gets all the columns from the table with first n number of rows", + "command": "SELECT * FROM
LIMIT ;" + }, + { + "title": "Select with limit and offset", + "description": "Gets all the columns and n number of rows starting from offset from the table.", + "command": "SELECT * FROM
LIMIT OFFSET ;" + }, + { + "title": "Select with ordering", + "description": "Gets all columns and rows from the table in sorted order according to given sort expression. Sort name can be a column or an expression.", + "command": "SELECT * FROM
ORDER BY [ASC | DESC];" + }, + { + "title": "Select using where clause with = operator", + "description": "Gets all the columns from the table and all the rows where the given column name value matches the given value.", + "command": "SELECT * FROM
WHERE = ;" + }, + { + "title": "Select using where clause with != operator", + "description": "Gets all the columns from the table and all the rows where the given column name value does not matche the given value.", + "command": "SELECT * FROM
WHERE != ;" + }, + { + "title": "Select using where clause with AND operator", + "description": "Gets all the columns from the table and all the rows where the given column name value matches the given value and the other column names match other given values.", + "command": "SELECT * FROM
WHERE = AND = ;" + }, + { + "title": "Select using where clause with OR operator", + "description": "Gets all the columns from the table and all the rows where the given column name value matches the given value or the other column names match other given values.", + "command": "SELECT * FROM
WHERE = OR = ;" + }, + { + "title": "Select using where clause with IN operator", + "description": "Gets all the columns from the table and all the rows where the given column name value matches any value in the given list", + "command": "SELECT * FROM
WHERE IN (, , );" + }, + { + "title": "Select using where clause with LIKE operator", + "description": "Gets all the columns from the table and all the rows where the given column name value matches the specified pattern", + "command": "SELECT * FROM
WHERE LIKE '';" + }, + { + "title": "Select using where clause with NOT LIKE operator", + "description": "Gets all the columns from the table and all the rows where the given column name value does not match the specified pattern", + "command": "SELECT * FROM
WHERE NOT LIKE '';" + }, + { + "title": "Select using where clause with BETWEEN operator", + "description": "Gets all the columns from the table and all the rows where the given column name value is in the range of specified values", + "command": "SELECT * FROM
WHERE BETWEEN AND ;" + }, + { + "title": "Select using where clause with IS NULL operator", + "description": "Gets all the columns from the table and all the rows where the given column name value is equal to null.", + "command": "SELECT * FROM
WHERE IS NULL;" + }, + { + "title": "Select using where clause with IS NOT NULL operator", + "description": "Gets all the columns from the table and all the rows where the given column name value is not equal to null.", + "command": "SELECT * FROM
WHERE IS NOT NULL;" + }, + { + "title": "Using Inner join", + "description": "Inner joins a table with other table on given condition. If the given condition is satified, the inner join creates a new row that contains columns from both tables and adds this new row to the result.", + "command": "SELECT FROM
INNER JOIN
ON
=
;" + }, + { + "title": "Using Left join", + "description": "Left joins a table with other table on given condition. If the given condition is satified, the left join creates a new row that contains columns from both tables and adds this new row to the result. If condition is not matched, it adds a new row with values from left table.", + "command": "SELECT FROM
LEFT JOIN
ON
=
;" + }, + { + "title": "Using Right join", + "description": "Right joins a table with other table on given condition. If the given condition is satified, the right join creates a new row that contains columns from both tables and adds this new row to the result. If condition is not matched, it adds a new row with values from right table.", + "command": "SELECT FROM
RIGHT JOIN
ON
=
;" + }, + { + "title": "Using Cross join", + "description": "A cross join clause allows you to produce a Cartesian Product of rows in two or more tables.", + "command": "SELECT FROM
CROSS JOIN
;" + }, + { + "title": "Using Group by and aggregate", + "description": "Divides the rows returned by select into groups and for each group applies the given aggregate function.", + "command": "SELECT , FROM
GROUP BY ;" + }, + { + "title": "Using having clause", + "description": "The HAVING clause specifies a search condition for a group or an aggregate. ", + "command": "SELECT , FROM
GROUP BY HAVING ;" + }, + { + "title": "Inserting row into table", + "description": "Inserts a rows with the given values for the given columns into the table.", + "command": "INSERT INTO
(, ,...) VALUES (, ,...);" + }, + { + "title": "Inserting multiple row into table", + "description": "Inserts multiple rows with the given values for the given columns into the table.", + "command": "INSERT INTO
(, ,...) VALUES (, ,...), (, ,...);" + }, + { + "title": "Create new table", + "description": "Creates new table with the given table name and given columns and thier data types.", + "command": "CREATE TABLE [IF NOT EXISTS]
( , ,...);" + }, + { + "title": "Rename a table", + "description": "Renames an existing table to the new given table name.", + "command": "ALTER TABLE [IF EXISTS]
RENAME TO ;" + }, + { + "title": "Add a new column to existing table", + "description": "Creates a new column with the given name and data type in a given existing table.", + "command": "ALTER TABLE
ADD COLUMN ;" + }, + { + "title": "Delete all data from given table", + "description": "Removes all the data from the given table.", + "command": "TRUNCATE TABLE
[CASCADE];" + }, +] + +const pgcommandList = [ + { + "title": "Select all", + "description": "Gets all the columns and rows from the table.", + "command": "SELECT * FROM
;" + }, + { + "title": "Select only given column names", + "description": "Gets only the given columns and all rows from the table.", + "command": "SELECT , , FROM
;" + }, + { + "title": "Select with column alias", + "description": "Gets only the given columns and all rows from the table.", + "command": "SELECT AS FROM
;" + }, + { + "title": "Select Distinct", + "description": "Gets all rows with unique values in given column from the table", + "command": "SELECT DISTINCT FROM
;" + }, + { + "title": "Select with limit", + "description": "Gets all the columns from the table with first n number of rows", + "command": "SELECT * FROM
LIMIT ;" + }, + { + "title": "Select with limit and offset", + "description": "Gets all the columns and n number of rows starting from offset from the table.", + "command": "SELECT * FROM
LIMIT OFFSET ;" + }, + { + "title": "Select with ordering", + "description": "Gets all columns and rows from the table in sorted order according to given sort expression. Sort name can be a column or an expression.", + "command": "SELECT * FROM
ORDER BY [ASC | DESC];" + }, + { + "title": "Select using where clause with = operator", + "description": "Gets all the columns from the table and all the rows where the given column name value matches the given value.", + "command": "SELECT * FROM
WHERE = ;" + }, + { + "title": "Select using where clause with != operator", + "description": "Gets all the columns from the table and all the rows where the given column name value does not matche the given value.", + "command": "SELECT * FROM
WHERE != ;" + }, + { + "title": "Select using where clause with AND operator", + "description": "Gets all the columns from the table and all the rows where the given column name value matches the given value and the other column names match other given values.", + "command": "SELECT * FROM
WHERE = AND = ;" + }, + { + "title": "Select using where clause with OR operator", + "description": "Gets all the columns from the table and all the rows where the given column name value matches the given value or the other column names match other given values.", + "command": "SELECT * FROM
WHERE = OR = ;" + }, + { + "title": "Select using where clause with IN operator", + "description": "Gets all the columns from the table and all the rows where the given column name value matches any value in the given list", + "command": "SELECT * FROM
WHERE IN (, , );" + }, + { + "title": "Select using where clause with LIKE operator", + "description": "Gets all the columns from the table and all the rows where the given column name value matches the specified pattern", + "command": "SELECT * FROM
WHERE LIKE '';" + }, + { + "title": "Select using where clause with NOT LIKE operator", + "description": "Gets all the columns from the table and all the rows where the given column name value does not match the specified pattern", + "command": "SELECT * FROM
WHERE NOT LIKE '';" + }, + { + "title": "Select using where clause with ILIKE operator", + "description": "Gets all the columns from the table and all the rows where the given column name value matches the specified pattern case-insensitively", + "command": "SELECT * FROM
WHERE ILIKE '';" + }, + { + "title": "Select using where clause with BETWEEN operator", + "description": "Gets all the columns from the table and all the rows where the given column name value is in the range of specified values", + "command": "SELECT * FROM
WHERE BETWEEN AND ;" + }, + { + "title": "Select using where clause with IS NULL operator", + "description": "Gets all the columns from the table and all the rows where the given column name value is equal to null.", + "command": "SELECT * FROM
WHERE IS NULL;" + }, + { + "title": "Select using where clause with IS NOT NULL operator", + "description": "Gets all the columns from the table and all the rows where the given column name value is not equal to null.", + "command": "SELECT * FROM
WHERE IS NOT NULL;" + }, + { + "title": "Using Inner join", + "description": "Inner joins a table with other table on given condition. If the given condition is satified, the inner join creates a new row that contains columns from both tables and adds this new row to the result.", + "command": "SELECT FROM
INNER JOIN
ON
=
;" + }, + { + "title": "Using Left join", + "description": "Left joins a table with other table on given condition. If the given condition is satified, the left join creates a new row that contains columns from both tables and adds this new row to the result. If condition is not matched, it adds a new row with values from left table.", + "command": "SELECT FROM
LEFT JOIN
ON
=
;" + }, + { + "title": "Using Right join", + "description": "Right joins a table with other table on given condition. If the given condition is satified, the right join creates a new row that contains columns from both tables and adds this new row to the result. If condition is not matched, it adds a new row with values from right table.", + "command": "SELECT FROM
RIGHT JOIN
ON
=
;" + }, + { + "title": "Using Full outer join", + "description": "Full outer joins a table with other table on given condition. The full outer join or full join returns a result that contains all rows from both left and right tables, with the matching rows from both sides if available. In case there is no match, the columns of the table will be filled with NULL.", + "command": "SELECT FROM
FULL OUTHER JOIN
ON
=
;" + }, + { + "title": "Using Cross join", + "description": "A cross join clause allows you to produce a Cartesian Product of rows in two or more tables.", + "command": "SELECT FROM
CROSS JOIN
;" + }, + { + "title": "Using Natural join", + "description": "A natural join is a join that creates an implicit join based on the same column names in the joined tables.", + "command": "SELECT FROM
NATURAL [INNER, LEFT, RIGHT] JOIN
;" + }, + { + "title": "Using Group by and aggregate", + "description": "Divides the rows returned by select into groups and for each group applies the given aggregate function.", + "command": "SELECT , FROM
GROUP BY ;" + }, + { + "title": "Using having clause", + "description": "The HAVING clause specifies a search condition for a group or an aggregate. ", + "command": "SELECT , FROM
GROUP BY HAVING ;" + }, + { + "title": "Inserting row into table", + "description": "Inserts a rows with the given values for the given columns into the table.", + "command": "INSERT INTO
(, ,...) VALUES (, ,...);" + }, + { + "title": "Inserting multiple row into table", + "description": "Inserts multiple rows with the given values for the given columns into the table.", + "command": "INSERT INTO
(, ,...) VALUES (, ,...), (, ,...);" + }, + { + "title": "Updating values in the table", + "description": "Updates the given value for the column name in the rows where the given condition matches.", + "command": "UPDATE
SET = WHERE ;" + }, + { + "title": "Deleting rows from table", + "description": "Deletes rows from given table where the given condition matches.", + "command": "DELETE FROM
WHERE ;" + }, + { + "title": "Create new table", + "description": "Creates new table with the given table name and given columns and thier data types.", + "command": "CREATE TABLE [IF NOT EXISTS]
( , ,...);" + }, + { + "title": "Rename a table", + "description": "Renames an existing table to the new given table name.", + "command": "ALTER TABLE [IF EXISTS]
RENAME TO ;" + }, + { + "title": "Add a new column to existing table", + "description": "Creates a new column with the given name and data type in a given existing table.", + "command": "ALTER TABLE
ADD COLUMN ;" + }, + { + "title": "Delete all data from given table", + "description": "Removes all the data from the given table.", + "command": "TRUNCATE TABLE
[CASCADE];" + }, +] + +const mongoCommandList = [ + { + "title": "Find all", + "description": "Gets all the documents from the collection.", + "command": "db..find();" + }, + { + "title": "Find with condition", + "description": "Gets all the documents matching the filter from the collection.", + "command": "db..find({});" + }, + { + "title": "Find with limit", + "description": "Gets first n documents from the collection.", + "command": "db..find().limit();" + }, + { + "title": "Find one document", + "description": "Gets the first document from the collection matching the filter.", + "command": "db..findOne({});" + }, + { + "title": "Insert one document", + "description": "Inserts one document into the collection.", + "command": "db..insertOne({});" + }, + { + "title": "Insert documents", + "description": "Inserts one or many documents into the collection.", + "command": "db..insert([{},{}, ... ]);" + }, + { + "title": "Delete one document", + "description": "Deletes one document from the collection matching the filter.", + "command": "db..deleteOne({});" + }, + { + "title": "Delete documents", + "description": "Deletes one or many documents from the collection matching the filter.", + "command": "db..deleteMany({});" + }, + { + "title": "Update one document", + "description": "Updates one document from the collection matching the filter.", + "command": "db..updateOne({}, {});" + }, + { + "title": "Update documents", + "description": "Updates one or many documents from the collection matching the filter.", + "command": "db..updateMany({}, {});" + }, + { + "title": "Replace one document", + "description": "Completely replaces one document matching the filter in the collection with given document.", + "command": "db..replaceOne({}, {});" + }, + { + "title": "Aggregate", + "description": "Processess the given aggregation pipeline", + "command": "db..aggregate([{}, {}, ...]);" + }, +] \ No newline at end of file diff --git a/frontend-server/src/components/dbfragments/cheatsheet/command.tsx b/frontend-server/src/components/dbfragments/cheatsheet/command.tsx new file mode 100644 index 00000000..a2266909 --- /dev/null +++ b/frontend-server/src/components/dbfragments/cheatsheet/command.tsx @@ -0,0 +1,23 @@ +import styles from './cheatsheet.module.scss' +import React from 'react' + +type CheatsheetCommandPropType = { + cmd: { title: string, description: string, command: string }, + isLast: boolean +} + +const CheatsheetCommand = ({ cmd, isLast }: CheatsheetCommandPropType) => { + + return ( +
+

{cmd.title}

+

{cmd.description}

+ {cmd.command} + {!isLast &&
} +
+ +
) +} + + +export default CheatsheetCommand \ No newline at end of file diff --git a/frontend-server/src/components/dbfragments/console.module.scss b/frontend-server/src/components/dbfragments/console.module.scss new file mode 100644 index 00000000..42e7af43 --- /dev/null +++ b/frontend-server/src/components/dbfragments/console.module.scss @@ -0,0 +1,25 @@ +.console { + font-family: monospace; + cursor: text; + height: 100%; + + .block { + white-space: pre; + } + + .cmd::before { + content: "〉"; + } + + .prompt { + position: relative; + padding-left: 20px; + } + + .prompt::before { + content: '〉'; + position: absolute; + left: 0; + } + +} \ No newline at end of file diff --git a/frontend-server/src/components/dbfragments/console.tsx b/frontend-server/src/components/dbfragments/console.tsx new file mode 100644 index 00000000..43a52b3f --- /dev/null +++ b/frontend-server/src/components/dbfragments/console.tsx @@ -0,0 +1,101 @@ +import React, { useContext, useEffect, useRef, useState } from "react" +import { Tab } from "../../data/models" +import { initConsole, runConsoleCmd, selectBlocks } from "../../redux/consoleSlice" +import { selectDBConnection } from "../../redux/dbConnectionSlice" +import { useAppDispatch, useAppSelector } from "../../redux/hooks" +import TabContext from "../layouts/tabcontext" +import styles from './console.module.scss' + +type DBConsolePropType = { +} + +const DBConsoleFragment = ({ }: DBConsolePropType) => { + + const dispatch = useAppDispatch() + + const currentTab: Tab = useContext(TabContext)! + + const consoleEndRef = useRef(null) + + const dbConnection = useAppSelector(selectDBConnection) + const output = useAppSelector(selectBlocks) + const [input, setInput] = useState("") + const [nfocus, setFocus] = useState(0) + + useEffect(() => { + dispatch(initConsole(dbConnection!.id)) + }, [dbConnection]) + + useEffect(() => { + consoleEndRef.current?.scrollIntoView({ behavior: 'smooth' }) + }, [output]) + + const confirmInput = () => { + dispatch(runConsoleCmd({ dbConnId: dbConnection!.id, cmdString: input })) + setInput('') + } + + const focus = (e: any) => { + if (e.target.id === "console") { + setFocus(Math.random()) + } + } + + return
+ + {output.map((block, idx) => { + return + })} + + +
+} + +export default DBConsoleFragment + + +const OutputBlock = ({ block }: any) => { + return

{block.text}

+} + + +const PromptInputWithRef = (props: any) => { + + const defaultValue = useRef("") + const inputRef = useRef(null) + + useEffect(() => { + if (props.isActive) { + inputRef.current?.focus() + } + }, [props.isActive, props.nfocus]) + + const handleInput = (event: any) => { + if (props.onChange) { + props.onChange(event.target.textContent) + } + } + + const handleKeyUp = (event: React.KeyboardEvent) => { + if (props.confirmInput && event.key.toLocaleLowerCase() === 'enter') { + props.confirmInput() + if (inputRef.current) { + inputRef.current.innerText = "" + } + } + } + + return

+ +} diff --git a/frontend-server/src/components/dbfragments/datamodel/addfieldmodal.tsx b/frontend-server/src/components/dbfragments/datamodel/addfieldmodal.tsx new file mode 100644 index 00000000..1c00ed90 --- /dev/null +++ b/frontend-server/src/components/dbfragments/datamodel/addfieldmodal.tsx @@ -0,0 +1,75 @@ +import { useContext, useRef } from 'react' +import { DBConnection, Tab } from '../../../data/models' +import toast from 'react-hot-toast' +import { useAppDispatch, useAppSelector } from '../../../redux/hooks' +import { addDBDataModelField } from '../../../redux/dataModelSlice' +import TabContext from '../../layouts/tabcontext' + +type AddModal = { + dbConn: DBConnection + mSchema: string | null, + mName: string, + onAddField: () => void + onClose: () => void +} + +const AddFieldModal = ({ dbConn, mSchema, mName, onAddField, onClose }: AddModal) => { + + const dispatch = useAppDispatch() + + const activeTab: Tab = useContext(TabContext)! + + const fieldNameRef = useRef(null); + const dataTypeRef = useRef(null); + + const startAdding = async () => { + const result = await dispatch(addDBDataModelField({ tabId: activeTab.id, dbConnectionId: dbConn.id, schemaName: mSchema!, name: mName, fieldName: fieldNameRef.current!.value, dataType: dataTypeRef.current!.value })).unwrap() + if (result.success) { + toast.success('new field added') + onAddField() + onClose() + } else { + toast.error(result.error!) + } + } + + return ( +

+
+
+
+

Add new field to {mSchema}.{mName}

+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+ + +
+
+
+ ) +} + +export default AddFieldModal diff --git a/frontend-server/src/components/dbfragments/datamodel/addindexmodal.tsx b/frontend-server/src/components/dbfragments/datamodel/addindexmodal.tsx new file mode 100644 index 00000000..c39bcbbc --- /dev/null +++ b/frontend-server/src/components/dbfragments/datamodel/addindexmodal.tsx @@ -0,0 +1,85 @@ +import { useContext, useRef } from 'react' +import { DBConnection, Tab } from '../../../data/models' +import toast from 'react-hot-toast' +import { addDBDataModelIndex } from '../../../redux/dataModelSlice' +import { useAppDispatch, useAppSelector } from '../../../redux/hooks' +import TabContext from '../../layouts/tabcontext' + +type AddIndexModal = { + dbConn: DBConnection + mSchema: string | null, + mName: string, + onAddIndex: () => void + onClose: () => void +} + +const AddIndexModal = ({ dbConn, mSchema, mName, onAddIndex, onClose }: AddIndexModal) => { + + const dispatch = useAppDispatch() + + const activeTab: Tab = useContext(TabContext)! + + const indexNameRef = useRef(null); + const fieldNamesRef = useRef(null); + const isUnqiueRef = useRef(null); + + const startAdding = async () => { + const result = await dispatch(addDBDataModelIndex({ tabId: activeTab.id, dbConnectionId: dbConn.id, schemaName: mSchema!, name: mName, indexName: indexNameRef.current!.value, fieldNames: fieldNamesRef.current!.value.split(","), isUnique: isUnqiueRef.current!.checked })).unwrap() + if (result.success) { + toast.success('new index added') + onAddIndex() + onClose() + } else { + toast.error(result.error!) + } + } + + return ( +
+
+
+
+

Add new index to {mSchema}.{mName}

+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+   + Select +
+
+
+
+ + +
+
+
+ ) +} + +export default AddIndexModal diff --git a/frontend-server/src/components/dbfragments/datamodel/datamodel.module.scss b/frontend-server/src/components/dbfragments/datamodel/datamodel.module.scss new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/frontend-server/src/components/dbfragments/datamodel/datamodel.module.scss @@ -0,0 +1 @@ + diff --git a/frontend-server/src/components/dbfragments/datamodel/datamodel.tsx b/frontend-server/src/components/dbfragments/datamodel/datamodel.tsx new file mode 100644 index 00000000..336a89e7 --- /dev/null +++ b/frontend-server/src/components/dbfragments/datamodel/datamodel.tsx @@ -0,0 +1,180 @@ +import styles from './datamodel.module.scss' +import React, { useContext, useEffect, useState } from 'react' +import { DBConnection, Tab } from '../../../data/models' +import { Tooltip } from 'react-tooltip' +import { DBConnType } from '../../../data/defaults' +import AddFieldModal from './addfieldmodal' +import ConfirmModal from '../../widgets/confirmModal' +import toast from 'react-hot-toast' +import AddIndexModal from './addindexmodal' +import { useAppDispatch, useAppSelector } from '../../../redux/hooks' +import { deleteDBDataModelField, deleteDBDataModelIndex, getSingleDataModel, selectSingleDataModel } from '../../../redux/dataModelSlice' +import TabContext from '../../layouts/tabcontext' + +type DataModelPropType = { + dbConn: DBConnection + mschema: string, + mname: string, + isEditable: boolean, +} + +const DataModel = ({ dbConn, mschema, mname, isEditable }: DataModelPropType) => { + + const dispatch = useAppDispatch() + + const currentTab: Tab = useContext(TabContext)! + const dataModel = useAppSelector(selectSingleDataModel) + + const [isEditingModel, setIsEditingModel] = useState(false) + const [isEditingIndex, setIsEditingIndex] = useState(false) + const [showingAddFieldModal, setShowingAddFieldModal] = useState(false) + const [showingAddIndexModal, setShowingAddIndexModal] = useState(false) + const [deletingField, setDeletingField] = useState('') + const [deletingIndex, setDeletingIndex] = useState('') + const [refresh, setRefresh] = useState(Date.now()) + + useEffect(() => { + if (!dbConn) return + dispatch(getSingleDataModel({ tabId: currentTab.id, dbConnectionId: dbConn!.id, schemaName: String(mschema), name: String(mname) })) + }, [dispatch, dbConn, mschema, mname, refresh]) + + const refreshModel = () => { + setRefresh(Date.now()) + } + + if (!dataModel) { + return null + } + const label = dbConn.type === DBConnType.POSTGRES ? `${dataModel.schemaName}.${dataModel.name}` : `${dataModel.name}` + + const deleteField = async () => { + const result = await dispatch(deleteDBDataModelField({ tabId: currentTab.id, dbConnectionId: dbConn.id, schemaName: dataModel.schemaName!, name: dataModel.name, fieldName: deletingField })).unwrap() + if (result.success) { + toast.success(`deleted field ${deletingField}`) + refreshModel() + setDeletingField('') + } else { + toast.error(result.error!) + } + } + + const deleteIndex = async () => { + const result = await dispatch(deleteDBDataModelIndex({ dbConnectionId: dbConn.id, schemaName: dataModel.schemaName!, name: dataModel.name, indexName: deletingIndex })).unwrap() + if (result.success) { + toast.success(`deleted index ${deletingIndex}`) + refreshModel() + setDeletingIndex('') + } else { + toast.error(result.error!) + } + } + + return ( + +
+
+ + + + {(dbConn.type === DBConnType.POSTGRES || dbConn.type === DBConnType.MYSQL) && isEditingModel && } + + + + { + dataModel.fields?.map(field => ( + + + + + {(dbConn.type === DBConnType.POSTGRES || dbConn.type === DBConnType.MYSQL) && } + {isEditingModel && } + + )) + } + +
+ {label} + {isEditable && } + + +
{ + field.isPrimary ? + : + field.isNullable ? + : + + }{field.name}{field.type} + {field.tags.length > 0 && field.tags.map(tag => ( + {tag} + )).reduce((prev, curr) => [prev, ' ', curr])} + + +
+ {dataModel.indexes && dataModel.indexes.length > 0 && + + + + + {isEditingIndex && } + + + + { + dataModel.indexes?.map(idx => ( + + + + {isEditingIndex && } + + )) + } + +
+ Indexes + {isEditable && } + + +
{idx.name}{idx.indexDef} + +
} + {(dbConn.type === DBConnType.POSTGRES || dbConn.type === DBConnType.MYSQL) && showingAddFieldModal && { setShowingAddFieldModal(false) }} />} + {showingAddIndexModal && { setShowingAddIndexModal(false) }} />} + {deletingField !== '' && { setDeletingField('') }} />} + {deletingIndex !== '' && { setDeletingIndex('') }} />} + + + + ) +} + +export default DataModel \ No newline at end of file diff --git a/frontend-server/src/components/dbfragments/history.tsx b/frontend-server/src/components/dbfragments/history.tsx new file mode 100644 index 00000000..9cf74941 --- /dev/null +++ b/frontend-server/src/components/dbfragments/history.tsx @@ -0,0 +1,87 @@ +import React, { useContext, useEffect } from 'react' +import { DBConnection, Tab } from '../../data/models' +import { selectDBConnection } from '../../redux/dbConnectionSlice' +import { useAppDispatch, useAppSelector } from '../../redux/hooks' +import toast from 'react-hot-toast' +import InfiniteScroll from 'react-infinite-scroll-component' +import dateformat from 'dateformat' +import { getDBQueryLogs, reset, selectDBQueryLogs, selectDBQueryLogsNext } from '../../redux/dbHistorySlice' +import TabContext from '../layouts/tabcontext' + + +type DBHistoryPropType = { +} + +const DBHistoryFragment = ({ }: DBHistoryPropType) => { + + const dispatch = useAppDispatch() + + const currentTab: Tab = useContext(TabContext)! + + const dbConnection: DBConnection | undefined = useAppSelector(selectDBConnection) + const dbQueryLogs = useAppSelector(selectDBQueryLogs) + const dbQueryLogsNext = useAppSelector(selectDBQueryLogsNext) + + useEffect(() => { + if (dbConnection) { + (async () => { + dispatch(reset()) + })() + fetchDBQueryLogs() + } + }, [dispatch, dbConnection]) + + const fetchDBQueryLogs = async () => { + const result = await dispatch(getDBQueryLogs({ dbConnId: dbConnection!.id })).unwrap() + if (!result.success) { + toast.error(result.error!) + } + } + + return ( +
+ {dbConnection && + +

Showing History in {dbConnection.name}

+
+ + Loading... +

+ } + endMessage={ +

+ You have seen it all! +

+ } + scrollableTarget="maincontent" + > + + + {dbQueryLogs.map((log) => { + return ( + + + + + ) + })} + +
+ {log.query} + + {dateformat(log.createdAt, "mmm dd, yyyy HH:MM:ss")} +
+
+
+ } +
+ ) +} + + +export default DBHistoryFragment \ No newline at end of file diff --git a/frontend-server/src/components/dbfragments/home.module.scss b/frontend-server/src/components/dbfragments/home.module.scss new file mode 100644 index 00000000..cde80524 --- /dev/null +++ b/frontend-server/src/components/dbfragments/home.module.scss @@ -0,0 +1,6 @@ +.connectingMsg { + width: 70%; + max-width: 400px; + min-height: 70px; + padding-top: 20px; +} \ No newline at end of file diff --git a/frontend-server/src/components/dbfragments/home.tsx b/frontend-server/src/components/dbfragments/home.tsx new file mode 100644 index 00000000..dd06e3e0 --- /dev/null +++ b/frontend-server/src/components/dbfragments/home.tsx @@ -0,0 +1,62 @@ +import styles from './home.module.scss' +import React, { useContext } from 'react' +import { DBConnection, DBDataModel, Tab } from '../../data/models' +import { selectDBConnection, selectDBDataModels, selectIsFetchingDBDataModels } from '../../redux/dbConnectionSlice' +import { useAppDispatch, useAppSelector } from '../../redux/hooks' +import DBDataModelCard from '../cards/dbdatamodelcard/dbdatamodelcard' +import { TabType } from '../../data/defaults' +import { updateActiveTab } from '../../redux/tabsSlice' +import TabContext from '../layouts/tabcontext' + +type DBHomePropType = { +} + +const DBHomeFragment = ({ }: DBHomePropType) => { + + const dispatch = useAppDispatch() + + const currentTab: Tab = useContext(TabContext)! + + const dbConnection: DBConnection | undefined = useAppSelector(selectDBConnection) + const dbDataModels: DBDataModel[] = useAppSelector(selectDBDataModels) + + const isFetching: boolean = useAppSelector(selectIsFetchingDBDataModels) + + const updateActiveTabToQuery = () => { + dispatch(updateActiveTab({ tabType: TabType.QUERY, metadata: { queryId: 'new', query: "" } })) + } + + const updateActiveTabToHistory = () => { + dispatch(updateActiveTab({ tabType: TabType.HISTORY, metadata: {} })) + } + + return ( +
+ {dbConnection && + +

Data Models

+ {isFetching &&
+ Connecting to DB... +
+ } + {dbDataModels.map(x => ( + + ))} +
+ + +
+
+ } +
+ ) +} + + +export default DBHomeFragment \ No newline at end of file diff --git a/frontend-server/src/components/dbfragments/jsontable/addmodel.tsx b/frontend-server/src/components/dbfragments/jsontable/addmodel.tsx new file mode 100644 index 00000000..fa7e8774 --- /dev/null +++ b/frontend-server/src/components/dbfragments/jsontable/addmodel.tsx @@ -0,0 +1,76 @@ +import styles from './jsontable.module.scss' +import React, { useContext, useRef, useState } from 'react' +import { ApiResult, AddDataResponse, DBConnection, DBQueryData, Tab } from '../../../data/models' +import toast from 'react-hot-toast' +import CodeMirror, { ReactCodeMirrorRef } from '@uiw/react-codemirror' +import { javascript } from '@codemirror/lang-javascript' +import { useAppDispatch, useAppSelector } from '../../../redux/hooks' +import { addDBData, selectQueryData, setQueryData } from '../../../redux/dataModelSlice' +import TabContext from '../../layouts/tabcontext' + +type AddModal = { + dbConnection: DBConnection + mName: string, + onClose: () => void +} + +const AddModal = ({ dbConnection, mName, onClose }: AddModal) => { + + const dispatch = useAppDispatch() + + const activeTab: Tab = useContext(TabContext)! + const queryData = useAppSelector(selectQueryData) + + const editorRef = useRef(null); + const [newData, setNewData] = useState(`{\n\t\n}`) + + const startAdding = async () => { + let jsonData: any + try { + jsonData = JSON.parse(newData) + } catch (e: any) { + toast.error(e.message) + return + } + const result: ApiResult = await dispatch(addDBData({ tabId: activeTab.id, dbConnectionId: dbConnection.id, schemaName: "", name: mName, data: jsonData })).unwrap() + if (result.success) { + toast.success('data added') + let mNewData = { _id: result.data.newId, ...jsonData } + const updatedRows = [mNewData, ...queryData!.data] + const updateQueryData: DBQueryData = { ...queryData!, data: updatedRows } + dispatch(setQueryData({ data: updateQueryData, tabId: activeTab.id })) + onClose() + } else { + toast.error(result.error!) + } + } + + const onChange = React.useCallback((value: any) => { + setNewData(value) + }, []); + + + return (
+
+
+
+

Add new data to {mName}

+ +
+
+ +
+
+ + +
+
+
) +} + +export default AddModal \ No newline at end of file diff --git a/frontend-server/src/components/dbfragments/jsontable/jsoncell.tsx b/frontend-server/src/components/dbfragments/jsontable/jsoncell.tsx new file mode 100644 index 00000000..99d1403a --- /dev/null +++ b/frontend-server/src/components/dbfragments/jsontable/jsoncell.tsx @@ -0,0 +1,88 @@ +import styles from './jsontable.module.scss' +import React, { useRef } from 'react' +import { js_beautify } from 'js-beautify' +import _ from 'lodash' +import toast from 'react-hot-toast' +import ReactJson from 'react-json-view' +import ReactCodeMirror, { ReactCodeMirrorRef } from '@uiw/react-codemirror' +import { javascript } from '@codemirror/lang-javascript' + + +const JsonCell = ({ + row: { index, original }, + editingCellIndex, + startEditing, + onSaveCell +}: any) => { + // We need to keep and update the state of the cell normally + const [value, setValue] = React.useState(original) + const [editingValue, setEditingValue] = React.useState(JSON.stringify(original)) + + const editorRef = useRef(null); + + // If the initialValue is changed external, sync it up with our state + React.useEffect(() => { + setValue(original) + setEditingValue(JSON.stringify(original)) + }, [original]) + + const onChange = React.useCallback((value: any) => { + setEditingValue(value) + }, []); + + const isEditing: boolean = editingCellIndex == index + + if (isEditing) { + + const cancelEdit = () => { + setValue(original) + startEditing(null) + } + + const onSave = async () => { + let jsonData: any + try { + jsonData = JSON.parse(editingValue) + } catch (e: any) { + toast.error(e.message) + return + } + onSaveCell(jsonData._id, JSON.stringify(_.omit(jsonData, ['_id']))) + } + + return ( + +
+ +    + +
+
) + } + + return ( + + ) +} + +export default JsonCell \ No newline at end of file diff --git a/frontend-server/src/components/dbfragments/jsontable/jsontable.module.scss b/frontend-server/src/components/dbfragments/jsontable/jsontable.module.scss new file mode 100644 index 00000000..9a9c2030 --- /dev/null +++ b/frontend-server/src/components/dbfragments/jsontable/jsontable.module.scss @@ -0,0 +1,6 @@ +.tableHeader { + padding: 15px; + border-top: 1px solid #d4d4d4; + border-left: 1px solid #d4d4d4; + border-right: 1px solid #d4d4d4; +} \ No newline at end of file diff --git a/frontend-server/src/components/dbfragments/jsontable/jsontable.tsx b/frontend-server/src/components/dbfragments/jsontable/jsontable.tsx new file mode 100644 index 00000000..78484cd0 --- /dev/null +++ b/frontend-server/src/components/dbfragments/jsontable/jsontable.tsx @@ -0,0 +1,278 @@ +import styles from './jsontable.module.scss' +import React, { useContext, useRef, useState } from 'react' +import { useRowSelect, useTable } from 'react-table' +import { DBConnection, DBQueryData, Tab } from '../../../data/models' +import JsonCell from './jsoncell' +import AddModal from './addmodel' +import toast from 'react-hot-toast' +import ConfirmModal from '../../widgets/confirmModal' +import { useAppDispatch } from '../../../redux/hooks' +import { deleteDBData, setQueryData, updateDBSingleData } from '../../../redux/dataModelSlice' +import TabContext from '../../layouts/tabcontext' + +type JsonTablePropType = { + queryData: DBQueryData, + dbConnection: DBConnection + mName: string, + isInteractive: boolean, + showHeader?: boolean, + onFilterChanged: (newFilter: string[] | undefined) => void, + onSortChanged: (newSort: string[] | undefined) => void, +} + +const JsonTable = ({ queryData, dbConnection, mName, isInteractive, showHeader, onFilterChanged, onSortChanged }: JsonTablePropType) => { + + const dispatch = useAppDispatch() + + const activeTab: Tab = useContext(TabContext)! + + const [isAdding, setIsAdding] = useState(false) + const [isEditing, setIsEditing] = useState(false) + const [isDeleting, setIsDeleting] = useState(false) + const [editingCellIndex, setEditingCellIndex] = useState<(number | null)>(null) + + const filterRef = useRef(null); + const sortRef = useRef(null); + + const data = React.useMemo( + () => queryData.data, + [queryData] + ) + + const displayColumns = ["data"] + + const columns = React.useMemo( + () => displayColumns.map((col, i) => ({ + Header: <>{col}, + accessor: (i).toString(), + })), + [queryData] + ) + + const defaultColumn = { + Cell: JsonCell, + } + + const startEditing = (index: number | null) => { + if (!(isInteractive && isEditing)) { + return + } + setEditingCellIndex(index) + } + + const changeFilter = () => { + let filter: string[] | undefined = undefined + let filterText = filterRef.current!.value.trim() + if (filterText !== '' && filterText.startsWith("{") && filterText.endsWith("}")) { + filter = [filterText] + } + onFilterChanged(filter) + } + + const changeSort = () => { + if (!isInteractive) { + return + } + let sort: string[] | undefined = undefined + let sortText = sortRef.current!.value.trim() + if (sortText !== '' && sortText.startsWith("{") && sortText.endsWith("}")) { + sort = [sortText] + } + onSortChanged(sort) + } + + + const onSaveCell = async (underscoreId: string, newData: string) => { + const result = await dispatch(updateDBSingleData({ dbConnectionId: dbConnection.id, schemaName: "", name: mName, id: underscoreId, columnName: "", newValue: newData })).unwrap() + if (result.success) { + const rowIdx = queryData!.data.findIndex(x => x["_id"] == underscoreId) + if (rowIdx) { + const newQueryData: DBQueryData = { ...queryData!, data: [...queryData!.data] } + newQueryData!.data[rowIdx] = { _id: underscoreId, ...JSON.parse(newData) } + dispatch(setQueryData({ data: newQueryData, tabId: activeTab.id })) + } else { + // fetchData(false) + } + startEditing(null) + toast.success('1 row updated'); + } else { + toast.error(result.error!); + } + } + + const { + getTableProps, + getTableBodyProps, + headerGroups, + rows, + prepareRow, + state, + } = useTable({ + columns, + data, + defaultColumn, + ...{ editingCellIndex, startEditing, onSaveCell } + }, useRowSelect, hooks => { + if (isInteractive && isEditing) + hooks.visibleColumns.push(columns => [ + { + id: 'selection', + Header: HeaderSelectionComponent, + Cell: CellSelectionComponent, + }, + ...columns, + ] + ) + }) + + const newState: any = state // temporary typescript hack + const selectedRows: number[] = Object.keys(newState.selectedRowIds).map(x => parseInt(x)) + const selectedUnderscoreIDs = rows.filter((_, i) => selectedRows.includes(i)).map(x => x.original['_id']).filter(x => x) + + const deleteRows = async () => { + if (selectedUnderscoreIDs.length > 0) { + const result = await dispatch(deleteDBData({ dbConnectionId: dbConnection.id, schemaName: "", name: mName, selectedIDs: selectedUnderscoreIDs })).unwrap() + if (result.success) { + toast.success('rows deleted'); + const filteredRows = queryData!.data.filter((_, i) => !selectedRows.includes(i)) + const newQueryData: DBQueryData = { ...queryData!, data: filteredRows } + dispatch(setQueryData({ data: newQueryData, tabId: activeTab.id })) + } else { + toast.error(result.error!); + } + } + setIsDeleting(false) + } + + return ( + + {(showHeader || (isInteractive && isEditing)) &&
+
+
+
+

+ +

+

+ +

+
+
+
+
+

+ +

+

+ +

+
+
+ {isInteractive && !isEditing && +
+ +
+
} + {isInteractive && isEditing && +
+ +    + +    + +
+
} +
+
} + {isAdding && + { setIsAdding(false) }} /> + } + {isDeleting && { setIsDeleting(false) }} />} +
+ + + {headerGroups.map(headerGroup => ( + + {headerGroup.headers.map(column => ( + + ))} + + ))} + + + {rows.map((row, rowIndex) => { + prepareRow(row) + const selectedRow: any = row // temp type hack + return ( + + {row.cells.map(cell => { + return ( + ) + })} + + ) + })} + +
+ {column.render('Header')} +
startEditing(rowIndex)} key={row.id + "" + cell.column.id}> + {cell.render('Cell')} +
+
+
+ ) +} + +const IndeterminateCheckbox = React.forwardRef( + ({ indeterminate, ...rest }, ref) => { + const defaultRef = React.useRef() + const resolvedRef: any = ref || defaultRef + + React.useEffect(() => { + resolvedRef.current.indeterminate = indeterminate + }, [resolvedRef, indeterminate]) + + return ( + <> + + + ) + } +) +IndeterminateCheckbox.displayName = 'IndeterminateCheckbox'; + +const HeaderSelectionComponent = ({ getToggleAllRowsSelectedProps }: any) => ( +
+ +
+) + +const CellSelectionComponent = ({ row }: any) => ( +
+ +
+) + +export default JsonTable \ No newline at end of file diff --git a/frontend-server/src/components/dbfragments/query.module.scss b/frontend-server/src/components/dbfragments/query.module.scss new file mode 100644 index 00000000..e69de29b diff --git a/frontend-server/src/components/dbfragments/query.tsx b/frontend-server/src/components/dbfragments/query.tsx new file mode 100644 index 00000000..438a9270 --- /dev/null +++ b/frontend-server/src/components/dbfragments/query.tsx @@ -0,0 +1,146 @@ +import styles from './query.module.scss' +import React, { useContext, useEffect, useState } from 'react' +import toast from 'react-hot-toast' +import { DBConnection, DBQuery, DBQueryData, DBQueryResult, Tab } from '../../data/models' +import QueryEditor from './queryeditor/queryeditor' +import { selectDBConnection } from '../../redux/dbConnectionSlice' +import { useAppDispatch, useAppSelector } from '../../redux/hooks' +import { DBConnType, TabType } from '../../data/defaults' +import JsonTable from './jsontable/jsontable' +import Table from './table/table' +import Chart from './chart/chart' +import { getDBQuery, runQuery, selectDBQuery, setDBQuery } from '../../redux/dbQuerySlice' +import { closeTab, updateActiveTab } from '../../redux/tabsSlice' +import TabContext from '../layouts/tabcontext' + + +type DBQueryPropType = { +} + +const DBQueryFragment = (_: DBQueryPropType) => { + + const dispatch = useAppDispatch() + + const dbConnection: DBConnection | undefined = useAppSelector(selectDBConnection) + const dbQuery = useAppSelector(selectDBQuery) + + const currentTab: Tab = useContext(TabContext)! + + const [queryData, setQueryData] = useState() + const [queryResult, setQueryResult] = useState() + const [isChartEnabled, setIsChartEnabled] = useState(false) + + const queryId = currentTab.metadata.queryId + const tabQuery = currentTab.metadata.query + + useEffect(() => { + (async () => { + if (queryId && queryId !== 'new') { + dispatch(getDBQuery({ queryId: String(queryId), tabId: currentTab.id })) + } + if (queryId === 'new') { + dispatch(setDBQuery({ data: undefined, tabId: currentTab.id })) + } + })() + }, [dispatch, queryId]) + + + useEffect(() => { + setQueryData(undefined) + setQueryResult(undefined) + setIsChartEnabled(false) + }, [queryId]) + + const onRunQueryBtn = async (query: string, callback: () => void) => { + const result = await dispatch(runQuery({ dbConnectionId: dbConnection!.id, query })).unwrap() + if (result.success) { + toast.success('Success') + if ((result.data as DBQueryResult).message) { + setQueryResult(result.data as DBQueryResult) + setQueryData(undefined) + } else { + setQueryData(result.data as DBQueryData) + setQueryResult(undefined) + } + } else { + toast.error(result.error!) + } + callback() + } + + const toggleIsChartEnabled = () => { + setIsChartEnabled(!isChartEnabled) + } + + const onQuerySaved = (queryId: string, query: string) => { + dispatch(updateActiveTab({ tabType: TabType.QUERY, metadata: { queryId: queryId, query: query } })) + } + + const onDelete = () => { + dispatch(closeTab({ dbConnId: dbConnection!.id, tabId: currentTab.id })) + } + + return ( +
+ {(dbConnection && ((queryId === 'new' && !dbQuery) || (dbQuery && dbQuery.id === queryId))) && + + } +
+ {queryData &&
+