Create & Init Project...

This commit is contained in:
2019-04-22 18:49:16 +08:00
commit fc4fa37393
25440 changed files with 4054998 additions and 0 deletions

View File

@@ -0,0 +1,26 @@
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//app/admin/main/macross/cmd:all-srcs",
"//app/admin/main/macross/conf:all-srcs",
"//app/admin/main/macross/dao:all-srcs",
"//app/admin/main/macross/http:all-srcs",
"//app/admin/main/macross/model/errors:all-srcs",
"//app/admin/main/macross/model/mail:all-srcs",
"//app/admin/main/macross/model/manager:all-srcs",
"//app/admin/main/macross/model/package:all-srcs",
"//app/admin/main/macross/model/publish:all-srcs",
"//app/admin/main/macross/service:all-srcs",
"//app/admin/main/macross/tools:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,88 @@
#### Macross 各种管理~
#### BGM
> http://girigiri.love/
### Version 3.0.9 - 2018.12.11
#### Features
> 1.修复 tools/unzip.go 的 bug使用 “zip -r xxxx.zip xxxx/*”命令时,产物中不包含文件夹信息导致的解压失败
### Version 3.0.8 - 2018.12.4
#### Features
> 1.如果 apk 的渠道包生成失败,说明包有问题,删除整个 apk 包
### Version 3.0.7 - 2018.11.29
##### Features
> 1.unzip 下沉到 tools 中
> 2.取包服务部分业务逻辑下沉到 service 中
> 3.邮件新增附件功能
### Version 3.0.6 - 2018.11.15
##### Features
> 1.取包服务改为 GET
### Version 3.0.5 - 2018.11.14
##### Features
> 1.邮件、包上传、取包服务出错返回具体错误原因
### Version 3.0.4 - 2018.11.12
##### Features
> 1.macross 新增包上传 & apk 重签服务
> 2.macross 新增取包服务
### Version 3.0.3 - 2018.10.30
##### Features
> 1.macross 邮件发送服务新增抄送和密送功能,部分字段名称标准化
### Version 3.0.2 - 2018.10.29
##### Features
> 1.新增 macross 邮件发送服务
### Version 3.0.1 - 2018.09.17
##### Features
> 1.publish接口去掉res_size为0的校验
### Version 3.0.0 - 2018.08.20
##### Features
> 1.初始化项目,全新逻辑
### Version 2.2.0 - 2018.01.23
##### Features
> 1.config使用新SDK
> 2.增加定期load model的逻辑
### Version 2.3.0 - 2018.02.26
##### Features
> 1.创建、修改依赖图添加extra_info字段
> 2.依赖图查询接口返回值添加extra_info字段
> 3.暂时注释修改弱依赖图的model冲突判断
> 4.上传framework时校验模糊依赖关系的符号
> 5.重新修改模糊依赖model的逻辑
> 6.修改模糊依赖model的返回值去掉图中已存在的model
> 7.增加查询和修改framwork依赖信息的接口
### Version 2.2.0 - 2018.01.23
##### Features
> 1.依赖图相关功能
### Version 2.1.2 - 2017.12.08
##### Features
> 1.精简逻辑,优化逻辑
### Version 2.1.1 - 2017.12.05
##### Features & BUG_Fix
> 1.修复ios的fremawork上传接口参数model与当前文件不匹配的问题
### Version 2.1.0 - 2017.12.05
##### Features & BUG_Fix
> 1.ios的fremawork上传接口添加用户和model权限校验
> 2.ios的fremawork上传接口添加code和model唯一性校验
### Version 2.0.0 - 2017.11.29
##### Features
> 1.ios的fremawork上传和列表查看接口
> 2.ios的version管理接口
> 3.ios的model、角色管理、权限查询接口
##### Version 1.0.0
> 1.添加权限管理模块接口

View File

@@ -0,0 +1,17 @@
# Owner
liweijia
peiyifei
yujia
zhangxinzheng
# Author
yujia
laiying
zhangxinzheng
huangyimin
# Reviewer
liweijia
peiyifei
yujia
zhangxinzheng

View File

@@ -0,0 +1,22 @@
# See the OWNERS docs at https://go.k8s.io/owners
approvers:
- huangyimin
- laiying
- liweijia
- peiyifei
- yujia
- zhangxinzheng
labels:
- admin
- admin/main/macross
- main
options:
no_parent_owners: true
reviewers:
- huangyimin
- laiying
- liweijia
- peiyifei
- yujia
- zhangxinzheng

View File

@@ -0,0 +1,10 @@
#### macross
##### 项目简介
> macross
##### 编译环境
> 请只用golang v1.9.x以上版本编译执行
##### 依赖包
> 1.公共包go-common

View File

@@ -0,0 +1,42 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_binary",
"go_library",
)
go_binary(
name = "cmd",
embed = [":go_default_library"],
tags = ["automanaged"],
)
go_library(
name = "go_default_library",
srcs = ["main.go"],
data = ["macross-admin-test.toml"],
importpath = "go-common/app/admin/main/macross/cmd",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/admin/main/macross/conf:go_default_library",
"//app/admin/main/macross/http:go_default_library",
"//library/log:go_default_library",
"//library/net/trace:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,108 @@
reload = "1m"
[log]
dir = "/data/log/macross/"
[bm]
[bm.inner]
addr = "0.0.0.0:5001"
timeout = "1s"
[bm.local]
addr = "0.0.0.0:5003"
timeout = "1s"
[oss]
accessKeyID = "LTAItAbov41J7Wea"
accessKeySecret = "yaPQXRPLicxe8xLOgPsCffYEIO843a"
endpoint = "http://oss-cn-shanghai.aliyuncs.com"
bucket = "dl-hdslb-com"
originDir = "mobile/test"
publishDir = "mobile/latest"
[db]
[db.macross]
name = "127.0.0.1:3306"
dsn = "root:test@tcp(127.0.0.1:3306)/mtc_macross?timeout=5s&readTimeout=5s&writeTimeout=5s&parseTime=true&loc=Local&charset=utf8"
active = 5
idle = 2
idleTimeout ="4h"
queryTimeout = "1s"
execTimeout = "1s"
tranTimeout = "1s"
[db.manager.breaker]
window = "3s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[httpClient]
key = "f022126a8a365e20"
secret = "b7b86838145d634b487e67b811b8fab2"
dial = "500ms"
timeout = "2s"
keepAlive = "60s"
timer = 1000
[httpClient.breaker]
window ="10s"
sleep ="10ms"
bucket = 10
ratio = 0.1
request = 10
[property]
[property.mail]
host = "smtp.exmail.qq.com"
port = 465
address = "no-reply@bilibili.com"
pwd = ""
name = "App Builder"
[property.package]
URLPrefix = "http://test"
savePath = "/Users/yourname"
[auth]
managerHost = "http://macross.bilibili.co"
dashboardHost = "http://dashboard-mng.bilibili.co"
dashboardCaller = "android-apk"
[auth.DsHTTPClient]
key = "manager-go"
secret = "2pnik62rr8xgrrleag2vx2l0svvnrfld"
dial = "1s"
timeout = "1s"
keepAlive = "60s"
[auth.DsHTTPClient.breaker]
window = "3s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[auth.MaHTTPClient]
key = "f022126a8a365e20"
secret = "b7b86838145d634b487e67b811b8fab2"
dial = "1s"
timeout = "1s"
keepAlive = "60s"
[auth.MaHTTPClient.breaker]
window = "3s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[auth.session]
sessionIDLength = 32
cookieLifeTime = 1800
cookieName = "mng-go"
domain = ".bilibili.co"
[auth.session.Memcache]
name = "go-business/auth"
proto = "tcp"
addr = "172.16.33.54:11211"
active = 10
idle = 10
dialTimeout = "1s"
readTimeout = "1s"
writeTimeout = "1s"
idleTimeout = "80s"

View File

@@ -0,0 +1,45 @@
package main
import (
"flag"
"os"
"os/signal"
"syscall"
"go-common/app/admin/main/macross/conf"
"go-common/app/admin/main/macross/http"
"go-common/library/log"
"go-common/library/net/trace"
)
func main() {
flag.Parse()
if err := conf.Init(); err != nil {
log.Error("conf.Init() error(%v)", err)
panic(err)
}
// init log
log.Init(conf.Conf.Log)
trace.Init(conf.Conf.Tracer)
defer trace.Close()
defer log.Close()
log.Info("macross start")
// service init
http.Init(conf.Conf)
// init signal
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT)
for {
s := <-c
log.Info("macross get a signal %s", s.String())
switch s {
case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT:
log.Info("macross exit")
return
case syscall.SIGHUP:
// TODO reload
default:
return
}
}
}

View File

@@ -0,0 +1,38 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["conf.go"],
importpath = "go-common/app/admin/main/macross/conf",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//library/conf:go_default_library",
"//library/database/sql:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/net/http/blademaster/middleware/permit:go_default_library",
"//library/net/trace:go_default_library",
"//library/time:go_default_library",
"//vendor/github.com/BurntSushi/toml:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,126 @@
package conf
import (
"errors"
"flag"
"go-common/library/conf"
"go-common/library/database/sql"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/middleware/permit"
"go-common/library/net/trace"
"go-common/library/time"
"github.com/BurntSushi/toml"
)
// config init.
var (
confPath string
Conf *Config
)
// Config struct.
type Config struct {
Version string `toml:"version"`
// xlog
Log *log.Config
// HTTPServer
BM *BM
// tracer
Tracer *trace.Config
// oss
Oss *Oss
// db
DB *DB
// reload time
Reload time.Duration
GitRelation map[string]string
// identify
Auth *permit.Config
// other
InvalidFrameworkFile []string
TextSizeLimitList []TextSizeLimit
Property *Property
}
// BM http.
type BM struct {
Inner *bm.ServerConfig
Local *bm.ServerConfig
}
// TextSizeLimit struct.
type TextSizeLimit struct {
Size int64
Limit float64
}
// Oss struct.
type Oss struct {
Endpoint string
AccessKeyID string
AccessKeySecret string
Bucket string
OriginDir string
PublishDir string
}
// DB struct.
type DB struct {
Macross *sql.Config
}
// Property struct.
type Property struct {
Mail *struct {
Host string
Port int
Address string
Pwd string
Name string
}
Package *struct {
URLPrefix string
SavePath string
}
}
func init() {
flag.StringVar(&confPath, "conf", "", "default config path")
}
// Init init config
func Init() (err error) {
if confPath != "" {
_, err = toml.DecodeFile(confPath, &Conf)
return
}
err = configCenter()
return
}
// configCenter ugc
func configCenter() (err error) {
var (
client *conf.Client
c string
ok bool
)
if client, err = conf.New(); err != nil {
panic(err)
}
if c, ok = client.Toml2(); !ok {
err = errors.New("load config center error")
return
}
_, err = toml.Decode(c, &Conf)
go func() {
for e := range client.Event() {
log.Error("get config from config center error(%v)", e)
}
}()
return
}

View File

@@ -0,0 +1,64 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"dao.go",
"mng_auth.go",
"mng_role.go",
"mng_user.go",
"publish.go",
],
importpath = "go-common/app/admin/main/macross/dao",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/admin/main/macross/conf:go_default_library",
"//app/admin/main/macross/model/manager:go_default_library",
"//app/admin/main/macross/model/publish:go_default_library",
"//library/database/sql:go_default_library",
"//library/log:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//app/admin/main/macross/dao/oss:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
go_test(
name = "go_default_test",
srcs = [
"dao_test.go",
"mng_auth_test.go",
"mng_role_test.go",
"mng_user_test.go",
],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/admin/main/macross/conf:go_default_library",
"//library/database/sql:go_default_library",
"//vendor/github.com/bouk/monkey:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)

View File

@@ -0,0 +1,41 @@
package dao
import (
"context"
"go-common/app/admin/main/macross/conf"
"go-common/library/database/sql"
"go-common/library/log"
)
// Dao macross dao.
type Dao struct {
// conf
c *conf.Config
// db
db *sql.DB
}
// New dao.
func New(c *conf.Config) (d *Dao) {
d = &Dao{
c: c,
db: sql.NewMySQL(c.DB.Macross),
}
return
}
// Ping dao.
func (d *Dao) Ping(c context.Context) (err error) {
if err = d.db.Ping(c); err != nil {
log.Error("d.db error(%v)", err)
}
return
}
// Close close kafka connection.
func (d *Dao) Close() {
if d.db != nil {
d.db.Close()
}
}

View File

@@ -0,0 +1,58 @@
package dao
import (
"context"
"flag"
"os"
"testing"
"go-common/app/admin/main/macross/conf"
"go-common/library/database/sql"
"github.com/smartystreets/goconvey/convey"
)
var (
d *Dao
)
func TestMain(m *testing.M) {
if os.Getenv("DEPLOY_ENV") != "" {
flag.Set("app_id", "main.app-svr.macross")
flag.Set("conf_token", "5cc0121bad1156f7eb0335fbb44ce0ff")
flag.Set("tree_id", "6798")
flag.Set("conf_version", "docker-1")
flag.Set("deploy_env", "uat")
flag.Set("conf_host", "config.bilibili.co")
flag.Set("conf_path", "/tmp")
flag.Set("region", "sh")
flag.Set("zone", "sh001")
} else {
flag.Set("conf", "../cmd/macross-admin-test.toml")
}
flag.Parse()
if err := conf.Init(); err != nil {
panic(err)
}
d = New(conf.Conf)
m.Run()
os.Exit(0)
}
func WithReopenDB(f func(d *Dao)) func() {
return func() {
convey.Reset(func() {
d.db = sql.NewMySQL(d.c.DB.Macross)
})
f(d)
}
}
func TestDaoPing(t *testing.T) {
convey.Convey("Ping", t, func(ctx convey.C) {
err := d.Ping(context.TODO())
ctx.Convey("Err should be nil", func() {
ctx.So(err, convey.ShouldBeNil)
})
})
}

View File

@@ -0,0 +1,138 @@
package dao
import (
"context"
model "go-common/app/admin/main/macross/model/manager"
"go-common/library/log"
)
const (
// load cache(get all).
_authRelationSQL = `SELECT rid,auth_id FROM auth_relation`
_authsSQL = `SELECT id,system,name,flag,ctime,mtime FROM auth`
// auth.
_inAuthSQL = `INSERT INTO auth (system,name,flag) VALUES(?,?,?)`
_upAuthSQL = `UPDATE auth SET name=? WHERE id=?`
_delAuthSQL = `DELETE FROM auth WHERE id=?`
// auth_relation.
_inAuthRelationSQL = `INSERT INTO auth_relation (rid,auth_id) VALUES(?,?)`
_delAuthRelationSQL = `DELETE FROM auth_relation WHERE rid=? AND auth_id=?`
_cleanRelationByAuth = "DELETE FROM auth_relation WHERE auth_id=?"
)
// Auths select all auth from db.
func (d *Dao) Auths(c context.Context) (res map[string]map[int64]*model.Auth, err error) {
rows, err := d.db.Query(c, _authsSQL)
if err != nil {
log.Error("Auths d.db.Query(%d) error(%v)", err)
return
}
defer rows.Close()
res = make(map[string]map[int64]*model.Auth)
for rows.Next() {
var (
auths map[int64]*model.Auth
ok bool
)
auth := &model.Auth{}
if err = rows.Scan(&auth.AuthID, &auth.System, &auth.AuthName, &auth.AuthFlag, &auth.CTime, &auth.MTime); err != nil {
log.Error("Auths rows.Scan error(%v)", err)
return
}
if auths, ok = res[auth.System]; !ok {
auths = make(map[int64]*model.Auth)
res[auth.System] = auths
}
auths[auth.AuthID] = auth
}
return
}
// AddAuth insert auth.
func (d *Dao) AddAuth(c context.Context, system, authName, authFlag string) (rows int64, err error) {
res, err := d.db.Exec(c, _inAuthSQL, system, authName, authFlag)
if err != nil {
log.Error("AddAuth d.db.Exec() error(%v)", err)
return
}
rows, err = res.RowsAffected()
return
}
// UpAuth update auth.
func (d *Dao) UpAuth(c context.Context, authName string, authID int64) (rows int64, err error) {
res, err := d.db.Exec(c, _upAuthSQL, authName, authID)
if err != nil {
log.Error("UpAuth d.db.Exec() error(%v)", err)
return
}
rows, err = res.RowsAffected()
return
}
// DelAuth del auth.
func (d *Dao) DelAuth(c context.Context, authID int64) (rows int64, err error) {
res, err := d.db.Exec(c, _delAuthSQL, authID)
if err != nil {
log.Error("DelAuth d.db.Exec() error(%v)", err)
return
}
rows, err = res.RowsAffected()
return
}
// CleanAuthRelationByAuth del all auth relation by auth.
func (d *Dao) CleanAuthRelationByAuth(c context.Context, authID int64) (rows int64, err error) {
res, err := d.db.Exec(c, _cleanRelationByAuth, authID)
if err != nil {
log.Error("CleanAuthRelationByAuth d.db.Exec() error(%v)", err)
return
}
rows, err = res.RowsAffected()
return
}
// AuthRelation select all auth_relation from db.
func (d *Dao) AuthRelation(c context.Context) (res map[int64][]int64, err error) {
rows, err := d.db.Query(c, _authRelationSQL)
if err != nil {
log.Error("Roles d.db.Query(%d) error(%v)", err)
return
}
defer rows.Close()
res = make(map[int64][]int64)
for rows.Next() {
var (
roleID, authID int64
)
if err = rows.Scan(&roleID, &authID); err != nil {
log.Error("Roles rows.Scan error(%v)", err)
return
}
res[roleID] = append(res[roleID], authID)
}
return
}
// AddAuthRelation insert auth_relation.
func (d *Dao) AddAuthRelation(c context.Context, roleID, authID int64) (rows int64, err error) {
res, err := d.db.Exec(c, _inAuthRelationSQL, roleID, authID)
if err != nil {
log.Error("AddAuthRelation d.db.Exec() error(%v)", err)
return
}
rows, err = res.RowsAffected()
return
}
// DelAuthRelation del auth_relation.
func (d *Dao) DelAuthRelation(c context.Context, roleID, authID int64) (rows int64, err error) {
res, err := d.db.Exec(c, _delAuthRelationSQL, roleID, authID)
if err != nil {
log.Error("DelAuthRelation d.db.Exec() error(%v)", err)
return
}
rows, err = res.RowsAffected()
return
}

View File

@@ -0,0 +1,55 @@
package dao
import (
"context"
"fmt"
"reflect"
"testing"
xsql "go-common/library/database/sql"
"github.com/bouk/monkey"
"github.com/smartystreets/goconvey/convey"
)
func TestAuths(t *testing.T) {
convey.Convey("Auths", t, func(ctx convey.C) {
ctx.Convey("When everything is correct", func(ctx convey.C) {
res, err := d.Auths(context.Background())
ctx.Convey("Error should be nil, res should not be empty", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeEmpty)
})
})
ctx.Convey("When db.Query gets error", func(ctx convey.C) {
guard := monkey.PatchInstanceMethod(reflect.TypeOf(d.db), "Query", func(_ *xsql.DB, _ context.Context, _ string, _ ...interface{}) (*xsql.Rows, error) {
return nil, fmt.Errorf("db.Query error")
})
defer guard.Unpatch()
_, err := d.Auths(context.Background())
ctx.Convey("Error should not be nil", func(ctx convey.C) {
ctx.So(err, convey.ShouldNotBeNil)
})
})
})
}
// Set d.close() to get reversal case
func TestAuthRelation(t *testing.T) {
convey.Convey("AuthRelation", t, func(ctx convey.C) {
convey.Convey("When everything is correct,", func(ctx convey.C) {
asgs, err := d.AuthRelation(context.Background())
ctx.Convey("Error should be nil, asgs should not be nil(No Data)", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(asgs, convey.ShouldBeNil)
})
})
convey.Convey("When set db closed", WithReopenDB(func(d *Dao) {
d.Close()
_, err := d.AuthRelation(context.Background())
convey.Convey("Error should not be nil", func(ctx convey.C) {
convey.So(err, convey.ShouldNotBeNil)
})
}))
})
}

View File

@@ -0,0 +1,90 @@
package dao
import (
"context"
model "go-common/app/admin/main/macross/model/manager"
"go-common/library/log"
)
const (
// load cache(get all).
_rolesSQL = `SELECT id,system,name,ctime,mtime FROM role`
// role.
_inRoleSQL = `INSERT INTO role (system,name) VALUES(?,?)`
_upRoleSQL = `UPDATE role SET name=? WHERE id=?`
_delRoleSQL = `DELETE FROM role WHERE id=?`
_cleanRelationByRole = "DELETE FROM auth_relation WHERE rid=?"
)
// Roles select all role from db.
func (d *Dao) Roles(c context.Context) (res map[string]map[int64]*model.Role, err error) {
rows, err := d.db.Query(c, _rolesSQL)
if err != nil {
log.Error("Roles d.db.Query(%d) error(%v)", err)
return
}
defer rows.Close()
res = make(map[string]map[int64]*model.Role)
for rows.Next() {
var (
roles map[int64]*model.Role
ok bool
)
role := &model.Role{}
if err = rows.Scan(&role.RoleID, &role.System, &role.RoleName, &role.CTime, &role.MTime); err != nil {
log.Error("Roles rows.Scan error(%v)", err)
return
}
if roles, ok = res[role.System]; !ok {
roles = make(map[int64]*model.Role)
res[role.System] = roles
}
roles[role.RoleID] = role
}
return
}
// AddRole insert role.
func (d *Dao) AddRole(c context.Context, system, roleName string) (rows int64, err error) {
res, err := d.db.Exec(c, _inRoleSQL, system, roleName)
if err != nil {
log.Error("AddRole d.db.Exec() error(%v)", err)
return
}
rows, err = res.RowsAffected()
return
}
// UpRole update role.
func (d *Dao) UpRole(c context.Context, roleName string, roleID int64) (rows int64, err error) {
res, err := d.db.Exec(c, _upRoleSQL, roleName, roleID)
if err != nil {
log.Error("UpRole d.db.Exec() error(%v)", err)
return
}
rows, err = res.RowsAffected()
return
}
// DelRole del role.
func (d *Dao) DelRole(c context.Context, roleID int64) (rows int64, err error) {
res, err := d.db.Exec(c, _delRoleSQL, roleID)
if err != nil {
log.Error("DelRole d.db.Exec() error(%v)", err)
return
}
rows, err = res.RowsAffected()
return
}
// CleanAuthRelationByRole del all auth relation by role.
func (d *Dao) CleanAuthRelationByRole(c context.Context, roleID int64) (rows int64, err error) {
res, err := d.db.Exec(c, _cleanRelationByRole, roleID)
if err != nil {
log.Error("CleanAuthRelationByRole d.db.Exec() error(%v)", err)
return
}
rows, err = res.RowsAffected()
return
}

View File

@@ -0,0 +1,35 @@
package dao
import (
"context"
"fmt"
"reflect"
"testing"
xsql "go-common/library/database/sql"
"github.com/bouk/monkey"
"github.com/smartystreets/goconvey/convey"
)
func TestRoles(t *testing.T) {
convey.Convey("Roles", t, func(ctx convey.C) {
ctx.Convey("When everything is correct", func(ctx convey.C) {
res, err := d.Roles(context.Background())
ctx.Convey("Error should be nil, res should not be empty", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeEmpty)
})
})
ctx.Convey("When db.Query gets error", func(ctx convey.C) {
guard := monkey.PatchInstanceMethod(reflect.TypeOf(d.db), "Query", func(_ *xsql.DB, _ context.Context, _ string, _ ...interface{}) (*xsql.Rows, error) {
return nil, fmt.Errorf("db.Query error")
})
defer guard.Unpatch()
_, err := d.Roles(context.Background())
ctx.Convey("Error should not be nil", func(ctx convey.C) {
ctx.So(err, convey.ShouldNotBeNil)
})
})
})
}

View File

@@ -0,0 +1,94 @@
package dao
import (
"context"
model "go-common/app/admin/main/macross/model/manager"
"go-common/library/database/sql"
"go-common/library/log"
)
const (
// load cache(get all).
_usersSQL = `SELECT user.id,user.system,user.name,user.rid,role.name,user.ctime,user.mtime FROM user,role WHERE user.rid=role.id`
_userSQL = `SELECT user.id,user.system,user.name,user.rid,role.name,user.ctime,user.mtime FROM user,role WHERE user.rid=role.id AND user.id=?`
// user.
_inUserSQL = `INSERT INTO user (system,name,rid) VALUES(?,?,?)`
_upUserSQL = `UPDATE user SET name=?,rid=? WHERE id=?`
_delUserSQL = `DELETE FROM user WHERE id=?`
)
// Users select all user from db.
func (d *Dao) Users(c context.Context) (res map[string]map[string]*model.User, err error) {
rows, err := d.db.Query(c, _usersSQL)
if err != nil {
log.Error("UserAll d.db.Query(%d) error(%v)", err)
return
}
defer rows.Close()
res = make(map[string]map[string]*model.User)
for rows.Next() {
var (
users map[string]*model.User
ok bool
)
user := &model.User{}
if err = rows.Scan(&user.UserID, &user.System, &user.UserName, &user.RoleID, &user.RoleName, &user.CTime, &user.MTime); err != nil {
log.Error("Users rows.Scan error(%v)", err)
return
}
if users, ok = res[user.System]; !ok {
users = make(map[string]*model.User)
res[user.System] = users
}
users[user.UserName] = user
}
return
}
// User get user.
func (d *Dao) User(c context.Context, userID int64) (re *model.User, err error) {
row := d.db.QueryRow(c, _userSQL, userID)
re = &model.User{}
if err = row.Scan(&re.UserID, &re.System, &re.UserName, &re.RoleID, &re.RoleName, &re.CTime, &re.MTime); err != nil {
if err == sql.ErrNoRows {
err = nil
} else {
log.Error("User d.db.QueryRow(%d) error(%v)", userID, err)
}
}
return
}
// AddUser insert user.
func (d *Dao) AddUser(c context.Context, roleID int64, system, userName string) (rows int64, err error) {
res, err := d.db.Exec(c, _inUserSQL, system, userName, roleID)
if err != nil {
log.Error("AddUser d.db.Exec() error(%v)", err)
return
}
rows, err = res.RowsAffected()
return
}
// UpUser update user.
func (d *Dao) UpUser(c context.Context, userID, roleID int64, userName string) (rows int64, err error) {
res, err := d.db.Exec(c, _upUserSQL, userName, roleID, userID)
if err != nil {
log.Error("UpUser d.db.Exec() error(%v)", err)
return
}
rows, err = res.RowsAffected()
return
}
// DelUser del user.
func (d *Dao) DelUser(c context.Context, userID int64) (rows int64, err error) {
res, err := d.db.Exec(c, _delUserSQL, userID)
if err != nil {
log.Error("DelUser d.db.Exec() error(%v)", err)
return
}
rows, err = res.RowsAffected()
return
}

View File

@@ -0,0 +1,56 @@
package dao
import (
"context"
"fmt"
"reflect"
"testing"
xsql "go-common/library/database/sql"
"github.com/bouk/monkey"
"github.com/smartystreets/goconvey/convey"
)
func TestUsers(t *testing.T) {
convey.Convey("Users", t, func(ctx convey.C) {
ctx.Convey("When everything is correct", func(ctx convey.C) {
res, err := d.Users(context.Background())
ctx.Convey("Error should be nil, res should not be empty", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeEmpty)
})
})
ctx.Convey("When db.Query gets error", func(ctx convey.C) {
guard := monkey.PatchInstanceMethod(reflect.TypeOf(d.db), "Query", func(_ *xsql.DB, _ context.Context, _ string, _ ...interface{}) (*xsql.Rows, error) {
return nil, fmt.Errorf("db.Query error")
})
defer guard.Unpatch()
_, err := d.Users(context.Background())
ctx.Convey("Error should not be nil", func(ctx convey.C) {
ctx.So(err, convey.ShouldNotBeNil)
})
})
})
}
// Set d.close() to get reversal case
func TestUser(t *testing.T) {
var id = int64(67)
convey.Convey("User", t, func(ctx convey.C) {
convey.Convey("When everything is correct,", func(ctx convey.C) {
asgs, err := d.User(context.Background(), id)
ctx.Convey("Error should be nil, asgs should not be nil(No Data)", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(asgs, convey.ShouldBeNil)
})
})
convey.Convey("When set db closed", WithReopenDB(func(d *Dao) {
d.Close()
_, err := d.User(context.Background(), id)
convey.Convey("Error should not be nil", func(ctx convey.C) {
convey.So(err, convey.ShouldNotBeNil)
})
}))
})
}

View File

@@ -0,0 +1,33 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["dao.go"],
importpath = "go-common/app/admin/main/macross/dao/oss",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/admin/main/macross/conf:go_default_library",
"//library/log:go_default_library",
"//vendor/github.com/aliyun/aliyun-oss-go-sdk/oss:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,61 @@
package oss
import (
"context"
"io"
"path"
"go-common/app/admin/main/macross/conf"
"go-common/library/log"
"github.com/aliyun/aliyun-oss-go-sdk/oss"
)
// Dao macross dao
type Dao struct {
// conf
c *conf.Config
client *oss.Client
}
// New dao.
func New(c *conf.Config) (d *Dao) {
d = &Dao{
c: c,
}
client, err := oss.New(d.c.Oss.Endpoint, d.c.Oss.AccessKeyID, d.c.Oss.AccessKeySecret)
if err != nil {
panic(err)
}
d.client = client
return
}
// Put put object into oss.
func (d *Dao) Put(c context.Context, rd io.Reader, apkName string) (uri string, err error) {
bucket, _ := d.client.Bucket(d.c.Oss.Bucket)
uri = path.Join(d.c.Oss.OriginDir, apkName)
// put
if err = bucket.PutObject(uri, rd); err != nil {
log.Error("bucket.PutObject(%s) error(%v)", uri, err)
return
}
uri = path.Join(d.c.Oss.Bucket, uri) // NOTE: begin with '/' when upload success
return
}
// Publish publish object into oss.
func (d *Dao) Publish(objectKey string, destKey string) (uri string, err error) {
bucket, _ := d.client.Bucket(d.c.Oss.Bucket)
_, err = bucket.CopyObject(d.c.Oss.PublishDir+objectKey, d.c.Oss.PublishDir+destKey)
if err != nil {
log.Error("bucket.CopyObject(%s, %s) error(%v)", d.c.Oss.PublishDir+objectKey, d.c.Oss.PublishDir+destKey, err)
return
}
uri = path.Join("/", d.c.Oss.PublishDir+destKey)
return
}
// Close close kafka connection.
func (d *Dao) Close() {
}

View File

@@ -0,0 +1,53 @@
package dao
import (
"bytes"
"context"
"fmt"
"go-common/app/admin/main/macross/model/publish"
"go-common/library/log"
)
const (
_logSharding = 10
// dashborad
_inDashboradSQL = `INSERT INTO dashboard (name,label,commit_info,out_url,coverage_url,text_size_arm64,res_size,extra) VALUES(?,?,?,?,?,?,?,?)`
_inDashboradLogsSQL = `INSERT INTO dashboard_log_%02d (dashboard_id,level,msg) VALUES`
)
func (d *Dao) hitLogs(id int64) int64 {
return id % _logSharding
}
// Dashborad insert dashboard.
func (d *Dao) Dashborad(c context.Context, dashboard *publish.Dashboard) (rows int64, err error) {
res, err := d.db.Exec(c, _inDashboradSQL, dashboard.Name, dashboard.Label, dashboard.Commit, dashboard.OutURL, dashboard.CoverageURL, dashboard.TextSizeArm64, dashboard.ResSize, dashboard.Extra)
if err != nil {
log.Error("Dashborad() d.db.Exec() error(%v)", err)
return
}
rows, err = res.LastInsertId()
return
}
// DashboradLogs insert dashboard log.
func (d *Dao) DashboradLogs(c context.Context, id int64, logs []*publish.Log) (rows int64, err error) {
var (
buffer bytes.Buffer
insertTp string
)
insertTp = "(%d,'%s','%s'),"
buffer.WriteString(fmt.Sprintf(_inDashboradLogsSQL, d.hitLogs(id)))
for _, v := range logs {
buffer.WriteString(fmt.Sprintf(insertTp, id, v.Level, v.Msg))
}
buffer.Truncate(buffer.Len() - 1)
res, err := d.db.Exec(c, buffer.String())
if err != nil {
log.Error("DashboradLogs d.db.Exec() error(%v)", err)
return
}
rows, err = res.RowsAffected()
return
}

View File

@@ -0,0 +1,50 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"http.go",
"local.go",
"mail.go",
"manager.go",
"mng_auth.go",
"mng_role.go",
"mng_user.go",
"package.go",
"publish.go",
],
importpath = "go-common/app/admin/main/macross/http",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/admin/main/macross/conf:go_default_library",
"//app/admin/main/macross/model/mail:go_default_library",
"//app/admin/main/macross/model/package:go_default_library",
"//app/admin/main/macross/model/publish:go_default_library",
"//app/admin/main/macross/service:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/net/http/blademaster/middleware/permit:go_default_library",
"//library/net/http/blademaster/middleware/verify:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,77 @@
package http
import (
"go-common/app/admin/main/macross/conf"
"go-common/app/admin/main/macross/service"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/middleware/permit"
"go-common/library/net/http/blademaster/middleware/verify"
)
var (
verifySvc *verify.Verify
authSvc *permit.Permit
svr *service.Service
)
// Init int http service
func Init(c *conf.Config) {
verifySvc = verify.New(nil)
authSvc = permit.New(c.Auth)
svr = service.New(conf.Conf)
// init internal router
engineInner := bm.DefaultServer(c.BM.Inner)
innerRouter(engineInner)
// init internal server
if err := engineInner.Start(); err != nil {
log.Error("engineInner.Start() error(%v) | config(%v)", err, c)
panic(err)
}
// init external router
engineLocal := bm.DefaultServer(c.BM.Local)
localRouter(engineLocal)
// init external server
if err := engineLocal.Start(); err != nil {
log.Error("engineLocal.Start() error(%v) | config(%v)", err, c)
panic(err)
}
}
// innerRouter init outer router api path.
func innerRouter(e *bm.Engine) {
e.Ping(ping)
e.Register(register)
rs := e.Group("/api/v2/macross")
// MANAGER
mng := rs.Group("/manager")
// auth init.
mng.GET("/getAuths", authSvc.Verify(), getAuths)
// user.
mng.GET("/user", user)
mng.POST("/user/save", saveUser)
mng.POST("/user/del", delUser)
// role.
mng.GET("/role", role)
mng.POST("/role/save", saveRole)
mng.POST("/role/del", DelRole)
// auth.
mng.GET("/auth", auth)
mng.POST("/auth/save", saveAuth)
mng.POST("/auth/del", delAuth)
// relation
mng.POST("/setRelation", authRelation)
// dashboard
rs.POST("/dashboard", dashboard)
// sendmail
rs.POST("/sendmail", sendmail)
// package upload
rs.POST("/upload", packageUpload)
// get package list
rs.GET("/archive", packageList)
}
// localRouter init local router api path.
func localRouter(e *bm.Engine) {
e.GET("/x/macross/version", version)
}

View File

@@ -0,0 +1,30 @@
package http
import (
"net/http"
"go-common/app/admin/main/macross/conf"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
)
// ping check server ok.
func ping(c *bm.Context) {
if err := svr.Ping(c); err != nil {
log.Error("resource service ping error(%v)", err)
c.AbortWithStatus(http.StatusServiceUnavailable)
}
}
// version check server version.
func version(c *bm.Context) {
data := map[string]interface{}{
"version": conf.Conf.Version,
}
c.JSONMap(data, nil)
}
// register for discovery
func register(c *bm.Context) {
c.JSON(nil, nil)
}

View File

@@ -0,0 +1,70 @@
package http
import (
"encoding/json"
"fmt"
"go-common/app/admin/main/macross/model/mail"
"go-common/library/ecode"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"io/ioutil"
)
// sendMail send mail
func sendmail(c *bm.Context) {
req := c.Request
res := map[string]interface{}{}
res["message"] = "success"
var attach *mail.Attach
// 附件
file, header, err := c.Request.FormFile("file")
if err == nil {
defer file.Close()
attach = &mail.Attach{}
attach.Name = header.Filename
attach.File = file
unzip := c.Request.Form.Get("unzip")
if unzip != "" && unzip != "0" {
attach.ShouldUnzip = true
} else {
attach.ShouldUnzip = false
}
}
var bs []byte
if attach == nil {
bs, err = ioutil.ReadAll(req.Body)
} else {
// 使用 multipart 上传附件时body 并不是 json因此原来的 json 放在 form 的 json_body 中
jsonBody := c.Request.Form.Get("json_body")
bs = []byte(jsonBody)
}
if err != nil {
log.Error("ioutil.ReadAll() error(%v)", err)
res["message"] = fmt.Sprintf("%v", err)
c.JSONMap(res, ecode.RequestErr)
return
}
req.Body.Close()
// params
var m = &mail.Mail{}
if err = json.Unmarshal(bs, m); err != nil {
log.Error("http sendmail() json.Unmarshal(%s) error(%v)", string(bs), err)
res["message"] = fmt.Sprintf("%v", err)
c.JSONMap(res, ecode.RequestErr)
return
}
if m.Subject == "" || m.Body == "" || len(m.ToAddresses) == 0 {
res["message"] = fmt.Sprintf("%v", err)
c.JSONMap(res, ecode.RequestErr)
return
}
if err = svr.SendMail(c, m, attach); err != nil {
res["message"] = fmt.Sprintf("%v", err)
c.JSONMap(res, err)
return
}
c.JSONMap(res, nil)
}

View File

@@ -0,0 +1,18 @@
package http
import (
"go-common/library/ecode"
bm "go-common/library/net/http/blademaster"
)
// getAuths get user.
func getAuths(c *bm.Context) {
var userName string
username, _ := c.Get("username")
userName, ok := username.(string)
if !ok || userName == "" {
c.JSON(nil, ecode.NoLogin)
return
}
c.JSON(svr.GetAuths(c, userName))
}

View File

@@ -0,0 +1,88 @@
package http
import (
"strconv"
"go-common/library/ecode"
bm "go-common/library/net/http/blademaster"
)
// auth get auth by sysid.
func auth(c *bm.Context) {
var (
params = c.Request.Form
system string
)
if system = params.Get("system"); system == "" {
c.JSON(nil, ecode.RequestErr)
return
}
c.JSON(svr.Auth(c, system), nil)
}
// saveAuth save auth.
func saveAuth(c *bm.Context) {
var (
params = c.Request.Form
authID int64
authName, authFlag string
)
if authName = params.Get("auth_name"); authName == "" {
c.JSON(nil, ecode.RequestErr)
return
}
authIDStr := params.Get("auth_id")
authID, _ = strconv.ParseInt(authIDStr, 10, 64)
system := params.Get("system")
if authID == 0 && system == "" {
c.JSON(nil, ecode.RequestErr)
return
}
authFlag = params.Get("auth_flag")
if authID == 0 && system != "" && authFlag == "" {
c.JSON(nil, ecode.RequestErr)
return
}
c.JSON(nil, svr.SaveAuth(c, authID, system, authName, authFlag))
}
// delAuth del auth.
func delAuth(c *bm.Context) {
var (
params = c.Request.Form
authID int64
err error
)
authIDStr := params.Get("auth_id")
if authID, err = strconv.ParseInt(authIDStr, 10, 64); err != nil {
c.JSON(nil, ecode.RequestErr)
return
}
c.JSON(nil, svr.DelAuth(c, authID))
}
// authRelation update authRelation.
func authRelation(c *bm.Context) {
var (
params = c.Request.Form
roleID, authID int64
state int
err error
)
roleIDStr := params.Get("role_id")
if roleID, err = strconv.ParseInt(roleIDStr, 10, 64); err != nil {
c.JSON(nil, ecode.RequestErr)
return
}
authIDStr := params.Get("auth_id")
if authID, err = strconv.ParseInt(authIDStr, 10, 64); err != nil {
c.JSON(nil, ecode.RequestErr)
return
}
stateStr := params.Get("state")
if state, err = strconv.Atoi(stateStr); err != nil {
c.JSON(nil, ecode.RequestErr)
return
}
c.JSON(nil, svr.AuthRelation(c, roleID, authID, state))
}

View File

@@ -0,0 +1,57 @@
package http
import (
"strconv"
"go-common/library/ecode"
bm "go-common/library/net/http/blademaster"
)
// role get role by sysid.
func role(c *bm.Context) {
var (
params = c.Request.Form
system string
)
if system = params.Get("system"); system == "" {
c.JSON(nil, ecode.RequestErr)
return
}
c.JSON(svr.Role(c, system), nil)
}
// saveRole save role.
func saveRole(c *bm.Context) {
var (
params = c.Request.Form
roleID int64
roleName string
)
if roleName = params.Get("role_name"); roleName == "" {
c.JSON(nil, ecode.RequestErr)
return
}
roleIDStr := params.Get("role_id")
roleID, _ = strconv.ParseInt(roleIDStr, 10, 64)
system := params.Get("system")
if roleID == 0 && system == "" {
c.JSON(nil, ecode.RequestErr)
return
}
c.JSON(nil, svr.SaveRole(c, roleID, system, roleName))
}
// DelRole del role.
func DelRole(c *bm.Context) {
var (
params = c.Request.Form
roleID int64
err error
)
roleIDStr := params.Get("role_id")
if roleID, err = strconv.ParseInt(roleIDStr, 10, 64); err != nil {
c.JSON(nil, ecode.RequestErr)
return
}
c.JSON(nil, svr.DelRole(c, roleID))
}

View File

@@ -0,0 +1,63 @@
package http
import (
"strconv"
"go-common/library/ecode"
bm "go-common/library/net/http/blademaster"
)
// user get user by sysid.
func user(c *bm.Context) {
var (
params = c.Request.Form
system string
)
if system = params.Get("system"); system == "" {
c.JSON(nil, ecode.RequestErr)
return
}
c.JSON(svr.User(c, system), nil)
}
// saveUser save user.
func saveUser(c *bm.Context) {
var (
params = c.Request.Form
roleID, userID int64
name string
err error
)
if name = params.Get("user_name"); name == "" {
c.JSON(nil, ecode.RequestErr)
return
}
roleIDStr := params.Get("role_id")
if roleID, err = strconv.ParseInt(roleIDStr, 10, 64); err != nil {
c.JSON(nil, ecode.RequestErr)
return
}
userIDStr := params.Get("user_id")
userID, _ = strconv.ParseInt(userIDStr, 10, 64)
system := params.Get("system")
if userID == 0 && system == "" {
c.JSON(nil, ecode.RequestErr)
return
}
c.JSON(nil, svr.SaveUser(c, roleID, userID, system, name))
}
// delUser del user.
func delUser(c *bm.Context) {
var (
params = c.Request.Form
userID int64
err error
)
userIDStr := params.Get("user_id")
if userID, err = strconv.ParseInt(userIDStr, 10, 64); err != nil {
c.JSON(nil, ecode.RequestErr)
return
}
c.JSON(nil, svr.DelUser(c, userID))
}

View File

@@ -0,0 +1,108 @@
package http
import (
"fmt"
"go-common/app/admin/main/macross/conf"
"go-common/app/admin/main/macross/model/package"
"go-common/library/ecode"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"path/filepath"
"strings"
)
func packageUpload(c *bm.Context) {
var err = c.Request.ParseMultipartForm(1 << 30)
res := map[string]interface{}{}
res["message"] = "success"
if err != nil {
log.Error("c.Request.ParseMultipartForm() error(%v)", err)
res["message"] = fmt.Sprintf("%v", err)
c.JSONMap(res, err)
return
}
file, header, err := c.Request.FormFile("file")
if err != nil {
log.Error("c.Request.FormFile() error(%v)", err)
res["message"] = fmt.Sprintf("%v", err)
c.JSONMap(res, err)
return
}
defer file.Close()
var (
clientType = strings.ToLower(c.Request.FormValue("client_type"))
appName = c.Request.FormValue("app_name")
pipelineID = c.Request.FormValue("pipeline_id")
apkName = c.Request.FormValue("apk_name")
channel = c.Request.FormValue("channel")
saveDir = filepath.Join(clientType, appName, pipelineID)
pkgInfo upload.PkgInfo
)
if clientType == "" || appName == "" || pipelineID == "" {
errMsg := "client_type, app_name, pipeline_id can not be null"
log.Error(errMsg)
res["message"] = errMsg
c.JSONMap(res, ecode.RequestErr)
return
}
if clientType != "ios" && clientType != "android" {
errMsg := "client_type must be 'ios' or 'android'"
log.Error(errMsg)
res["message"] = errMsg
c.JSONMap(res, ecode.RequestErr)
return
}
pkgInfo.FileName = header.Filename
pkgInfo.SaveDir = filepath.Join(conf.Conf.Property.Package.SavePath, saveDir)
pkgInfo.ClientType = clientType
pkgInfo.Channel = channel
pkgInfo.ApkName = apkName
err = svr.PackageUpload(file, pkgInfo)
if err != nil {
res["message"] = fmt.Sprintf("%v", err)
c.JSONMap(res, err)
return
}
c.JSONMap(res, nil)
}
func packageList(c *bm.Context) {
var (
clientType = strings.ToLower(c.Request.FormValue("client_type"))
appName = c.Request.Form.Get("app_name")
pipelineID = c.Request.Form.Get("pipeline_id")
saveDir = filepath.Join(clientType, appName, pipelineID)
)
res := map[string]interface{}{}
res["message"] = "success"
saveDir = filepath.Join(conf.Conf.Property.Package.SavePath, saveDir)
if clientType == "" || appName == "" || pipelineID == "" {
errMsg := "client_type, app_name, pipeline_id can not be null"
log.Error(errMsg)
res["message"] = errMsg
c.JSONMap(res, ecode.RequestErr)
return
}
if clientType != "ios" && clientType != "android" {
errMsg := "client_type must be 'ios' or 'android'"
log.Error(errMsg)
res["message"] = errMsg
c.JSONMap(res, ecode.RequestErr)
return
}
fileList, err := svr.PackageList(saveDir)
if err != nil {
res["message"] = fmt.Sprintf("%v", err)
c.JSONMap(res, err)
return
}
c.JSON(fileList, nil)
}

View File

@@ -0,0 +1,35 @@
package http
import (
"encoding/json"
"io/ioutil"
"go-common/app/admin/main/macross/model/publish"
"go-common/library/ecode"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
)
// dashboard get user.
func dashboard(c *bm.Context) {
req := c.Request
bs, err := ioutil.ReadAll(req.Body)
if err != nil {
log.Error("ioutil.ReadAll() error(%v)", err)
c.JSON(nil, ecode.RequestErr)
return
}
req.Body.Close()
// params
var d = &publish.Dashboard{}
if err = json.Unmarshal(bs, d); err != nil {
log.Error("http dashboard() json.Unmarshal(%s) error(%v)", string(bs), err)
c.JSON(nil, ecode.RequestErr)
return
}
if d.Name == "" || d.Label == "" || d.Commit == "" || d.TextSizeArm64 == 0 {
c.JSON(nil, ecode.RequestErr)
return
}
c.JSON(nil, svr.Dashborad(c, d))
}

View File

@@ -0,0 +1,29 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["errors.go"],
importpath = "go-common/app/admin/main/macross/model/errors",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = ["//library/ecode:go_default_library"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,28 @@
package errors
import (
"fmt"
"go-common/library/ecode"
)
// DependError for show like "model(%s) code(%s) error"
type DependError struct {
errInfo string
Err error
}
// New new a video error.
func New(errInfo string, m error) error {
return &DependError{errInfo, m}
}
// Ecode return ecode.
func (e *DependError) Ecode() error {
return e.Err
}
// Error implement error interface.
func (e *DependError) Error() string {
m := ecode.String(e.Err.Error()).Message()
return fmt.Sprintf(m, e.errInfo)
}

View File

@@ -0,0 +1,28 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["mail.go"],
importpath = "go-common/app/admin/main/macross/model/mail",
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,35 @@
package mail
import "mime/multipart"
// Type for mail
type Type uint8
// Mail types
const (
TypeTextPlain Type = iota
TypeTextHTML
)
// Attach def.
type Attach struct {
Name string
File multipart.File
ShouldUnzip bool
}
// Mail def.
type Mail struct {
ToAddresses []*Address `json:"to_addresses"`
CcAddresses []*Address `json:"cc_addresses"`
BccAddresses []*Address `json:"bcc_addresses"`
Subject string `json:"subject"`
Body string `json:"body"`
Type Type `json:"type"`
}
// Address def.
type Address struct {
Address string `json:"address"`
Name string `json:"name"`
}

View File

@@ -0,0 +1,32 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"manager.go",
"model.go",
],
importpath = "go-common/app/admin/main/macross/model/manager",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = ["//library/time:go_default_library"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,56 @@
package manager
import "go-common/library/time"
// User for manager.
type User struct {
UserID int64 `json:"user_id"`
System string `json:"-"`
UserName string `json:"user_name"`
RoleID int64 `json:"role_id"`
RoleName string `json:"role_name"`
CTime time.Time `json:"-"`
MTime time.Time `json:"-"`
}
// Role for manager.
type Role struct {
RoleID int64 `json:"role_id"`
System string `json:"-"`
RoleName string `json:"role_name"`
Auths map[string]*Auth `json:"auths"`
Models map[string]*Model `json:"models"`
CTime time.Time `json:"-"`
MTime time.Time `json:"-"`
}
// Auth for manager.
type Auth struct {
AuthID int64 `json:"auth_id"`
System string `json:"-"`
AuthName string `json:"auth_name"`
AuthFlag string `json:"auth_flag"`
CTime time.Time `json:"-"`
MTime time.Time `json:"-"`
}
// Users User sorted.
type Users []*User
func (u Users) Len() int { return len(u) }
func (u Users) Less(i, j int) bool { return int64(u[i].UserID) < int64(u[j].UserID) }
func (u Users) Swap(i, j int) { u[i], u[j] = u[j], u[i] }
// Roles Role sorted.
type Roles []*Role
func (r Roles) Len() int { return len(r) }
func (r Roles) Less(i, j int) bool { return int64(r[i].RoleID) < int64(r[j].RoleID) }
func (r Roles) Swap(i, j int) { r[i], r[j] = r[j], r[i] }
// Auths Auth sorted.
type Auths []*Auth
func (a Auths) Len() int { return len(a) }
func (a Auths) Less(i, j int) bool { return int64(a[i].AuthID) < int64(a[j].AuthID) }
func (a Auths) Swap(i, j int) { a[i], a[j] = a[j], a[i] }

View File

@@ -0,0 +1,23 @@
package manager
import "go-common/library/time"
// Model for manager.
type Model struct {
ModelID int64 `json:"model_id"`
System string `json:"-"`
ModelName string `json:"model_name"`
ModelFlag string `json:"model_flag"`
HasDependence bool `json:"has_dependence"`
GitURL string `json:"git_url"`
Count int64 `json:"count"`
CTime time.Time `json:"-"`
MTime time.Time `json:"-"`
}
// Models model sorted.
type Models []*Model
func (a Models) Len() int { return len(a) }
func (a Models) Less(i, j int) bool { return int64(a[i].ModelID) < int64(a[j].ModelID) }
func (a Models) Swap(i, j int) { a[i], a[j] = a[j], a[i] }

View File

@@ -0,0 +1,26 @@
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["package.go"],
importpath = "go-common/app/admin/main/macross/model/package",
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,10 @@
package upload
// PkgInfo which will be uploaded.
type PkgInfo struct {
SaveDir string
FileName string
ClientType string
Channel string
ApkName string
}

View File

@@ -0,0 +1,28 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["publish.go"],
importpath = "go-common/app/admin/main/macross/model/publish",
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,20 @@
package publish
// Dashboard for dashboard.
type Dashboard struct {
Name string `json:"name"`
Label string `json:"label"`
Commit string `json:"commit"`
OutURL string `json:"out_url"`
CoverageURL string `json:"coverage_url"`
TextSizeArm64 int64 `json:"text_size_arm64"`
ResSize int64 `json:"res_size"`
Logs []*Log `json:"logs"`
Extra string `json:"extra"`
}
// Log from Dashboard.
type Log struct {
Level string `json:"level"`
Msg string `json:"msg"`
}

View File

@@ -0,0 +1,48 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"mail.go",
"mng_auth.go",
"mng_role.go",
"mng_user.go",
"package.go",
"publish.go",
"service.go",
],
importpath = "go-common/app/admin/main/macross/service",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/admin/main/macross/conf:go_default_library",
"//app/admin/main/macross/dao:go_default_library",
"//app/admin/main/macross/dao/oss:go_default_library",
"//app/admin/main/macross/model/mail:go_default_library",
"//app/admin/main/macross/model/manager:go_default_library",
"//app/admin/main/macross/model/package:go_default_library",
"//app/admin/main/macross/model/publish:go_default_library",
"//app/admin/main/macross/tools:go_default_library",
"//library/log:go_default_library",
"//vendor/gopkg.in/gomail.v2:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,120 @@
package service
import (
"context"
"fmt"
"go-common/app/admin/main/macross/conf"
"go-common/app/admin/main/macross/model/mail"
"go-common/app/admin/main/macross/tools"
"go-common/library/log"
"io"
"os"
"path/filepath"
"strings"
gomail "gopkg.in/gomail.v2"
)
// SendMail send mail
func (s *Service) SendMail(c context.Context, m *mail.Mail, attach *mail.Attach) (err error) {
var (
toUsers []string
ccUsers []string
bccUsers []string
msg = gomail.NewMessage()
)
msg.SetAddressHeader("From", conf.Conf.Property.Mail.Address, conf.Conf.Property.Mail.Name) // 发件人
for _, ads := range m.ToAddresses {
toUsers = append(toUsers, msg.FormatAddress(ads.Address, ads.Name))
}
for _, ads := range m.CcAddresses {
ccUsers = append(ccUsers, msg.FormatAddress(ads.Address, ads.Name))
}
for _, ads := range m.BccAddresses {
bccUsers = append(bccUsers, msg.FormatAddress(ads.Address, ads.Name))
}
msg.SetHeader("To", toUsers...)
msg.SetHeader("Subject", m.Subject) // 主题
if len(ccUsers) > 0 {
msg.SetHeader("Cc", ccUsers...)
}
if len(bccUsers) > 0 {
msg.SetHeader("Bcc", bccUsers...)
}
if m.Type == mail.TypeTextHTML {
msg.SetBody("text/html", m.Body)
} else {
msg.SetBody("text/plain", m.Body)
}
// 附件处理
if attach != nil {
tmpSavePath := filepath.Join(os.TempDir(), "mail_tmp")
err = os.MkdirAll(tmpSavePath, 0755)
if err != nil {
log.Error("os.MkdirAll error(%v)", err)
return
}
destFilePath := filepath.Join(tmpSavePath, attach.Name)
destFile, cErr := os.Create(destFilePath)
if cErr != nil {
log.Error("os.Create(%s) error(%v)", destFilePath, cErr)
return cErr
}
defer os.RemoveAll(tmpSavePath)
io.Copy(destFile, attach.File)
// 如果 zip 文件需要解压以后放在邮件附件中
if attach.ShouldUnzip && strings.HasSuffix(attach.Name, ".zip") {
unzipFilePath := filepath.Join(tmpSavePath, "unzip")
err = os.MkdirAll(tmpSavePath, 0755)
if err != nil {
log.Error("os.MkdirAll error(%v)", err)
return
}
err = tools.Unzip(destFilePath, unzipFilePath)
if err != nil {
log.Error("unzip(%s, %s) error(%v)", destFilePath, unzipFilePath, err)
return
}
err = filepath.Walk(unzipFilePath, func(path string, f os.FileInfo, err error) error {
if err != nil {
log.Error("filepath.Walk error(%v)", err)
return err
}
if f == nil {
errMsg := "found no file"
err = fmt.Errorf(errMsg)
log.Error(errMsg)
return err
}
if f.IsDir() {
return nil
}
msg.Attach(path)
return err
})
} else {
msg.Attach(destFilePath)
}
}
d := gomail.NewDialer(
conf.Conf.Property.Mail.Host,
conf.Conf.Property.Mail.Port,
conf.Conf.Property.Mail.Address,
conf.Conf.Property.Mail.Pwd,
)
if err = d.DialAndSend(msg); err != nil {
log.Error("Send mail Fail(%v) diff(%s)", msg, err)
return
}
return
}

View File

@@ -0,0 +1,111 @@
package service
import (
"context"
"sort"
model "go-common/app/admin/main/macross/model/manager"
"go-common/library/log"
)
// GetAuths get auths.
func (s *Service) GetAuths(c context.Context, name string) (res map[string]map[string]*model.Auth, err error) {
res = make(map[string]map[string]*model.Auth)
for system, users := range s.user {
var (
user *model.User
authIDs []int64
auths map[int64]*model.Auth
resTmp map[string]*model.Auth
ok bool
)
if user, ok = users[name]; !ok {
continue
}
if authIDs, ok = s.authRelation[user.RoleID]; !ok {
continue
}
if auths, ok = s.auth[system]; !ok {
continue
}
for _, authID := range authIDs {
if auth, ok := auths[authID]; ok {
if resTmp, ok = res[system]; !ok {
resTmp = make(map[string]*model.Auth)
res[system] = resTmp
}
resTmp[auth.AuthFlag] = auth
}
}
}
return
}
// Auth get auth.
func (s *Service) Auth(c context.Context, system string) (res []*model.Auth) {
for _, auth := range s.auth[system] {
res = append(res, auth)
}
sort.Sort(model.Auths(res))
return
}
// SaveAuth save auth.
func (s *Service) SaveAuth(c context.Context, authID int64, system, authName, authFlag string) (err error) {
var rows int64
if authID == 0 {
if rows, err = s.dao.AddAuth(c, system, authName, authFlag); err != nil {
log.Error("s.dao.AddAuth(%s, %s, %s) error(%v)", system, authName, authFlag, err)
return
}
} else {
if rows, err = s.dao.UpAuth(c, authName, authID); err != nil {
log.Error("s.dao.UpAuth(%s, %d) error(%v)", authName, authID, err)
return
}
}
if rows != 0 {
// update cache
s.loadAuthCache()
}
return
}
// DelAuth del auth.
func (s *Service) DelAuth(c context.Context, authID int64) (err error) {
var rows int64
if rows, err = s.dao.DelAuth(c, authID); err != nil {
log.Error("s.dao.DelAuth(%d) error(%s)", authID, err)
return
} else if rows != 0 {
// update cache
s.loadAuthCache()
if rows, err = s.dao.CleanAuthRelationByAuth(c, authID); err != nil {
log.Error("s.dao.CleanAuthRelationByAuth(%d) error(%v)", authID, err)
return
} else if rows != 0 {
s.loadAuthRelationCache()
}
}
return
}
// AuthRelation get auth relation.
func (s *Service) AuthRelation(c context.Context, roleID, authID int64, state int) (err error) {
var rows int64
if state == 0 {
if rows, err = s.dao.DelAuthRelation(c, roleID, authID); err != nil {
log.Error("s.dao.DelAuthRelation(%d, %d) error(%v)", roleID, authID, err)
return
}
} else {
if rows, err = s.dao.AddAuthRelation(c, roleID, authID); err != nil {
log.Error("s.dao.AddAuthRelation(%d, %d) error(%v)", roleID, authID, err)
return
}
}
if rows != 0 {
s.loadAuthRelationCache()
}
return
}

View File

@@ -0,0 +1,72 @@
package service
import (
"context"
"sort"
"go-common/library/log"
model "go-common/app/admin/main/macross/model/manager"
)
// Role get role.
func (s *Service) Role(c context.Context, system string) (res []*model.Role) {
for _, role := range s.role[system] {
if role == nil {
continue
}
role.Auths = make(map[string]*model.Auth)
if authIDs, ok := s.authRelation[role.RoleID]; ok {
if auths, ok := s.auth[system]; ok {
for _, authID := range authIDs {
if auth, ok := auths[authID]; ok {
role.Auths[auth.AuthFlag] = auth
}
}
}
}
res = append(res, role)
}
sort.Sort(model.Roles(res))
return
}
// SaveRole save role.
func (s *Service) SaveRole(c context.Context, roleID int64, system, roleName string) (err error) {
var rows int64
if roleID == 0 {
if rows, err = s.dao.AddRole(c, system, roleName); err != nil {
log.Error("s.dao.AddRole(%s, %s) error(%v)", system, roleName, err)
return
}
} else {
if rows, err = s.dao.UpRole(c, roleName, roleID); err != nil {
log.Error("s.dao.UpRole(%s, %d) error(%v)", roleName, roleID, err)
return
}
}
if rows != 0 {
// update cache
s.loadRoleCache()
}
return
}
// DelRole del role.
func (s *Service) DelRole(c context.Context, roleID int64) (err error) {
var rows int64
if rows, err = s.dao.DelRole(c, roleID); err != nil {
log.Error("s.dao.DelRole(%d) error(%s)", roleID, err)
return
} else if rows != 0 {
// update cache
s.loadRoleCache()
if rows, err = s.dao.CleanAuthRelationByRole(c, roleID); err != nil {
log.Error("s.dao.CleanAuthRelationByRole(%d) error(%s)", roleID, err)
return
} else if rows != 0 {
s.loadAuthRelationCache()
}
}
return
}

View File

@@ -0,0 +1,56 @@
package service
import (
"context"
"sort"
"go-common/library/log"
model "go-common/app/admin/main/macross/model/manager"
)
// User get user.
func (s *Service) User(c context.Context, system string) (res []*model.User) {
users, ok := s.user[system]
if ok {
for _, user := range users {
res = append(res, user)
}
}
sort.Sort(model.Users(res))
return
}
// SaveUser save user.
func (s *Service) SaveUser(c context.Context, roleID, userID int64, system, userName string) (err error) {
var rows int64
if userID == 0 {
if rows, err = s.dao.AddUser(c, roleID, system, userName); err != nil {
log.Error("s.dao.AddUser(%d, %s, %s) error(%v)", roleID, system, userName, err)
return
}
} else {
if rows, err = s.dao.UpUser(c, userID, roleID, userName); err != nil {
log.Error("s.dao.UpUser(%d, %d, %s) error(%v)", userID, roleID, userName, err)
return
}
}
// update cache
if rows != 0 {
s.loadUserCache()
}
return
}
// DelUser del user.
func (s *Service) DelUser(c context.Context, userID int64) (err error) {
var rows int64
if rows, err = s.dao.DelUser(c, userID); err != nil {
log.Error("s.dao.DelUser(%d) error(%v)", userID, err)
return
} else if rows != 0 {
// update cache
s.loadUserCache()
}
return
}

View File

@@ -0,0 +1,82 @@
package service
import (
"fmt"
"go-common/app/admin/main/macross/conf"
"go-common/app/admin/main/macross/model/package"
"go-common/app/admin/main/macross/tools"
"go-common/library/log"
"io"
"mime/multipart"
"os"
"path/filepath"
"strings"
)
// PackageUpload upload package zip file & unzip it
func (s *Service) PackageUpload(file multipart.File, pkgInfo upload.PkgInfo) (err error) {
err = os.MkdirAll(pkgInfo.SaveDir, 0755)
if err != nil {
log.Error("os.MkdirAll error(%v)", err)
return
}
destFilePath := filepath.Join(pkgInfo.SaveDir, pkgInfo.FileName)
destFile, err := os.Create(destFilePath)
if err != nil {
log.Error("os.Create(%s) error(%v)", destFilePath, err)
return
}
io.Copy(destFile, file)
err = tools.Unzip(destFilePath, pkgInfo.SaveDir)
if err != nil {
log.Error("unzip(%s, %s) error(%v)", destFilePath, pkgInfo.SaveDir, err)
return
}
err = os.Remove(destFilePath)
if err != nil {
log.Error("os.Remove(%s) error(%v)", destFilePath, err)
return
}
// generate Android channel package
if pkgInfo.ClientType == "android" {
if len(pkgInfo.Channel) > 0 && len(pkgInfo.ApkName) > 0 {
dest := filepath.Join(pkgInfo.SaveDir, "channel")
targetFilePath := filepath.Join(pkgInfo.SaveDir, pkgInfo.ApkName)
_, err = tools.GenerateChannelApk(dest, pkgInfo.Channel, nil, targetFilePath, false, false)
if err != nil {
log.Error("tools.GenerateChannelApk(%s, %s, nil, %s, false, false) error(%v)", dest, pkgInfo.Channel, targetFilePath, err)
os.RemoveAll(pkgInfo.SaveDir)
return
}
}
}
return
}
// PackageList list the Package Files
func (s *Service) PackageList(path string) (fileList []string, err error) {
err = filepath.Walk(path, func(path string, f os.FileInfo, err error) error {
if err != nil {
log.Error("filepath.Walk error(%v)", err)
return err
}
if f == nil {
errMsg := "found no file"
err = fmt.Errorf(errMsg)
log.Error(errMsg)
return err
}
if f.IsDir() {
return nil
}
fileURL := strings.Replace(path, conf.Conf.Property.Package.SavePath, conf.Conf.Property.Package.URLPrefix, -1)
fileList = append(fileList, fileURL)
return err
})
return
}

View File

@@ -0,0 +1,24 @@
package service
import (
"context"
"go-common/app/admin/main/macross/model/publish"
"go-common/library/log"
)
// Dashborad insert dashboard info and logs.
func (s *Service) Dashborad(c context.Context, d *publish.Dashboard) (err error) {
var id int64
if id, err = s.dao.Dashborad(c, d); err != nil {
log.Error("Dashborad() error(%v)", err)
return
}
if len(d.Logs) > 0 && id > 0 {
if _, err = s.dao.DashboradLogs(c, id, d.Logs); err != nil {
log.Error("DashboradLogs() error(%v)", err)
return
}
}
return
}

View File

@@ -0,0 +1,131 @@
package service
import (
"context"
"io"
"time"
"go-common/library/log"
"go-common/app/admin/main/macross/conf"
"go-common/app/admin/main/macross/dao"
"go-common/app/admin/main/macross/dao/oss"
model "go-common/app/admin/main/macross/model/manager"
)
// Service service struct info.
type Service struct {
c *conf.Config
oss *oss.Dao
dao *dao.Dao
// manager cache
user map[string]map[string]*model.User // system => { username => managerInfo }
role map[string]map[int64]*model.Role // system => { roleId => roleInfo }
authRelation map[int64][]int64 // role_id => [ auth_id ]
auth map[string]map[int64]*model.Auth // system => { authId => authInfo }
// ios cache
modelNameCache map[string]map[string]int64
}
// New service.
func New(c *conf.Config) (s *Service) {
s = &Service{
c: c,
dao: dao.New(c),
oss: oss.New(c),
// init manager cache
user: make(map[string]map[string]*model.User),
role: make(map[string]map[int64]*model.Role),
authRelation: make(map[int64][]int64),
auth: make(map[string]map[int64]*model.Auth),
// init ios cache
modelNameCache: make(map[string]map[string]int64),
}
// manager cache
if err := s.loadUserCache(); err != nil {
panic(err)
}
if err := s.loadRoleCache(); err != nil {
panic(err)
}
if err := s.loadAuthRelationCache(); err != nil {
panic(err)
}
if err := s.loadAuthCache(); err != nil {
panic(err)
}
go s.loadproc()
return
}
// loadproc is a routine load to cache
func (s *Service) loadproc() {
for {
time.Sleep(time.Duration(conf.Conf.Reload))
s.loadUserCache()
s.loadRoleCache()
s.loadAuthRelationCache()
s.loadAuthCache()
}
}
func (s *Service) loadUserCache() (err error) {
var tmpUser map[string]map[string]*model.User
if tmpUser, err = s.dao.Users(context.TODO()); err != nil {
log.Error("s.dao.Users() error(%v)", err)
return
}
s.user = tmpUser
return
}
func (s *Service) loadRoleCache() (err error) {
var tmpRole map[string]map[int64]*model.Role
if tmpRole, err = s.dao.Roles(context.TODO()); err != nil {
log.Error("s.dao.Roles() error(%v)", err)
return
}
s.role = tmpRole
return
}
func (s *Service) loadAuthRelationCache() (err error) {
var tmpAuthRelation map[int64][]int64
if tmpAuthRelation, err = s.dao.AuthRelation(context.TODO()); err != nil {
log.Error("s.dao.AuthRelation() error(%v)", err)
return
}
s.authRelation = tmpAuthRelation
return
}
func (s *Service) loadAuthCache() (err error) {
var tmpAuth map[string]map[int64]*model.Auth
if tmpAuth, err = s.dao.Auths(context.TODO()); err != nil {
log.Error("s.dao.Auths() error(%v)", err)
return
}
s.auth = tmpAuth
return
}
// DiffPutOss upload diff to oss
func (s *Service) DiffPutOss(c context.Context, f io.Reader, filename string) (uri string, err error) {
if uri, err = s.oss.Put(c, f, filename); err != nil {
log.Error("s.oss.Put(%s) error(%v)", filename, err)
}
return
}
// Ping dao
func (s *Service) Ping(c context.Context) (err error) {
if err = s.dao.Ping(c); err != nil {
log.Error("s.dao error(%v)", err)
}
return
}
// Close dao
func (s *Service) Close() {
s.dao.Close()
}

View File

@@ -0,0 +1,34 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"apk.go",
"helper.go",
"unzip.go",
"writer.go",
],
importpath = "go-common/app/admin/main/macross/tools",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = ["//library/log:go_default_library"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,378 @@
package tools
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"math"
"os"
)
const (
apkSigBlockMinSize uint32 = 32
// https://android.googlesource.com/platform/build/+/android-7.1.2_r27/tools/signapk/src/com/android/signapk/ApkSignerV2.java
// APK_SIGNING_BLOCK_MAGIC = {
// 0x41, 0x50, 0x4b, 0x20, 0x53, 0x69, 0x67, 0x20,
// 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x34, 0x32 }
apkSigBlockMagicHi = 0x3234206b636f6c42 // LITTLE_ENDIAN, High
apkSigBlockMagicLo = 0x20676953204b5041 // LITTLE_ENDIAN, Low
apkChannelBlockID = 0x71777777
// https://en.wikipedia.org/wiki/Zip_(file_format)
// https://android.googlesource.com/platform/build/+/android-7.1.2_r27/tools/signapk/src/com/android/signapk/ZipUtils.java
zipEocdRecSig = 0x06054b50
zipEocdRecMinSize = 22
zipEocdCentralDirSizeFieldOffset = 12
zipEocdCentralDirOffsetFieldOffset = 16
zipEocdCommentLengthFieldOffset = 20
)
// ChannelInfo for apk
type ChannelInfo struct {
Channel string
Extras map[string]string
raw []byte
}
// ChannelInfo to string
func (c *ChannelInfo) String() string {
b := c.Bytes()
if b == nil {
return ""
}
return string(b)
}
// Bytes for ChannelInfo to byte array
func (c *ChannelInfo) Bytes() []byte {
if c.raw != nil {
return c.raw
}
if len(c.Channel) == 0 && c.Extras == nil {
return nil
}
var buf bytes.Buffer
buf.WriteByte('{')
if len(c.Channel) != 0 {
buf.WriteString("\"channel\":")
buf.WriteByte('"')
buf.WriteString(c.Channel)
buf.WriteByte('"')
buf.WriteByte(',')
}
if c.Extras != nil {
for k, v := range c.Extras {
buf.WriteByte('"')
buf.WriteString(k)
buf.WriteByte('"')
buf.WriteByte(':')
buf.WriteByte('"')
buf.WriteString(v)
buf.WriteByte('"')
buf.WriteByte(',')
}
}
if buf.Len() > 2 {
buf.Truncate(buf.Len() - 1)
}
buf.WriteByte('}')
return buf.Bytes()
}
func readChannelInfo(file string) (c ChannelInfo, err error) {
block, err := readChannelBlock(file)
if err != nil {
return c, err
}
if block != nil {
var bundle map[string]string
err := json.Unmarshal(block, &bundle)
if err != nil {
return c, err
}
c.Channel = bundle["channel"]
delete(bundle, "channel")
c.Extras = bundle
c.raw = block
}
return c, nil
}
// read block associated to apkChannelBlockID
func readChannelBlock(file string) ([]byte, error) {
m, err := readIDValues(file, apkChannelBlockID)
if err != nil {
return nil, err
}
return m[apkChannelBlockID], nil
}
func readIDValues(file string, ids ...uint32) (map[uint32][]byte, error) {
f, err := os.Open(file)
if err != nil {
return nil, err
}
defer f.Close()
eocd, offset, err := findEndOfCentralDirectoryRecord(f)
if err != nil {
return nil, err
}
if offset <= 0 {
return nil, errors.New("Cannot find EOCD record, maybe a broken zip file")
}
centralDirOffset := getEocdCentralDirectoryOffset(eocd)
block, _, err := findApkSigningBlock(f, centralDirOffset)
if err != nil {
return nil, err
}
return findIDValuesInApkSigningBlock(block, ids...)
}
// End of central directory record (EOCD)
//
// Offset Bytes Description[23]
// 0 4 End of central directory signature = 0x06054b50
// 4 2 Number of this disk
// 6 2 Disk where central directory starts
// 8 2 Number of central directory records on this disk
// 10 2 Total number of central directory records
// 12 4 Size of central directory (bytes)
// 16 4 Offset of start of central directory, relative to start of archive
// 20 2 Comment length (n)
// 22 n Comment
// For a zip with no archive comment, the
// end-of-central-directory record will be 22 bytes long, so
// we expect to find the EOCD marker 22 bytes from the end.
func findEndOfCentralDirectoryRecord(f *os.File) ([]byte, int64, error) {
fi, err := f.Stat()
if err != nil {
return nil, -1, err
}
if fi.Size() < zipEocdRecMinSize {
// No space for EoCD record in the file.
return nil, -1, nil
}
// Optimization: 99.99% of APKs have a zero-length comment field in the EoCD record and thus
// the EoCD record offset is known in advance. Try that offset first to avoid unnecessarily
// reading more data.
ret, offset, err := findEOCDRecord(f, 0)
if err != nil {
return nil, -1, err
}
if ret != nil && offset != -1 {
return ret, offset, nil
}
// EoCD does not start where we expected it to. Perhaps it contains a non-empty comment
// field. Expand the search. The maximum size of the comment field in EoCD is 65535 because
// the comment length field is an unsigned 16-bit number.
return findEOCDRecord(f, math.MaxUint16)
}
func findEOCDRecord(f *os.File, maxCommentSize uint16) ([]byte, int64, error) {
if maxCommentSize > math.MaxUint16 {
return nil, -1, os.ErrInvalid
}
fi, err := f.Stat()
if err != nil {
return nil, -1, err
}
fileSize := fi.Size()
if fileSize < zipEocdRecMinSize {
// No space for EoCD record in the file.
return nil, -1, nil
}
// Lower maxCommentSize if the file is too small.
if s := uint16(fileSize - zipEocdRecMinSize); maxCommentSize > s {
maxCommentSize = s
}
maxEocdSize := zipEocdRecMinSize + maxCommentSize
bufOffsetInFile := fileSize - int64(maxEocdSize)
buf := make([]byte, maxEocdSize)
n, e := f.ReadAt(buf, bufOffsetInFile)
if e != nil {
return nil, -1, err
}
eocdOffsetInFile :=
func() int64 {
eocdWithEmptyCommentStartPosition := n - zipEocdRecMinSize
for expectedCommentLength := uint16(0); expectedCommentLength < maxCommentSize; expectedCommentLength++ {
eocdStartPos := eocdWithEmptyCommentStartPosition - int(expectedCommentLength)
if getUint32(buf, eocdStartPos) == zipEocdRecSig {
n := eocdStartPos + zipEocdCommentLengthFieldOffset
actualCommentLength := getUint16(buf, n)
if actualCommentLength == expectedCommentLength {
return int64(eocdStartPos)
}
}
}
return -1
}()
if eocdOffsetInFile == -1 {
// No EoCD record found in the buffer
return nil, -1, nil
}
// EoCD found
return buf[eocdOffsetInFile:], bufOffsetInFile + eocdOffsetInFile, nil
}
func getEocdCentralDirectoryOffset(buf []byte) uint32 {
return getUint32(buf, zipEocdCentralDirOffsetFieldOffset)
}
func getEocdCentralDirectorySize(buf []byte) uint32 {
return getUint32(buf, zipEocdCentralDirSizeFieldOffset)
}
func setEocdCentralDirectoryOffset(eocd []byte, offset uint32) {
putUint32(offset, eocd, zipEocdCentralDirOffsetFieldOffset)
}
func isExpected(ids []uint32, test uint32) bool {
for _, id := range ids {
if id == test {
return true
}
}
return false
}
func findIDValuesInApkSigningBlock(block []byte, ids ...uint32) (map[uint32][]byte, error) {
ret := make(map[uint32][]byte)
position := 8
limit := len(block) - 24
entryCount := 0
for limit > position { // has remaining bytes
entryCount++
if limit-position < 8 { // but not enough
return nil, fmt.Errorf("APK Signing Block broken on entry #%d", entryCount)
}
length := int(getUint64(block, position))
position += 8
if length < 4 || length > limit-position {
return nil, fmt.Errorf("APK Signing Block broken on entry #%d,"+
" size out of range: length=%d, remaining=%d", entryCount, length, limit-position)
}
nextEntryPosition := position + length
id := getUint32(block, position)
position += 4
if len(ids) == 0 || isExpected(ids, id) {
ret[id] = block[position : position+length-4]
}
position = nextEntryPosition
}
return ret, nil
}
// Find the APK Signing Block. The block immediately precedes the Central Directory.
//
// FORMAT:
// uint64: size (excluding this field)
// repeated ID-value pairs:
// uint64: size (excluding this field)
// uint32: ID
// (size - 4) bytes: value
// uint64: size (same as the one above)
// uint128: magic
func findApkSigningBlock(f *os.File, centralDirOffset uint32) (block []byte, offset int64, err error) {
if centralDirOffset < apkSigBlockMinSize {
return block, offset, fmt.Errorf("APK too small for APK Signing Block."+
" ZIP Central Directory offset: %d", centralDirOffset)
}
// Read the footer of APK signing block
// 24 = sizeof(uint128) + sizeof(uint64)
footer := make([]byte, 24)
_, err = f.ReadAt(footer, int64(centralDirOffset-24))
if err != nil {
return
}
// Read the magic and block size
var blockSizeInFooter = getUint64(footer, 0)
if blockSizeInFooter < 24 || blockSizeInFooter > uint64(math.MaxInt32-8 /* ID-value size field*/) {
return block, offset, fmt.Errorf("APK Signing Block size out of range: %d", blockSizeInFooter)
}
if getUint64(footer, 8) != apkSigBlockMagicLo ||
getUint64(footer, 16) != apkSigBlockMagicHi {
return block, offset, errors.New("No APK Signing Block before ZIP Central Directory")
}
totalSize := blockSizeInFooter + 8 /* APK signing block size field*/
offset = int64(uint64(centralDirOffset) - totalSize)
if offset <= 0 {
return block, offset, fmt.Errorf("invalid offset for APK Signing Block %d", offset)
}
block = make([]byte, totalSize)
_, err = f.ReadAt(block, offset)
if err != nil {
return
}
blockSizeInHeader := getUint64(block, 0)
if blockSizeInHeader != blockSizeInFooter {
return nil, offset, fmt.Errorf("APK Signing Block sizes in header "+
"and footer are mismatched! Except %d but %d", blockSizeInFooter, blockSizeInHeader)
}
return block, offset, nil
}
// FORMAT:
// uint64: size (excluding this field)
// repeated ID-value pairs:
// uint64: size (excluding this field)
// uint32: ID
// (size - 4) bytes: value
// uint64: size (same as the one above)
// uint128: magic
func makeSigningBlockWithChannelInfo(info ChannelInfo, signingBlock []byte) ([]byte, int, error) {
signingBlockSize := getUint64(signingBlock, 0)
signingBlockLen := len(signingBlock)
if n := uint64(signingBlockLen - 8); signingBlockSize != n {
return nil, 0, fmt.Errorf("APK Signing Block is illegal! Expect size %d but %d", signingBlockSize, n)
}
channelValue := info.Bytes()
channelValueSize := uint64(4 + len(channelValue))
resultSize := 8 + signingBlockSize + 8 + channelValueSize
newBlock := make([]byte, resultSize)
position := 0
putUint64(resultSize-8, newBlock, position)
position += 8
// copy raw id-value pairs
n, _ := copyBytes(signingBlock, 8, newBlock, position, int(signingBlockSize)-16-8)
position += n
putUint64(channelValueSize, newBlock, position)
position += 8
putUint32(apkChannelBlockID, newBlock, position)
position += 4
n, _ = copyBytes(channelValue, 0, newBlock, position, len(channelValue))
position += n
putUint64(resultSize-8, newBlock, position)
position += 8
copyBytes(signingBlock, signingBlockLen-16, newBlock, int(resultSize-16), 16)
position += 16
if position != int(resultSize) {
return nil, -1, fmt.Errorf("count mismatched ! %d vs %d", position, resultSize)
}
return newBlock, int(resultSize) - signingBlockLen, nil
}
func makeEocd(origin []byte, newCentralDirOffset uint32) []byte {
eocd := make([]byte, len(origin))
copy(eocd, origin)
setEocdCentralDirectoryOffset(eocd, newCentralDirOffset)
return eocd
}

View File

@@ -0,0 +1,78 @@
package tools
import (
"fmt"
"os"
"path/filepath"
)
//LittleEndian
func getUint16(b []byte, offset int) uint16 {
_ = b[offset+1] // early bounds check
return uint16(b[offset+0]) |
uint16(b[offset+1])<<8
}
//LittleEndian
func getUint32(b []byte, offset int) uint32 {
_ = b[offset+3] // early bounds check
return uint32(b[offset+0]) |
uint32(b[offset+1])<<8 |
uint32(b[offset+2])<<16 |
uint32(b[offset+3])<<24
}
//LittleEndian
func getUint64(b []byte, offset int) uint64 {
_ = b[offset+7] // bounds check hint to compiler; see golang.org/issue/14808
return uint64(b[offset+0]) |
uint64(b[offset+1])<<8 |
uint64(b[offset+2])<<16 |
uint64(b[offset+3])<<24 |
uint64(b[offset+4])<<32 |
uint64(b[offset+5])<<40 |
uint64(b[offset+6])<<48 |
uint64(b[offset+7])<<56
}
//LittleEndian
func putUint64(v uint64, b []byte, offset int) {
_ = b[offset+7] // early bounds check to guarantee safety of writes below
b[offset+0] = byte(v)
b[offset+1] = byte(v >> 8)
b[offset+2] = byte(v >> 16)
b[offset+3] = byte(v >> 24)
b[offset+4] = byte(v >> 32)
b[offset+5] = byte(v >> 40)
b[offset+6] = byte(v >> 48)
b[offset+7] = byte(v >> 56)
}
//LittleEndian
func putUint32(v uint32, b []byte, offset int) {
_ = b[offset+3]
b[offset+0] = byte(v)
b[offset+1] = byte(v >> 8)
b[offset+2] = byte(v >> 16)
b[offset+3] = byte(v >> 24)
}
func copyBytes(src []byte, srcStart int, dst []byte, dstStart int, count int) (int, error) {
if len(src) < srcStart+count || len(dst) < dstStart+count {
return -1, fmt.Errorf("Array index out of bounds")
}
for i := 0; i < count; i++ {
dst[dstStart+i] = src[srcStart+i]
}
return count, nil
}
func fileNameAndExt(path string) (string, string) {
name := filepath.Base(path)
for i := len(name) - 1; i >= 0 && !os.IsPathSeparator(name[i]); i-- {
if name[i] == '.' {
return name[:i], name[i:]
}
}
return name, ""
}

View File

@@ -0,0 +1,60 @@
package tools
import (
"archive/zip"
"go-common/library/log"
"io"
"os"
"path/filepath"
"strings"
)
// Unzip unzip a file to the target directory.
func Unzip(filePath string, targetDir string) (err error) {
reader, err := zip.OpenReader(filePath)
if err != nil {
log.Error("ozip.OpenReader(%s) error(%v)", filePath, err)
return
}
defer reader.Close()
err = os.MkdirAll(targetDir, 0755)
if err != nil {
log.Error("os.MkdirAll(%s) error(%v)", targetDir, err)
return
}
for _, file := range reader.File {
path := filepath.Join(targetDir, file.Name)
if strings.Contains(path, "__MACOSX") {
continue
}
if file.FileInfo().IsDir() {
os.MkdirAll(path, file.Mode())
continue
} else {
os.MkdirAll(filepath.Dir(path), 0755)
}
fileReader, err := file.Open()
if err != nil {
log.Error("file.Open() error(%v)", err)
return err
}
defer fileReader.Close()
targetFile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, file.Mode())
if err != nil {
log.Error("os.OpenFile() error(%v)", err)
return err
}
defer targetFile.Close()
if _, err := io.Copy(targetFile, fileReader); err != nil {
log.Error("io.Copy() error(%v)", err)
return err
}
}
return
}

View File

@@ -0,0 +1,225 @@
package tools
import (
"fmt"
"go-common/library/log"
"os"
"path/filepath"
"time"
)
type zipSections struct {
beforeSigningBlock []byte
signingBlock []byte
signingBlockOffset int64
centraDir []byte
centralDirOffset int64
eocd []byte
eocdOffset int64
}
type transform func(*zipSections) (*zipSections, error)
func (z *zipSections) writeTo(output string, transform transform) (err error) {
f, err := os.Create(output)
if err != nil {
return
}
defer f.Close()
newZip, err := transform(z)
if err != nil {
return
}
for _, s := range [][]byte{
newZip.beforeSigningBlock,
newZip.signingBlock,
newZip.centraDir,
newZip.eocd} {
_, err := f.Write(s)
if err != nil {
return err
}
}
return
}
var _debug bool
// GenerateChannelApk generate a specified channel apk
func GenerateChannelApk(out string, channel string, extras map[string]string, input string, force bool, debug bool) (output string, err error) {
_debug = debug
if len(input) == 0 {
err = fmt.Errorf("Error: no input file specified")
log.Error("no input file specified, error(%v)", err)
return
}
if _, err = os.Stat(input); os.IsNotExist(err) {
return
}
if len(out) == 0 {
out = filepath.Dir(input)
} else {
var fi os.FileInfo
err = os.MkdirAll(out, 0755)
if err != nil {
log.Error("os.MkdirAll(%s) error(%v)", out, err)
return
}
fi, err = os.Stat(out)
println("error %v", err)
if os.IsNotExist(err) || !fi.IsDir() {
err = fmt.Errorf("Error: output %s is neither exist nor a dir", out)
log.Error("output %s is neither exist nor a dir, error(%v)", out, err)
return
}
}
if channel == "" {
err = fmt.Errorf("Error: no channel specified")
log.Error("no channel specified, error(%v)", err)
return
}
//TODO: add new option for generating new channel from channelled apk
if c, _ := readChannelInfo(input); len(c.Channel) != 0 {
err = fmt.Errorf("Error: file %s is registered a channel block %s", filepath.Base(input), c.String())
log.Error("file %s is registered a channel block %s, error(%v)", filepath.Base(input), c.String(), err)
return
}
var start time.Time
if _debug {
start = time.Now()
}
z, err := newZipSections(input)
if err != nil {
err = fmt.Errorf("Error occurred on parsing apk %s, %s", input, err)
log.Error("Error occurred on parsing apk %s, error(%v)", input, err)
return
}
name, ext := fileNameAndExt(input)
output = filepath.Join(out, name+"-"+channel+ext)
c := ChannelInfo{Channel: channel, Extras: extras}
err = gen(c, z, output, force)
if err != nil {
err = fmt.Errorf("Error occurred on generating channel %s, %s", channel, err)
log.Error("Error occurred on generating channel %s, error(%v)", input, err)
return
}
if _debug {
println("Consume", time.Since(start).String())
} else {
println("Done!")
}
return
}
func newZipSections(input string) (z zipSections, err error) {
in, err := os.Open(input)
if err != nil {
return
}
defer in.Close()
// read eocd
eocd, eocdOffset, err := findEndOfCentralDirectoryRecord(in)
if err != nil {
return
}
centralDirOffset := getEocdCentralDirectoryOffset(eocd)
centralDirSize := getEocdCentralDirectorySize(eocd)
z.eocd = eocd
z.eocdOffset = eocdOffset
z.centralDirOffset = int64(centralDirOffset)
// read signing block
signingBlock, signingBlockOffset, err := findApkSigningBlock(in, centralDirOffset)
if err != nil {
return
}
z.signingBlock = signingBlock
z.signingBlockOffset = signingBlockOffset
// read bytes before signing block
//TODO: waste too large memory
if signingBlockOffset >= 64*1024*1024 {
fmt.Print("Warning: maybe waste large memory on processing this apk! ")
fmt.Println("Before APK Signing Block bytes size is", signingBlockOffset/1024/1024, "MB")
}
beforeSigningBlock := make([]byte, signingBlockOffset)
n, err := in.ReadAt(beforeSigningBlock, 0)
if err != nil {
return
}
if int64(n) != signingBlockOffset {
return z, fmt.Errorf("Read bytes count mismatched! Expect %d, but %d", signingBlockOffset, n)
}
z.beforeSigningBlock = beforeSigningBlock
centralDir := make([]byte, centralDirSize)
n, err = in.ReadAt(centralDir, int64(centralDirOffset))
if uint32(n) != centralDirSize {
return z, fmt.Errorf("Read bytes count mismatched! Expect %d, but %d", centralDirSize, n)
}
z.centraDir = centralDir
if _debug {
fmt.Printf("signingBlockOffset=%d, signingBlockLenth=%d\n"+
"centralDirOffset=%d, centralDirSize=%d\n"+
"eocdOffset=%d, eocdLenthe=%d\n",
signingBlockOffset,
len(signingBlock),
centralDirOffset,
centralDirSize,
eocdOffset,
len(eocd))
}
return
}
func gen(info ChannelInfo, sections zipSections, output string, force bool) (err error) {
fi, err := os.Stat(output)
if err != nil && !os.IsNotExist(err) {
return err
}
if fi != nil {
if !force {
return fmt.Errorf("file already exists %s", output)
}
println("Force generating channel", info.Channel)
}
var s time.Time
if _debug {
s = time.Now()
}
err = sections.writeTo(output, newTransform(info))
if _debug {
fmt.Printf(" write %s consume %s", output, time.Since(s).String())
fmt.Println()
}
return
}
func newTransform(info ChannelInfo) transform {
return func(zip *zipSections) (*zipSections, error) {
newBlock, diffSize, err := makeSigningBlockWithChannelInfo(info, zip.signingBlock)
if err != nil {
return nil, err
}
newzip := new(zipSections)
newzip.beforeSigningBlock = zip.beforeSigningBlock
newzip.signingBlock = newBlock
newzip.signingBlockOffset = zip.signingBlockOffset
newzip.centraDir = zip.centraDir
newzip.centralDirOffset = zip.centralDirOffset
newzip.eocdOffset = zip.eocdOffset
newzip.eocd = makeEocd(zip.eocd, uint32(int64(diffSize)+zip.centralDirOffset))
return newzip, nil
}
}