This commit is contained in:
Star 2024-10-11 11:32:50 +08:00
commit af36b16ece
8 changed files with 590 additions and 0 deletions

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
.*
!.gitignore
go.sum
node_modules
package.json

9
LICENSE Normal file
View File

@ -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.

169
README.md Normal file
View File

@ -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<Object>{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)

240
db.go Normal file
View File

@ -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
}

82
db.ts Normal file
View File

@ -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<Object>{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<Object>
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<any>
result: Array<Object>
}
interface QueryResult1 {
sql: string
args: Array<any>
result: Object
}
interface QueryResult11 {
sql: string
args: Array<any>
result: any
}
interface ExecResult {
sql: string
args: Array<any>
id: number
changes: number
}

32
db_test.go Normal file
View File

@ -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"))
}
}
}

13
db_test.js Normal file
View File

@ -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
}

40
go.mod Normal file
View File

@ -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
)