From af36b16ece781d4e40a00f54506e6b94ed9c5199 Mon Sep 17 00:00:00 2001 From: Star Date: Fri, 11 Oct 2024 11:32:50 +0800 Subject: [PATCH] 1 --- .gitignore | 5 ++ LICENSE | 9 ++ README.md | 169 +++++++++++++++++++++++++++++++++++++ db.go | 240 +++++++++++++++++++++++++++++++++++++++++++++++++++++ db.ts | 82 ++++++++++++++++++ db_test.go | 32 +++++++ db_test.js | 13 +++ go.mod | 40 +++++++++ 8 files changed, 590 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 db.go create mode 100644 db.ts create mode 100644 db_test.go create mode 100644 db_test.js create mode 100644 go.mod diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..867e853 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.* +!.gitignore +go.sum +node_modules +package.json diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d894367 --- /dev/null +++ b/LICENSE @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) 2024 apigo + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..d65c216 --- /dev/null +++ b/README.md @@ -0,0 +1,169 @@ +# db for GoJS + +## set db config in env.yml + +```yaml +db: + default: sqlite3://test.db + db2: mysql://root:zKeL8Qhs09tDvg4pYab0zg==@localhost:3306/test +``` + +### more config see [ssgo/db](https://github.com/ssgo/db) + +set default connection can use db.query, db.xxxx directly + +or set default connection by db.setDefault('sqlite3://test.db') + +## encrypt password + +```shell +go install github.com/ssgo/tool/sskey@latest +sskey -e 'your password' | grep 'url base64' +``` + +use sskey to encrypt password, then use url base64 to encode it. + +if you want use custom sskey secret + +add code to your project + +```go +import "github.com/ssgo/db" +func setSSKey(key, iv []byte){ + db.SetEncryptKeys(key, iv) +} +``` + +generate setSSKey.go into your project + +```shell +sskey -c your_secret_name +sskey -go your_secret_name > setSSKey.go +``` + +## set db config in env for docker + +```shell +docker run -e DB_DEFAULT=sqlite3://test.db -e DB2="mysql://root:zKeL8Qhs09tDvg4pYab0zg==@localhost:3306/test" +``` + +## import db driver in use + +```go +_ "modernc.org/sqlite" +_ "github.com/go-sql-driver/mysql" +``` + +## create or update db from simple description + +```javascript +import db from 'apigo.cc/gojs/db' + +db.make(` +// Account + +User // User +id c12 PK // User ID +phone v20 U // Phone Number +password v80 n // Password +salt v50 // Salt +name v100 n // Name +serverKey v200 // Server Key +isValid b // Is Valid + +Device // Device +id v30 PK // Device ID +userId c12 // Current User +salt v50 // Salt +secretTime dt // Secret Generation Time + +// Log + +LoginLog // Login Log +id ubi AI // Login ID +way v20 // Login Method (verifyCode/autoLogin/oneClickLogin) +userId c12 I // Current User +deviceId v30 I // Device ID +time dt I // Login Time +userAgent v200 // Device Information +requestId v20 // Request ID +sessionId v20 // Session ID +successful b // Was Successful +message v1024 // Failure Message +`) +``` + +### field types + +``` +c => char +v => varchar +dt => datetime +d => date +tm => time +i => int +ui => int unsigned +ti => tinyint +uti => tinyint unsigned +b => tinyint unsigned +bi => bigint +ubi => bigint unsigned +f => float +uf => float unsigned +ff => double +uff => double unsigned +si => smallint +usi => smallint unsigned +mi => middleint +umi => middleint unsigned +t => text +bb => blob +``` + +### index (mulit field use I1 for make mulit index named 1) + +``` +PK => PRIMARY KEY NOT NULL +AI => PRIMARY KEY AUTOINCREMENT NOT NULL +I => index +U => unique +TI => fulltext +``` + +### more defile doc see [ssgo/dao](https://github.com/ssgo/dao) + +## example + +```javascript +import db from 'apigo.cc/gojs/db' +import console from 'console' + +let r1 = db.query('select * from user limit 10') +console.log(r1.result) + +let conn2 = db.get('db2') +let r2 = conn2.query('select * from user limit 10') +console.log(r2.result) + +let conn3 = db.get('sqlite3://test.db') +``` + +## module.exports + +```ts +function get(dbName: string): DB {return null as any} +function setDefault(dbName: string): void {} +function make(descFileOrContent: string): Array{return null as any} +function query(sql: string, ...args:any): QueryResult{return null as any} +function query1(sql: string, ...args:any): QueryResult1{return null as any} +function query11(sql: string, ...args:any): QueryResult11{return null as any} +function exec(sql: string, ...args:any): ExecResult{return null as any} +function insert(table: string, data:Object): ExecResult{return null as any} +function replace(table: string, data:Object): ExecResult{return null as any} +function update(table: string, data:Object, where: string, ...args:any): ExecResult{return null as any} +function delete_(table: string, where: string, ...args:any): ExecResult{return null as any} +function destroy(): void{} +function begin(): Tx{return null as any} +``` + +## full api see [db.ts](https://apigo.cc/gojs/db/db.ts) diff --git a/db.go b/db.go new file mode 100644 index 0000000..8102e68 --- /dev/null +++ b/db.go @@ -0,0 +1,240 @@ +package db + +import ( + _ "embed" + + "apigo.cc/gojs" + "apigo.cc/gojs/goja" + "github.com/ssgo/dao/dao" + "github.com/ssgo/db" + "github.com/ssgo/log" + "github.com/ssgo/u" +) + +//go:embed db.ts +var dbTS string + +//go:embed README.md +var dbMD string + +func init() { + obj := map[string]any{ + "get": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { + args := gojs.MakeArgs(&argsIn, vm).Check(1) + conn := db.GetDB(args.Str(0), args.Logger) + return vm.ToValue(makeDBObject(conn, nil)) + }, + "setDefault": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { + args := gojs.MakeArgs(&argsIn, vm).Check(1) + conn := db.GetDB(args.Str(0), args.Logger) + args.This.ToObject(vm).Set("conn", conn) + return nil + }, + } + + gojs.Register("apigo.cc/gojs/db", gojs.Module{ + ObjectMaker: func(vm *goja.Runtime) gojs.Map { + conn := db.GetDB("default", gojs.GetLogger(vm)) + dbObj := makeDBObject(conn, nil) + for k, v := range obj { + dbObj[k] = v + } + return dbObj + }, + Desc: "db api by github.com/ssgo/db", + TsCode: dbTS, + Example: dbMD, + }) +} + +func makeDBObject(conn *db.DB, tx *db.Tx) map[string]any { + obj := map[string]any{ + "conn": conn, + "tx": tx, + "query": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { + args, conn, tx, _ := initDBArgs(argsIn, vm, 1) + var r *db.QueryResult + if tx != nil { + r = tx.Query(args.Str(0), args.Array(1)...) + } else { + r = conn.Query(args.Str(0), args.Array(1)...) + } + if r.Error == nil { + return vm.ToValue(makeQueryResult(r, r.MapResults())) + } else { + panic(vm.NewGoError(r.Error)) + } + }, + "query1": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { + args, conn, tx, _ := initDBArgs(argsIn, vm, 1) + var r *db.QueryResult + if tx != nil { + r = tx.Query(args.Str(0), args.Array(1)...) + } else { + r = conn.Query(args.Str(0), args.Array(1)...) + } + if r.Error == nil { + return vm.ToValue(makeQueryResult(r, r.MapOnR1())) + } else { + panic(vm.NewGoError(r.Error)) + } + }, + "query11": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { + args, conn, tx, _ := initDBArgs(argsIn, vm, 1) + var r *db.QueryResult + if tx != nil { + r = tx.Query(args.Str(0), args.Array(1)...) + } else { + r = conn.Query(args.Str(0), args.Array(1)...) + } + if r.Error == nil { + a := r.SliceResults() + if len(a) > 0 && len(a[0]) > 0 { + return vm.ToValue(makeQueryResult(r, a[0][0])) + } + return vm.ToValue(nil) + } else { + panic(vm.NewGoError(r.Error)) + } + }, + "exec": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { + args, conn, tx, _ := initDBArgs(argsIn, vm, 1) + var r *db.ExecResult + if tx != nil { + r = tx.Exec(args.Str(0), args.Array(1)...) + } else { + r = conn.Exec(args.Str(0), args.Array(1)...) + } + if r.Error == nil { + return vm.ToValue(makeExecResult(r)) + } else { + panic(vm.NewGoError(r.Error)) + } + }, + "insert": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { + args, conn, tx, _ := initDBArgs(argsIn, vm, 2) + var r *db.ExecResult + if tx != nil { + r = tx.Insert(args.Str(0), args.Any(1)) + } else { + r = conn.Insert(args.Str(0), args.Any(1)) + } + if r.Error == nil { + return vm.ToValue(makeExecResult(r)) + } else { + panic(vm.NewGoError(r.Error)) + } + }, + "replace": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { + args, conn, tx, _ := initDBArgs(argsIn, vm, 2) + var r *db.ExecResult + if tx != nil { + r = tx.Replace(args.Str(0), args.Any(1)) + } else { + r = conn.Replace(args.Str(0), args.Any(1)) + } + if r.Error == nil { + return vm.ToValue(makeExecResult(r)) + } else { + panic(vm.NewGoError(r.Error)) + } + }, + "update": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { + args, conn, tx, _ := initDBArgs(argsIn, vm, 3) + var r *db.ExecResult + if tx != nil { + r = tx.Update(args.Str(0), args.Any(1), args.Str(2), args.Array(3)...) + } else { + r = conn.Update(args.Str(0), args.Any(1), args.Str(2), args.Array(3)...) + } + if r.Error == nil { + return vm.ToValue(makeExecResult(r)) + } else { + panic(vm.NewGoError(r.Error)) + } + }, + "delete": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { + args, conn, tx, _ := initDBArgs(argsIn, vm, 2) + var r *db.ExecResult + if tx != nil { + r = tx.Delete(args.Str(0), args.Str(1), args.Array(2)...) + } else { + r = conn.Delete(args.Str(0), args.Str(1), args.Array(2)...) + } + if r.Error == nil { + return vm.ToValue(makeExecResult(r)) + } else { + panic(vm.NewGoError(r.Error)) + } + }, + } + + if conn != nil { + obj["make"] = func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { + args, conn, _, logger := initDBArgs(argsIn, vm, 1) + arg0 := args.Str(0) + tryFile := gojs.FindPath(vm, arg0) + if u.FileExists(tryFile) { + arg0 = u.ReadFileN(tryFile) + } + if err := dao.MakeDBFromDesc(conn, args.Str(0), logger); err == nil { + return nil + } else { + panic(vm.NewGoError(err)) + } + } + + obj["destroy"] = func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { + _, conn, _, _ := initDBArgs(argsIn, vm, 0) + if err := conn.Destroy(); err == nil { + return nil + } else { + panic(vm.NewGoError(err)) + } + } + + obj["begin"] = func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { + _, conn, _, _ := initDBArgs(argsIn, vm, 0) + return vm.ToValue(makeDBObject(nil, conn.Begin())) + } + } + + if tx != nil { + obj["end"] = func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { + args, _, tx, _ := initDBArgs(argsIn, vm, 1) + if err := tx.Finish(args.Bool(0)); err != nil { + panic(vm.NewGoError(err)) + } + return nil + } + } + return obj +} + +func makeQueryResult(r *db.QueryResult, result any) map[string]any { + return map[string]any{ + "sql": *r.Sql, + "args": r.Args, + "result": result, + } +} + +func makeExecResult(r *db.ExecResult) map[string]any { + return map[string]any{ + "sql": *r.Sql, + "args": r.Args, + "id": r.Id(), + "changes": r.Changes(), + } +} + +func initDBArgs(argsIn goja.FunctionCall, vm *goja.Runtime, checkArgsNum int) (*gojs.Arr, *db.DB, *db.Tx, *log.Logger) { + args := gojs.MakeArgs(&argsIn, vm).Check(checkArgsNum) + logger := args.Logger + conn, _ := args.This.ToObject(vm).Get("conn").Export().(*db.DB) + var tx *db.Tx + if conn == nil { + tx, _ = args.This.ToObject(vm).Get("tx").Export().(*db.Tx) + } + return args, conn, tx, logger +} diff --git a/db.ts b/db.ts new file mode 100644 index 0000000..e82f6fc --- /dev/null +++ b/db.ts @@ -0,0 +1,82 @@ +// just for develop + +export default { + get, + setDefault, + make, + query, + query1, + query11, + exec, + insert, + replace, + update, + delete: delete_, + destroy, + begin, +} + +function get(dbName: string): DB {return null as any} +function setDefault(dbName: string): void {} +function make(descFileOrContent: string): Array{return null as any} +function query(sql: string, ...args:any): QueryResult{return null as any} +function query1(sql: string, ...args:any): QueryResult1{return null as any} +function query11(sql: string, ...args:any): QueryResult11{return null as any} +function exec(sql: string, ...args:any): ExecResult{return null as any} +function insert(table: string, data:Object): ExecResult{return null as any} +function replace(table: string, data:Object): ExecResult{return null as any} +function update(table: string, data:Object, where: string, ...args:any): ExecResult{return null as any} +function delete_(table: string, where: string, ...args:any): ExecResult{return null as any} +function destroy(): void{} +function begin(): Tx{return null as any} + +interface DB { + make(descFileOrContent: string): Array + query(sql: string, ...args:any): QueryResult + query1(sql: string, ...args:any): QueryResult1 + query11(sql: string, ...args:any): QueryResult11 + exec(sql: string, ...args:any): ExecResult + insert(table: string, data:Object): ExecResult + replace(table: string, data:Object): ExecResult + update(table: string, data:Object, where: string, ...args:any): ExecResult + delete(table: string, where: string, ...args:any): ExecResult + destroy(): void + begin(): Tx +} + +interface Tx { + query(sql: string, ...args:any): QueryResult + query1(sql: string, ...args:any): QueryResult1 + query11(sql: string, ...args:any): QueryResult11 + exec(sql: string, ...args:any): ExecResult + insert(table: string, data:Object): ExecResult + replace(table: string, data:Object): ExecResult + update(table: string, data:Object, where: string, ...args:any): ExecResult + delete(table: string, where: string, ...args:any): ExecResult + end(ok:boolean): void +} + +interface QueryResult { + sql: string + args: Array + result: Array +} + +interface QueryResult1 { + sql: string + args: Array + result: Object +} + +interface QueryResult11 { + sql: string + args: Array + result: any +} + +interface ExecResult { + sql: string + args: Array + id: number + changes: number +} diff --git a/db_test.go b/db_test.go new file mode 100644 index 0000000..55573d0 --- /dev/null +++ b/db_test.go @@ -0,0 +1,32 @@ +package db_test + +import ( + "fmt" + "os" + "testing" + + "apigo.cc/gojs" + _ "apigo.cc/gojs/db" + "github.com/ssgo/u" + _ "modernc.org/sqlite" +) + +func Test(t *testing.T) { + // gojs.ExportForDev() + defer os.Remove("test.db") + r, err := gojs.RunFile("db_test.js", "Tom") + if err != nil { + t.Fatal(err) + } else { + user := struct { + Id int + Name string + }{} + u.Convert(r, &user) + if user.Id != 1 || user.Name != "Tom" { + t.Fatal("user info is error", r) + } else { + fmt.Println(u.BGreen("test succeess")) + } + } +} diff --git a/db_test.js b/db_test.js new file mode 100644 index 0000000..155edfe --- /dev/null +++ b/db_test.js @@ -0,0 +1,13 @@ +import db from 'apigo.cc/gojs/db' + +function main(testUserName){ + db.setDefault('sqlite://test.db') + db.make(` + User + id i AI + name t + `) + db.insert('User', {name: testUserName}) + let user = db.query1('SELECT * FROM User').result + return user +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..f1c2f63 --- /dev/null +++ b/go.mod @@ -0,0 +1,40 @@ +module apigo.cc/gojs/db + +go 1.18 + +require ( + apigo.cc/gojs v0.0.1 + github.com/ssgo/dao v0.1.5 + github.com/ssgo/db v1.7.9 + github.com/ssgo/log v1.7.7 + github.com/ssgo/u v1.7.9 + modernc.org/sqlite v1.33.1 +) + +require ( + filippo.io/edwards25519 v1.1.0 // indirect + github.com/dlclark/regexp2 v1.11.4 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/go-sourcemap/sourcemap v2.1.4+incompatible // indirect + github.com/go-sql-driver/mysql v1.8.1 // indirect + github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/ncruces/go-strftime v0.1.9 // indirect + github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect + github.com/ssgo/config v1.7.7 // indirect + github.com/ssgo/standard v1.7.7 // indirect + github.com/ssgo/tool v0.4.27 // indirect + golang.org/x/sys v0.26.0 // indirect + golang.org/x/text v0.19.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect + modernc.org/libc v1.55.3 // indirect + modernc.org/mathutil v1.6.0 // indirect + modernc.org/memory v1.8.0 // indirect + modernc.org/strutil v1.2.0 // indirect + modernc.org/token v1.1.0 // indirect +)