--- /dev/null
+.vscode/
+.DS_Store
+*.so
+*.log
+*.exe
+*.zip
+*.har
+*.swp
+.git.*/
\ No newline at end of file
--- /dev/null
+# uzoomBox OpenSDK
+
+OpenSDK of open.uzoombox.com, for writting plugins of uzoombox with golang.
+
+## Contents
+
+- [uzoomBox OpenSDK](#uzoombox-opensdk)
+ - [Contents](#contents)
+ - [Installation](#installation)
+ - [Quick start](#quick-start)
+ - [Testing](#testing)
+
+## Installation
+
+To install OpenSDK package, you need to install Go and set your Go workspace first.
+
+1. You first need [Go](https://golang.org/) installed (**version 1.14+ is required**), then you can use the below Go command to install OpenSDK.
+
+```sh
+$ go get -u git.uzoombox.com/git/uzsdk
+```
+
+2. Import it in your code:
+
+```go
+import "git.uzoombox.com/git/uzsdk"
+```
+
+## Quick start
+
+```sh
+# assume the following codes in example.go file
+$ cat example.go
+```
+
+```go
+package main
+
+import (
+ "git.uzoombox.com/git/uzsdk"
+)
+
+func main() {
+}
+```
+
+```
+# run example.go
+$ go run example.go
+```
+
+
+## Testing
--- /dev/null
+// Copyright 2023 nanzoom.com. All Rights Reserved.
+
+package uzsdk
+
+// Application defines structure of application's information.
+type Application struct {
+ NAME string
+ VENDOR string
+ AUTHOR string
+ VERSION string
+ RELEASE string
+ COPYRIGHT string
+}
--- /dev/null
+module git.uzoombox.com/git/uzsdk
+
+go 1.18
+
+require (
+ github.com/gin-gonic/gin v1.9.1
+ github.com/jmoiron/sqlx v1.3.5
+)
+
+require (
+ github.com/bytedance/sonic v1.9.1 // indirect
+ github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
+ github.com/gabriel-vasile/mimetype v1.4.2 // indirect
+ github.com/gin-contrib/sse v0.1.0 // indirect
+ github.com/go-playground/locales v0.14.1 // indirect
+ github.com/go-playground/universal-translator v0.18.1 // indirect
+ github.com/go-playground/validator/v10 v10.14.0 // indirect
+ github.com/goccy/go-json v0.10.2 // indirect
+ github.com/json-iterator/go v1.1.12 // indirect
+ github.com/klauspost/cpuid/v2 v2.2.4 // indirect
+ github.com/leodido/go-urn v1.2.4 // indirect
+ github.com/mattn/go-isatty v0.0.19 // indirect
+ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
+ github.com/modern-go/reflect2 v1.0.2 // indirect
+ github.com/pelletier/go-toml/v2 v2.0.8 // indirect
+ github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
+ github.com/ugorji/go/codec v1.2.11 // indirect
+ golang.org/x/arch v0.3.0 // indirect
+ golang.org/x/crypto v0.9.0 // indirect
+ golang.org/x/net v0.10.0 // indirect
+ golang.org/x/sys v0.8.0 // indirect
+ golang.org/x/text v0.9.0 // indirect
+ google.golang.org/protobuf v1.30.0 // indirect
+ gopkg.in/yaml.v3 v3.0.1 // indirect
+)
--- /dev/null
+github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
+github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
+github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
+github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
+github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
+github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
+github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
+github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
+github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
+github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
+github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
+github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
+github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
+github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
+github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
+github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
+github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js=
+github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
+github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
+github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
+github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
+github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
+github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
+github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
+github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
+github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
+github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
+github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
+github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
+github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
+github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
+github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
+github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
+github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
+github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg=
+github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
+github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ=
+github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+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.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+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/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
+github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
+github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
+github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
+github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
+github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
+golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
+golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
+golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
+golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
+golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
+golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
+golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
+golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
+golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
+golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
+google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
+google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/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=
+rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
--- /dev/null
+// Copyright 2023 nanzoom.com. All Rights Reserved.
+
+package logger
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "log"
+ "os"
+ "path/filepath"
+ "runtime"
+ "sync"
+ "time"
+)
+
+const (
+ Fatal = iota // fatal level.
+ Error
+ Warn
+ Info
+ Debug
+)
+
+type Logger struct {
+ // params
+ filepath string
+ level int
+ prefix string
+ maxline int64
+
+ // status
+ mutex sync.Mutex
+ out *os.File
+ linenum int64
+}
+
+// New creates a new Logger, the path is the filepath of logfile,
+// logv is an string in ['fatal','error','warn','info','debug'],
+// prefix is the content write after timestamp and before message,
+// maxline[default: 100000] create a new logfile if linenum exceed it.
+func New(path, logv, prefix string, options ...interface{}) *Logger {
+ level := Info
+ switch logv {
+ case "debug":
+ level = Debug
+ case "info":
+ level = Info
+ case "warn":
+ level = Warn
+ case "error":
+ level = Error
+ case "fatal":
+ level = Fatal
+ }
+
+ maxline := int64(100000)
+ for _, option := range options {
+ switch option.(type) {
+ case int64:
+ maxline = option.(int64)
+ }
+ }
+
+ linenum := int64(0)
+ if path != "" {
+ if r, err := os.Open(path); err == nil {
+ linenum, _ = lineCounter(r)
+ }
+ }
+ return &Logger{
+ filepath: path,
+ level: level,
+ prefix: prefix,
+ maxline: maxline,
+ out: nil,
+ linenum: linenum,
+ }
+}
+
+// Fatal calls output(Fatal, Sprint(v)) to print the log.
+func (l *Logger) Fatal(v ...interface{}) {
+ l.Output(Fatal, "FATAL", fmt.Sprint(v...))
+}
+
+// Fatalf calls output(Fatal, Sprintf(format, v)) to print the log.
+func (l *Logger) Fatalf(format string, v ...interface{}) {
+ l.Output(Fatal, "FATAL", fmt.Sprintf(format, v...))
+}
+
+// Error calls output(Error) to print the log.
+func (l *Logger) Error(v ...interface{}) {
+ l.Output(Error, "ERROR", fmt.Sprint(v...))
+}
+
+// Errorf calls output(Error, Sprintf(format, v)) to print the log.
+func (l *Logger) Errorf(format string, v ...interface{}) {
+ l.Output(Error, "ERROR", fmt.Sprintf(format, v...))
+}
+
+// Warn calls output(Warn) to print the log.
+func (l *Logger) Warn(v ...interface{}) {
+ l.Output(Warn, "WARN", fmt.Sprint(v...))
+}
+
+// Warnf calls output(Warn, Sprintf(format, v)) to print the log.
+func (l *Logger) Warnf(format string, v ...interface{}) {
+ l.Output(Warn, "WARN", fmt.Sprintf(format, v...))
+}
+
+// Info calls output(Info) to print the log.
+func (l *Logger) Info(v ...interface{}) {
+ l.Output(Info, "INFO", fmt.Sprint(v...))
+}
+
+// Infof calls output(Info, Sprintf(format, v)) to print the log.
+func (l *Logger) Infof(format string, v ...interface{}) {
+ l.Output(Info, "INFO", fmt.Sprintf(format, v...))
+}
+
+// Debug calls output(Debug) to print the log.
+func (l *Logger) Debug(v ...interface{}) {
+ l.Output(Debug, "DEBUG", fmt.Sprint(v...))
+}
+
+// Debugf calls output(Debug, Sprintf(format, v)) to print the log.
+func (l *Logger) Debugf(format string, v ...interface{}) {
+ l.Output(Debug, "DEBUG", fmt.Sprintf(format, v...))
+}
+
+// Output writes the output for a logging event. The string ltype contains
+// the type of log and the string msg contains the text to print the Logger.
+func (l *Logger) Output(level int, ltype, msg string) {
+ timestamp := time.Now().Format(time.Stamp)
+ if level > l.level {
+ return
+ }
+ _, file, line, ok := runtime.Caller(2)
+ if !ok {
+ file = "???"
+ line = 0
+ }
+ text := fmt.Sprintf("[%v] %v %v %v:%v: %v\n", ltype, timestamp, l.prefix, filepath.Base(file), line, msg)
+
+ l.mutex.Lock()
+ defer l.mutex.Unlock()
+
+ // make a new logfile.
+ if l.maxline <= l.linenum {
+ if l.filepath != "" {
+ if l.out != nil {
+ l.out.Close()
+ l.out = nil
+ }
+ newname := fmt.Sprintf("%v.%v", l.filepath, time.Now().Format("20060102"))
+ if err := os.Rename(l.filepath, newname); err == nil {
+ l.linenum = 0
+ }
+ } else {
+ l.linenum = 0
+ }
+ }
+ if l.out == nil {
+ if l.filepath != "" {
+ l.out, _ = os.OpenFile(l.filepath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
+ } else {
+ l.out = os.Stdout
+ }
+ }
+ if l.out == nil {
+ log.Println("logger's output is nil")
+ }
+
+ // write to logfile.
+ buf := []byte(text)
+ if _, err := l.out.Write(buf); err == nil {
+ l.linenum++
+ }
+}
+
+func lineCounter(r io.Reader) (int64, error) {
+ count := int64(0)
+ sep := []byte{'\n'}
+ buf := make([]byte, 32*1024)
+ for {
+ c, err := r.Read(buf)
+ count += int64(bytes.Count(buf[:c], sep))
+ switch {
+ case err == io.EOF:
+ return count, nil
+ case err != nil:
+ return count, err
+ }
+ }
+}
--- /dev/null
+// Copyright 2023 nanzoom.com. All Rights Reserved.
+
+package uzsdk
+
+// Setting defines application's configuration.
+type Setting struct {
+ Service struct {
+ IP string // server listen address:ip, default: 0.0.0.0
+ Port int64 // server listen address:port, default: 9000
+ ServerName string // local address served by this server, default: http://[IP]:[Port], exp: http://127.0.0.1:9000
+ SystemName string // smartsystem address served by httpd, default: http://[IP], exp: http://192.168.0.6
+ PublicName string // smartcloud address served by httpd, default: [ServerName], exp: http://192.168.0.6:8080
+ ServicePath string // urlpath for this service, default: /wservice
+ DataDirectory string // server data directory, default: /data/cloud
+ TrustedProxies []string // a list of proxies trusted, default: ["127.0.0.1"]
+ }
+ System struct {
+ DBDriver string // database driver, default: postgres
+ DBHost string // database host, default: 127.0.0.1
+ DBPort int64 // database port, default: 5432
+ DBUser string // database user, default: postgres
+ DBPassword string // database password, default: postgres
+ DBName string // database name, default: serverdb
+ Heartbeat int64 // heartbeat(unit: second) for keepalive between client and server, default: 36000
+ IdleTime int64 // server kick off client those do nothing after this idle time, default: [Heartbeat]<<2
+ CheckTime int64 // interval that server check client status, default: [Heartbeat]>>1
+ DbgOutput bool // debug: output more details, default: fasle.
+ DbgToken bool // debug: use token '63886e35-7291-4b4d-a66f-fa7431542ace', default: false
+ }
+ GZip struct {
+ Enable bool // enable gzip with gin, default: true
+ Level int64 // gzip compress level, default: gzip.DefaultCompression
+ }
+ Logging struct {
+ Level string // TODO: log level, default: error
+ Prefix string // prefix add to each log message, default: ""
+ File struct {
+ Name string // log filename
+ }
+ Syslog struct {
+ Enable bool // TODO: enable log to system, default: false
+ }
+ }
+ Wechat struct {
+ // for wechat-app
+ Appid string
+ Appsecret string
+ // for wechat-pay
+ Mchid string
+ Apikey string
+ CertificateSerialno string
+ PrivateKeyPath string
+ NotifyUrl string
+ }
+ Task struct {
+ Timeout int64
+ }
+}
--- /dev/null
+// Copyright 2010-2022 nanzoom.com. All Rights Reserved.
+
+package translations
+
+func init() {
+ TrlTable["en_US"] = trnode{
+ // service
+ "ParseError": "An error occurred while parsing the request message.",
+ "InvalidRequest": "The message is not a valid Request object.",
+ "MethodNotFound": "The method does not exist or not available.",
+ "InvalidParams": "Invalid method parameter(s).",
+ "InternalError": "Internal server error.",
+ "START": "The start of user-defined server-errors.",
+ "VersionNotMatch": "version not match.",
+ "DBError": "Error occurred when connecting to database.",
+ "SQLError": "Sql execute returns error.",
+ "LoginFailed": "Login failed, wrong username or password.",
+ "UserOnline": "The user is already online and exceed login limits.",
+ "TokenError": "Token error, please try login again.",
+ "RightLimit": "You have no right to use the module.",
+ "AccessDeny": "You have no right to access the data.",
+ "InvalidValue": "Invalid value for method parameter.",
+ "NameRegistered": "Failed to register because the user is exist.",
+ "RecordExist": "Failed to add because this record is already exist.",
+ "PointShortage": "Do not have enough point to done this.",
+ "RuleNotMatch": "Operation ignored because not match rule.",
+ "InvalidSettings": "Server's setting needed for supporting this method not found or invalid.",
+ "FsErrInvalid": "Invalid argument on filesystem.",
+ "FsErrPermission": "Permission denied on filesystem.",
+ "FsErrExist": "File already exists on filesystem.",
+ "FsErrNotExist": "File does not exist on filesystem.",
+ "FsErrClosed": "File already closed on filesystem.",
+ "FsInitFailed": "Initialize filesystem failed.",
+ "FsCreateFailed": "Create filepath failed.",
+ "FsRemoveFailed": "Remove filepath failed.",
+ "FsWriteFailed": "Write filepath failed.",
+ "FsReadFailed": "Read filepath failed.",
+ "FsBackupFailed": "Backup filepath failed.",
+ "FsArchiveFailed": "Archive filepath failed.",
+ "FsNotRegular": "Is not a regular file.",
+ "FsNotDirectory": "Is not a directory.",
+ "FsShareExpired": "Share has expired.",
+ "FsSharePassword": "Share needs right password to open.",
+ "LoginMaxretry": "You are disabled to login because failed for too many times.",
+ "FsViewNotSupport": "File not support view.",
+ "FsConvertFailed": "Convert file format failed.",
+ "FsWatermarkFailed": "Add watermark failed.",
+ "FsMakeLinkFailed": "Make links between files failed.",
+ "END": "The end of user-defined server-errors.",
+ // htmls
+ "fileshares.password.title": "require password",
+ "fileshares.password.placeholder": "please input password",
+ "fileshares.password.submit": "Submit",
+ "fileshares.browser.title": "Folder",
+ "fileshares.browser.name": "Name",
+ "fileshares.browser.filesize": "Size",
+ "fileshares.browser.updated": "Updated",
+ "fileshares.browser.download": "Download",
+ "fileshares.browser.downloadall": "Download All",
+ "fileviewer.viewer.loading": "preview loading...",
+ "fileviewer.viewer.ok": "OK",
+ "fileviewer.viewer.cancel": "Cancel",
+ }
+}
--- /dev/null
+// Copyright 2010-2022 nanzoom.com. All Rights Reserved.
+
+package translations
+
+type trnode map[string]string
+
+var TrlTable = make(map[string]trnode)
+
+// Translate use key to find value that defined in TrlTable[language] and then return it,
+// returns "" if not found.
+func Translate(language *string, key string) string {
+ lang := "en_US"
+ if language != nil {
+ lang = *language
+ }
+ if list, ok := TrlTable[lang]; ok {
+ if value, ok := list[key]; ok {
+ return value
+ }
+ }
+ if list, ok := TrlTable["en_US"]; ok {
+ if value, ok := list[key]; ok {
+ return value
+ }
+ }
+ return ""
+}
--- /dev/null
+// Copyright 2010-2022 nanzoom.com. All Rights Reserved.
+
+package translations
+
+func init() {
+ TrlTable["zh_CN"] = trnode{
+ // service
+ "ParseError": "解析请求消息失败。",
+ "InvalidRequest": "该请求无效。",
+ "MethodNotFound": "该请求方法不存在或不可用。",
+ "InvalidParams": "该请求参数无效。",
+ "InternalError": "服务内部错误。",
+ "START": "用户自定义服务错误起始。",
+ "VersionNotMatch": "版本不匹配。",
+ "DBError": "数据库连接失败。",
+ "SQLError": "SQL语句执行失败。",
+ "LoginFailed": "登录失败:错误的用户名或密码。",
+ "UserOnline": "当前用户在线并超出登录限制。",
+ "TokenError": "令牌错误,请重新登录。",
+ "RightLimit": "没有权限访问该功能。",
+ "AccessDeny": "没有权限访问该数据。",
+ "InvalidValue": "该请求参数传值错误。",
+ "NameRegistered": "注册失败:该用户已存在。",
+ "RecordExist": "添加失败:该记录已存在。",
+ "PointShortage": "所需点数不够。",
+ "RuleNotMatch": "操作忽略:不符合规则。",
+ "InvalidSettings": "服务设定不支持该请求。",
+ "FsErrInvalid": "文件系统:无效参数。",
+ "FsErrPermission": "文件系统:权限拒绝。",
+ "FsErrExist": "文件系统:文件已存在。",
+ "FsErrNotExist": "文件系统:文件不存在。",
+ "FsErrClosed": "文件系统:文件已关闭。",
+ "FsInitFailed": "文件系统:初始化失败。",
+ "FsCreateFailed": "文件系统:创建失败。",
+ "FsRemoveFailed": "文件系统:删除失败。",
+ "FsWriteFailed": "文件系统:写入失败。",
+ "FsReadFailed": "文件系统:读取失败。",
+ "FsBackupFailed": "文件系统:备份失败。",
+ "FsArchiveFailed": "文件系统:归档失败。",
+ "FsNotRegular": "文件系统:非普通文件。",
+ "FsNotDirectory": "文件系统:非目录。",
+ "FsShareExpired": "文件系统:共享已过期。",
+ "FsSharePassword": "文件系统:该共享需要密码打开。",
+ "LoginMaxretry": "由于尝试次数过多您已被禁止登录",
+ "FsViewNotSupport": "文件系统:该文件不支持预览.",
+ "FsConvertFailed": "文件系统:转换格式失败。",
+ "FsWatermarkFailed": "文件系统:添加水印失败。",
+ "FsMakeLinkFailed": "文件系统:创建链接失败。",
+ "END": "用户自定义服务错误结束",
+ // htmls
+ "fileshares.password.title": "需要密码访问",
+ "fileshares.password.placeholder": "请输入密码",
+ "fileshares.password.submit": "提交",
+ "fileshares.browser.title": "文件夹",
+ "fileshares.browser.name": "名称",
+ "fileshares.browser.filesize": "大小",
+ "fileshares.browser.updated": "更新时间",
+ "fileshares.browser.download": "下载",
+ "fileshares.browser.downloadall": "下载所有",
+ "fileviewer.viewer.loading": "预览加载中...",
+ "fileviewer.viewer.ok": "确定",
+ "fileviewer.viewer.cancel": "取消",
+ }
+}
--- /dev/null
+// Copyright 2023 nanzoom.com. All Rights Reserved.
+
+package uzsdk
+
+import (
+ "git.uzoombox.com/git/uzsdk/logger"
+ "git.uzoombox.com/git/uzsdk/translations"
+ "github.com/gin-gonic/gin"
+ "github.com/jmoiron/sqlx"
+)
+
+// RespCode defines response code and messages, reference to https://www.jsonrpc.org/specification#error_object.
+type Respnode struct {
+ Code int64 `json:"code"`
+ Message string `json:"message"`
+}
+
+var RespCode = map[string]int64{
+ "ParseError": -32700,
+ "InvalidRequest": -32600,
+ "MethodNotFound": -32601,
+ "InvalidParams": -32602,
+ "InternalError": -32603,
+ "START": -32000,
+ "VersionNotMatch": -32001,
+ "DBError": -32002,
+ "SQLError": -32003,
+ "LoginFailed": -32004,
+ "UserOnline": -32005,
+ "TokenError": -32006,
+ "RightLimit": -32007,
+ "AccessDeny": -32008,
+ "InvalidValue": -32009,
+ "NameRegistered": -32010,
+ "RecordExist": -32011,
+ "PointShortage": -32012,
+ "RuleNotMatch": -32013,
+ "InvalidSettings": -32014,
+ "FsErrInvalid": -32015,
+ "FsErrPermission": -32016,
+ "FsErrExist": -32017,
+ "FsErrNotExist": -32018,
+ "FsErrClosed": -32019,
+ "FsInitFailed": -32020,
+ "FsCreateFailed": -32021,
+ "FsRemoveFailed": -32022,
+ "FsWriteFailed": -32023,
+ "FsReadFailed": -32024,
+ "FsBackupFailed": -32025,
+ "FsArchiveFailed": -32026,
+ "FsNotRegular": -32027,
+ "FsNotDirectory": -32028,
+ "FsShareExpired": -32029,
+ "FsSharePassword": -32030,
+ "LoginMaxretry": -32031,
+ "FsViewNotSupport": -32032,
+ "FsConvertFailed": -32033,
+ "FsWatermarkFailed": -32034,
+ "FsMakeLinkFailed": -32035,
+ "END": -32099,
+}
+
+// Error defines error object, reference to https://www.jsonrpc.org/specification#error_object.
+type Error struct {
+ Code int64 `json:"code"`
+ Message string `json:"message"`
+ Data interface{} `json:"data"`
+}
+
+// Request defines Request object, reference to https://www.jsonrpc.org/specification#request_object.
+type Request struct {
+ // Jsonrpc string `json:"jsonrpc"`
+ Method string `json:"method"`
+ Params interface{} `json:"params"`
+ ID interface{} `json:"id"` // string, number or null
+ Token string `json:"token"`
+}
+
+// ReqParamList defines Request.Param of method:list.
+type ReqParamList struct {
+ Fields []string `json:"fields"`
+ Wheres interface{} `json:"wheres"` // use gin.H, exp: gin.H{"isactive": "='Y'"}
+ Limits struct {
+ Offset int64 `json:"offset"`
+ Limit int64 `json:"limit"`
+ } `json:"limits"`
+ Groupby string `json:"groupby"`
+ Orderby string `json:"orderby"`
+}
+
+// Response defines response object, reference to https://www.jsonrpc.org/specification#response_object.
+type Response struct {
+ // Jsonrpc string `json:"jsonrpc"`
+ Result interface{} `json:"result"`
+ Error Error `json:"error"`
+ ID interface{} `json:"id"` // string, number or null
+}
+
+// ResResultExec defines Response.Result of method:[add,del,set].
+type ResResultExec struct {
+ Affected int64 `json:"affected"`
+ Returnids []interface{} `json:"returnids"`
+}
+
+// ResResultList defines Response.Result of method:list.
+type ResResultList struct {
+ Count int64 `json:"count"`
+ Rows []interface{} `json:"rows"`
+}
+
+// Respcode returns a structure of Respnode that message has been translated using language if options.language is set,
+// else directly returns Respcode(code, ct.AdLanguage).
+// options will be [language]:
+// language *string: name of language.
+func Respcode(code string, options ...interface{}) Respnode {
+ var language *string = nil
+ for _, option := range options {
+ switch option.(type) {
+ case *string:
+ language = option.(*string)
+ }
+ }
+
+ return Respnode{
+ Code: RespCode[code],
+ Message: translations.Translate(language, code),
+ }
+}
+
+// Instance is the framework's instance, used for server & plugin both.
+type Instance struct {
+ Setting *Setting // settings of this application.
+ Db *sqlx.DB // the first database connection created by main routine.
+ Logger *logger.Logger // logger created by main routine.
+ Router *gin.Engine // default webservice engine created by main routine.
+ Maintenance bool // when maintenance mode is true, only serve api /config.
+}
--- /dev/null
+// Copyright 2023 nanzoom.com. All Rights Reserved.
+
+package uzsdk
+
+// Version is the current opensdk's version.
+const Version = "1.0.0"