diff --git a/app/zeus/api/lark/deploy/templates/deployment.yaml b/app/zeus/api/lark/deploy/templates/deployment.yaml index 6f357bb..e2c7b48 100755 --- a/app/zeus/api/lark/deploy/templates/deployment.yaml +++ b/app/zeus/api/lark/deploy/templates/deployment.yaml @@ -44,8 +44,14 @@ spec: - containerPort: {{ .Values.port }} protocol: TCP env: - - name: endpoints + - name: ENDPOINTS value: 'etcd:2379' + - name: APP_NAME + value: {{ .Values.appName }} + - name: NAMESPACE + value: {{ .Values.nameSpace }} + - name: APP_TYPE + value: api resources: limits: cpu: 200m diff --git a/app/zeus/api/lark/deploy/values.yaml b/app/zeus/api/lark/deploy/values.yaml index 3d581af..58ec2a1 100755 --- a/app/zeus/api/lark/deploy/values.yaml +++ b/app/zeus/api/lark/deploy/values.yaml @@ -1,4 +1,5 @@ nameSpace: zeus +appName: lark aliasName: 飞书 image: reg.icechen.cn/zeus/api-lark imageTag: latest diff --git a/app/zeus/service/lark/Dockerfile b/app/zeus/service/lark/Dockerfile new file mode 100755 index 0000000..22c796f --- /dev/null +++ b/app/zeus/service/lark/Dockerfile @@ -0,0 +1,20 @@ +FROM golang:1.17 as builder +ENV GO111MODULE on +ENV GOPROXY https://goproxy.io,direct +WORKDIR /go/cache +ADD go.mod . +ADD go.sum . +RUN go mod download -x +WORKDIR /go/src +ADD . . +RUN go mod tidy +RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o service_lark ./app/zeus/service/lark + +FROM reg.icechen.cn/alpine as lark +WORKDIR /go/src +COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo +COPY --from=builder /go/src/service_lark ./ +ENV TZ=Asia/Shanghai +RUN chmod +x ./service_lark +EXPOSE 8080 +CMD ["./service_lark"] \ No newline at end of file diff --git a/app/zeus/service/lark/deploy/.helmignore b/app/zeus/service/lark/deploy/.helmignore new file mode 100755 index 0000000..0e8a0eb --- /dev/null +++ b/app/zeus/service/lark/deploy/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/app/zeus/service/lark/deploy/Chart.yaml b/app/zeus/service/lark/deploy/Chart.yaml new file mode 100755 index 0000000..30cbf6f --- /dev/null +++ b/app/zeus/service/lark/deploy/Chart.yaml @@ -0,0 +1,24 @@ +apiVersion: v2 +name: lark +description: 飞书 + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.0.1 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +appVersion: "0.0.1" diff --git a/app/zeus/service/lark/deploy/templates/NOTES.txt b/app/zeus/service/lark/deploy/templates/NOTES.txt new file mode 100755 index 0000000..3041ade --- /dev/null +++ b/app/zeus/service/lark/deploy/templates/NOTES.txt @@ -0,0 +1 @@ +notes \ No newline at end of file diff --git a/pkg/orm/.keep b/app/zeus/service/lark/deploy/templates/_helpers.tpl old mode 100644 new mode 100755 similarity index 100% rename from pkg/orm/.keep rename to app/zeus/service/lark/deploy/templates/_helpers.tpl diff --git a/app/zeus/service/lark/deploy/templates/application.yaml b/app/zeus/service/lark/deploy/templates/application.yaml new file mode 100755 index 0000000..ec8cc30 --- /dev/null +++ b/app/zeus/service/lark/deploy/templates/application.yaml @@ -0,0 +1,20 @@ +apiVersion: app.k8s.io/v1beta1 +kind: Application +metadata: + name: {{ .Release.Name }} + namespace: {{ .Values.nameSpace }} + labels: + app: {{ .Release.Name }} + app.kubernetes.io/version: v1 + app.kubernetes.io/name: {{ .Release.Name }} + version: v1 + annotations: + servicemesh.kubesphere.io/enabled: 'true' +spec: + selector: + matchLabels: + app: {{ .Release.Name }} + app.kubernetes.io/version: v1 + app.kubernetes.io/name: {{ .Release.Name }} + version: v1 + addOwnerRef: true \ No newline at end of file diff --git a/app/zeus/service/lark/deploy/templates/deployment.yaml b/app/zeus/service/lark/deploy/templates/deployment.yaml new file mode 100755 index 0000000..a84ea82 --- /dev/null +++ b/app/zeus/service/lark/deploy/templates/deployment.yaml @@ -0,0 +1,82 @@ +kind: Deployment +apiVersion: apps/v1 +metadata: + name: {{ .Release.Name }} + namespace: {{ .Values.nameSpace }} + labels: + app: {{ .Release.Name }} + app.kubernetes.io/version: v1 + app.kubernetes.io/name: {{ .Release.Name }} + version: v1 + app.kubernetes.io/instance: {{ .Release.Name }} + annotations: + kubesphere.io/alias-name: {{ .Values.aliasName }} + kubesphere.io/creator: drone + meta.helm.sh/release-name: {{ .Release.Name }} + meta.helm.sh/release-namespace: {{ .Values.nameSpace }} +spec: + replicas: 1 + selector: + matchLabels: + app: {{ .Release.Name }} + app.kubernetes.io/version: v1 + app.kubernetes.io/name: {{ .Release.Name }} + version: v1 + app.kubernetes.io/instance: {{ .Release.Name }} + template: + metadata: + labels: + app: {{ .Release.Name }} + app.kubernetes.io/version: v1 + app.kubernetes.io/name: {{ .Release.Name }} + version: v1 + app.kubernetes.io/instance: {{ .Release.Name }} + spec: + volumes: + - name: host-time + hostPath: + path: /etc/localtime + type: '' + containers: + - name: {{ .Release.Name }} + image: {{ .Values.image }}:{{ .Values.imageTag }} + ports: + - containerPort: {{ .Values.port }} + protocol: TCP + env: + - name: ENDPOINTS + value: 'etcd:2379' + - name: APP_NAME + value: {{ .Values.appName }} + - name: NAMESPACE + value: {{ .Values.nameSpace }} + - name: APP_TYPE + value: service + resources: + limits: + cpu: 200m + memory: 1000Mi + requests: + cpu: 10m + memory: 200Mi + volumeMounts: + - name: host-time + readOnly: true + mountPath: /etc/localtime + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + imagePullPolicy: IfNotPresent + restartPolicy: Always + terminationGracePeriodSeconds: 30 + dnsPolicy: ClusterFirst + securityContext: {} + imagePullSecrets: + - name: registry-secret + schedulerName: default-scheduler + strategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 25% + maxSurge: 25% + revisionHistoryLimit: 10 + progressDeadlineSeconds: 600 \ No newline at end of file diff --git a/app/zeus/service/lark/deploy/templates/service.yaml b/app/zeus/service/lark/deploy/templates/service.yaml new file mode 100755 index 0000000..4cbc43c --- /dev/null +++ b/app/zeus/service/lark/deploy/templates/service.yaml @@ -0,0 +1,26 @@ +kind: Service +apiVersion: v1 +metadata: + name: {{ .Release.Name }} + namespace: {{ .Values.nameSpace }} + annotations: + kubesphere.io/creator: drone + labels: + app: {{ .Release.Name }} + app.kubernetes.io/version: v1 + app.kubernetes.io/name: {{ .Release.Name }} + version: v1 + app.kubernetes.io/instance: {{ .Release.Name }} +spec: + ports: + - name: http-{{ .Release.Name }} + protocol: TCP + port: 80 + targetPort: {{ .Values.port }} + selector: + app: {{ .Release.Name }} + type: ClusterIP + sessionAffinity: None + ipFamilies: + - IPv4 + ipFamilyPolicy: SingleStack \ No newline at end of file diff --git a/app/zeus/service/lark/deploy/values.yaml b/app/zeus/service/lark/deploy/values.yaml new file mode 100755 index 0000000..582598a --- /dev/null +++ b/app/zeus/service/lark/deploy/values.yaml @@ -0,0 +1,6 @@ +nameSpace: zeus +appName: lark +aliasName: 飞书 +image: reg.icechen.cn/zeus/service-lark +imageTag: latest +port: 3000 \ No newline at end of file diff --git a/app/zeus/service/lark/internal/config/config.go b/app/zeus/service/lark/internal/config/config.go new file mode 100644 index 0000000..75c1b7d --- /dev/null +++ b/app/zeus/service/lark/internal/config/config.go @@ -0,0 +1,17 @@ +package config + +import "git.icechen.cn/monorepo/backend/pkg/config" + +type Cfg struct { + LarkAppID string `json:"lark_app_id"` + LarkAppSecret string `json:"lark_app_secret"` +} + +var Config Cfg + +func init() { + err := config.GetConfig(&Config) + if err != nil { + panic(err) + } +} diff --git a/app/zeus/service/lark/internal/lark/lark.go b/app/zeus/service/lark/internal/lark/lark.go new file mode 100644 index 0000000..55063c1 --- /dev/null +++ b/app/zeus/service/lark/internal/lark/lark.go @@ -0,0 +1,31 @@ +package lark + +import ( + "context" + cfg "git.icechen.cn/monorepo/backend/app/zeus/service/lark/internal/config" + "github.com/larksuite/oapi-sdk-go/core" + "github.com/larksuite/oapi-sdk-go/core/config" + authen "github.com/larksuite/oapi-sdk-go/service/authen/v1" +) + +type Lark struct { + conf *config.Config +} + +func NewLark() Lark { + appSettings := core.NewInternalAppSettings( + core.SetAppCredentials(cfg.Config.LarkAppID, cfg.Config.LarkAppSecret)) + conf := core.NewConfig(core.DomainFeiShu, appSettings, core.SetLoggerLevel(core.LoggerLevelError)) + + return Lark{ + conf: conf, + } +} + +func (l Lark) Auth(code string) (*authen.UserAccessTokenInfo, error) { + s := authen.NewService(l.conf) + return s.Authens.AccessToken(core.WrapContext(context.Background()), &authen.AuthenAccessTokenReqBody{ + GrantType: "", + Code: code, + }).Do() +} diff --git a/app/zeus/service/lark/internal/lark/rpc.go b/app/zeus/service/lark/internal/lark/rpc.go new file mode 100644 index 0000000..0811363 --- /dev/null +++ b/app/zeus/service/lark/internal/lark/rpc.go @@ -0,0 +1,50 @@ +package lark + +import ( + "context" + "fmt" + "google.golang.org/grpc/keepalive" + "log" + "net" + "time" + + "git.icechen.cn/monorepo/backend/pkg/proto/zeus/lark" + "github.com/jinzhu/copier" + "google.golang.org/grpc" + "google.golang.org/grpc/reflection" +) + +func RpcServer() { + lis, err := net.Listen("tcp", ":3000") + if err != nil { + log.Fatalf("failed to listen: %v", err) + } + + s := grpc.NewServer(grpc.KeepaliveParams(keepalive.ServerParameters{ + MaxConnectionIdle: 5 * time.Minute, + })) + lark.RegisterUserServer(s, &Server{}) + + reflection.Register(s) + + fmt.Println("lark server run in :3000") + if err := s.Serve(lis); err != nil { + log.Fatalf("failed to serve: %v", err) + } +} + +type Server struct{} + +func (s Server) Login(ctx context.Context, in *lark.LoginRequest) (*lark.LoginResponse, error) { + userInfo, err := NewLark().Auth(in.Code) + if err != nil { + return nil, err + } + + resp := new(lark.LoginResponse) + err = copier.Copy(resp, userInfo) + if err != nil { + return nil, err + } + return resp, nil +} diff --git a/app/zeus/service/lark/main.go b/app/zeus/service/lark/main.go new file mode 100755 index 0000000..5a8c693 --- /dev/null +++ b/app/zeus/service/lark/main.go @@ -0,0 +1,9 @@ +package main + +import ( + "git.icechen.cn/monorepo/backend/app/zeus/service/lark/internal/lark" +) + +func main() { + lark.RpcServer() +} diff --git a/app/zeus/service/lark/model/init.go b/app/zeus/service/lark/model/init.go new file mode 100644 index 0000000..6cd7452 --- /dev/null +++ b/app/zeus/service/lark/model/init.go @@ -0,0 +1,14 @@ +package model + +import ( + "gorm.io/gorm" +) + +var db *gorm.DB + +func init() { + err := db.AutoMigrate(&UserInfo{}) + if err != nil { + panic(err) + } +} diff --git a/app/zeus/service/lark/model/user_info.go b/app/zeus/service/lark/model/user_info.go new file mode 100644 index 0000000..a2a5e50 --- /dev/null +++ b/app/zeus/service/lark/model/user_info.go @@ -0,0 +1,28 @@ +package model + +import "gorm.io/gorm" + +type UserInfo struct { + gorm.Model + AccessToken string `json:"access_token,omitempty" gorm:"access_token"` + TokenType string `json:"token_type,omitempty" gorm:"token_type"` + ExpiresIn int `json:"expires_in,omitempty" gorm:"expires_in"` + Name string `json:"name,omitempty" gorm:"name"` + EnName string `json:"en_name,omitempty" gorm:"en_name"` + AvatarUrl string `json:"avatar_url,omitempty" gorm:"avatar_url"` + AvatarThumb string `json:"avatar_thumb,omitempty" gorm:"avatar_thumb"` + AvatarMiddle string `json:"avatar_middle,omitempty" gorm:"avatar_middle"` + AvatarBig string `json:"avatar_big,omitempty" gorm:"avatar_big"` + OpenId string `json:"open_id,omitempty" gorm:"open_id"` + UnionId string `json:"union_id,omitempty" gorm:"union_id"` + Email string `json:"email,omitempty" gorm:"email"` + UserId string `json:"user_id,omitempty" gorm:"user_id"` + Mobile string `json:"mobile,omitempty" gorm:"mobile"` + TenantKey string `json:"tenant_key,omitempty" gorm:"tenant_key"` + RefreshExpiresIn int `json:"refresh_expires_in,omitempty" gorm:"refresh_expires_in"` + RefreshToken string `json:"refresh_token,omitempty" gorm:"refresh_token"` +} + +func (UserInfo) TableName() string { + return "user_info" +} diff --git a/pkg/config/config.go b/pkg/config/config.go index d12800e..edb66d5 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -3,48 +3,70 @@ package config import ( "encoding/json" "fmt" + "git.icechen.cn/monorepo/backend/pkg/env" "git.icechen.cn/monorepo/backend/pkg/etcd" "github.com/pkg/errors" + "reflect" ) const ( prefix = "/config" - env = "/env" + envKey = "/env" ) +type Env string + const ( - EnvTest = "test" - EnvOnl = "onl" + EnvTest Env = "test" + EnvOnl Env = "onl" ) // GetConfig 根据etcd获取存入配置 -func GetConfig(name string, config interface{}) error { - key := fmt.Sprintf("%v/config/%v", prefix, name) +func GetConfig(config interface{}) error { + return Bind("config", config) +} + +// GetMysql 获取数据库连接字符串 +func GetMysql() (string, error) { + return GetString("mysql") +} + +// Bind 读取配置项到结构体 +func Bind(item string, out interface{}) error { + if reflect.TypeOf(out).Kind() != reflect.Ptr { + return errors.New("bind对象不是指针类型") + } + key := fmt.Sprintf("%s/%s/%s/%s", prefix, env.Namespace, env.GetAppNameWithType(), item) value, err := etcd.GetValue(key) if err != nil { return err } - err = json.Unmarshal([]byte(value), config) + err = json.Unmarshal([]byte(value), out) if err != nil { return errors.WithMessage(err, "etcd配置解析到json失败") } return nil } -func GetMysql(name string) (string, error) { - key := fmt.Sprintf("%v/mysql/%v", prefix, name) +// GetString 读取配置项字符串 +func GetString(item string) (string, error) { + key := fmt.Sprintf("%s/%s/%s/%s", prefix, env.Namespace, env.GetAppNameWithType(), item) return etcd.GetValue(key) } -func GetEnv() (string, error) { - return etcd.GetValue(env) +// GetEnv 获取环境 +func GetEnv() (Env, error) { + envString, err := etcd.GetValue(envKey) + return Env(envString), err } +// IsTest 是否是测试环境 func IsTest() bool { envString, _ := GetEnv() return envString == EnvTest } +// IsOnl 是否是正式环境 func IsOnl() bool { envString, _ := GetEnv() return envString == EnvOnl diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index c155c4d..9270c0c 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -2,6 +2,8 @@ package config import ( "fmt" + "io/ioutil" + "net/http" "testing" ) @@ -10,24 +12,11 @@ type config struct { Es string `json:"es,omitempty"` } -func TestGetMysql(t *testing.T) { - //获取 /config/mysql/api-zeus 的str - mysql, err := GetMysql("api-zeus") - if err != nil { - fmt.Println(err) - } - fmt.Println(mysql) -} - -func TestConfig(t *testing.T) { - c := new(config) - err := GetConfig("api-zeus", c) - if err != nil { - fmt.Println(err) - } -} - func TestEnv(t *testing.T) { + resp, err := http.Get("http://api-lark-test/user") + b, _ := ioutil.ReadAll(resp.Body) + t.Log(string(b)) + env, err := GetEnv() if err != nil { fmt.Println(err) diff --git a/pkg/env/env.go b/pkg/env/env.go index 8f69176..3c2f9c9 100644 --- a/pkg/env/env.go +++ b/pkg/env/env.go @@ -1,6 +1,26 @@ package env -import "os" +import ( + "fmt" + "os" +) + +func init() { + AppName = GetEnvDefault("APP_NAME", "default") + Namespace = GetEnvDefault("NAMESPACE", "default") + AppType = SAppType(GetEnvDefault("APP_TYPE", string(AppTypeAPI))) +} + +var AppName string +var Namespace string +var AppType SAppType + +type SAppType string + +const ( + AppTypeAPI = SAppType("api") // api应用类型 + AppTypeService = SAppType("service") // service应用类型 +) func GetEnvDefault(key, defVal string) string { val, ex := os.LookupEnv(key) @@ -10,3 +30,8 @@ func GetEnvDefault(key, defVal string) string { } return val } + +// GetAppNameWithType 获取带应用类型的应用名 +func GetAppNameWithType() string { + return fmt.Sprintf("%s-%s", AppType, AppName) +} diff --git a/pkg/etcd/etcd.go b/pkg/etcd/etcd.go index a4c25a7..fb3fffc 100644 --- a/pkg/etcd/etcd.go +++ b/pkg/etcd/etcd.go @@ -11,8 +11,8 @@ import ( ) const ( - EndPoints = "endpoints" - EndAddress = "etcd:2379" + EndPoints = "ENDPOINTS" + EndAddress = "etcd1:2379" ) var ( @@ -40,11 +40,11 @@ func GetValue(key string) (string, error) { } kv := clientV3.NewKV(client) defer client.Close() - ctx, cancel := context.WithTimeout(context.TODO(), time.Second*3) + ctx, cancel := context.WithTimeout(context.TODO(), time.Minute*3) defer cancel() if getResp, err := kv.Get(ctx, key, clientV3.WithPrefix()); err == nil { - for _, v := range getResp.Kvs { - return string(v.Value), nil + if len(getResp.Kvs) > 0 { + return string(getResp.Kvs[0].Value), nil } } else { return "", errors.New("etcd 连接超时") diff --git a/pkg/orm/db.go b/pkg/orm/db.go new file mode 100644 index 0000000..5ece946 --- /dev/null +++ b/pkg/orm/db.go @@ -0,0 +1,22 @@ +package orm + +import ( + "fmt" + "git.icechen.cn/monorepo/backend/pkg/config" + "gorm.io/driver/mysql" + "gorm.io/gorm" +) + +var DB *gorm.DB + +func init() { + dsn, err := config.GetMysql() + if err != nil { + fmt.Println("init db error: ", err) + return + } + DB, err = gorm.Open(mysql.Open(dsn)) + if err != nil { + panic(err) + } +}