mirror of https://gitee.com/answerdev/answer.git
feat(plugin): github connector
This commit is contained in:
parent
0628e52da0
commit
154fa84bbb
|
@ -210,7 +210,9 @@ func initApplication(debug bool, serverConf *conf.Server, dbConf *data.Database,
|
|||
templateRenderController := templaterender.NewTemplateRenderController(questionService, userService, tagService, answerService, commentService, dataData, siteInfoCommonService)
|
||||
templateController := controller.NewTemplateController(templateRenderController, siteInfoCommonService)
|
||||
templateRouter := router.NewTemplateRouter(templateController, templateRenderController, siteInfoController)
|
||||
ginEngine := server.NewHTTPServer(debug, staticRouter, answerAPIRouter, swaggerRouter, uiRouter, authUserMiddleware, avatarMiddleware, templateRouter)
|
||||
connectorController := controller.NewConnectorController(siteInfoCommonService)
|
||||
pluginAPIRouter := router.NewPluginAPIRouter(connectorController)
|
||||
ginEngine := server.NewHTTPServer(debug, staticRouter, answerAPIRouter, swaggerRouter, uiRouter, authUserMiddleware, avatarMiddleware, templateRouter, pluginAPIRouter)
|
||||
scheduledTaskManager := cron.NewScheduledTaskManager(siteInfoCommonService, questionService)
|
||||
application := newApplication(serverConf, ginEngine, scheduledTaskManager)
|
||||
return application, func() {
|
||||
|
|
7
go.mod
7
go.mod
|
@ -12,10 +12,11 @@ require (
|
|||
github.com/go-playground/locales v0.14.0
|
||||
github.com/go-playground/universal-translator v0.18.0
|
||||
github.com/go-playground/validator/v10 v10.11.1
|
||||
github.com/go-resty/resty/v2 v2.7.0
|
||||
github.com/go-sql-driver/mysql v1.6.0
|
||||
github.com/goccy/go-json v0.9.11
|
||||
github.com/golang/mock v1.4.4
|
||||
github.com/gomarkdown/markdown v0.0.0-20221013030248-663e2500819c
|
||||
github.com/google/go-github/v48 v48.2.0
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/google/wire v0.5.0
|
||||
github.com/gosimple/slug v1.13.1
|
||||
|
@ -41,6 +42,7 @@ require (
|
|||
github.com/yuin/goldmark v1.4.13
|
||||
golang.org/x/crypto v0.1.0
|
||||
golang.org/x/net v0.1.0
|
||||
golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5
|
||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
xorm.io/builder v0.3.12
|
||||
|
@ -69,7 +71,9 @@ require (
|
|||
github.com/go-openapi/swag v0.22.3 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||
github.com/gosimple/unidecode v1.0.1 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
|
@ -114,6 +118,7 @@ require (
|
|||
golang.org/x/sys v0.1.0 // indirect
|
||||
golang.org/x/text v0.5.0 // indirect
|
||||
golang.org/x/tools v0.2.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/protobuf v1.28.1 // indirect
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
|
|
18
go.sum
18
go.sum
|
@ -202,6 +202,8 @@ github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn
|
|||
github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=
|
||||
github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ=
|
||||
github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU=
|
||||
github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY=
|
||||
github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I=
|
||||
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
|
||||
|
@ -252,12 +254,12 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD
|
|||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/gomarkdown/markdown v0.0.0-20221013030248-663e2500819c h1:iyaGYbCmcYK0Ja9a3OUa2Fo+EaN0cbLu0eKpBwPFzc8=
|
||||
github.com/gomarkdown/markdown v0.0.0-20221013030248-663e2500819c/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
|
@ -271,7 +273,11 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
|||
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-github/v48 v48.2.0 h1:68puzySE6WqUY9KWmpOsDEQfDZsso98rT6pZcz9HqcE=
|
||||
github.com/google/go-github/v48 v48.2.0/go.mod h1:dDlehKBDo850ZPvCTK0sEqTCVWcrGl2LcDiajkYi89Y=
|
||||
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
||||
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
|
@ -830,7 +836,9 @@ golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v
|
|||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
|
||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0=
|
||||
|
@ -844,6 +852,8 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ
|
|||
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 h1:OSnWWcOd/CtWQC2cYSBgbTSJv3ciqd8r54ySIW2y3RE=
|
||||
golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
|
@ -1043,6 +1053,7 @@ google.golang.org/appengine v1.6.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
|
|||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
|
||||
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
|
@ -1113,6 +1124,7 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD
|
|||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
|
||||
|
|
|
@ -20,6 +20,7 @@ func NewHTTPServer(debug bool,
|
|||
authUserMiddleware *middleware.AuthUserMiddleware,
|
||||
avatarMiddleware *middleware.AvatarMiddleware,
|
||||
templateRouter *router.TemplateRouter,
|
||||
pluginAPIRouter *router.PluginAPIRouter,
|
||||
) *gin.Engine {
|
||||
|
||||
if debug {
|
||||
|
@ -62,5 +63,8 @@ func NewHTTPServer(debug bool,
|
|||
answerRouter.RegisterAnswerAdminAPIRouter(adminauthV1)
|
||||
|
||||
templateRouter.RegisterTemplateRouter(rootGroup)
|
||||
|
||||
// plugin routes
|
||||
pluginAPIRouter.RegisterConnector(r)
|
||||
return r
|
||||
}
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
package controller
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/answerdev/answer/internal/base/handler"
|
||||
"github.com/answerdev/answer/internal/plugin"
|
||||
_ "github.com/answerdev/answer/internal/plugin/connector"
|
||||
"github.com/answerdev/answer/internal/schema"
|
||||
"github.com/answerdev/answer/internal/service/siteinfo_common"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
const (
|
||||
oauthRedirectRouterPrefix = "/answer/api/v1/oauth/redirect/"
|
||||
oauthLoginRouterPrefix = "/answer/api/v1/oauth/login/"
|
||||
)
|
||||
|
||||
// ConnectorController comment controller
|
||||
type ConnectorController struct {
|
||||
siteInfoService *siteinfo_common.SiteInfoCommonService
|
||||
}
|
||||
|
||||
// NewConnectorController new controller
|
||||
func NewConnectorController(
|
||||
siteInfoService *siteinfo_common.SiteInfoCommonService,
|
||||
) *ConnectorController {
|
||||
return &ConnectorController{siteInfoService: siteInfoService}
|
||||
}
|
||||
|
||||
func (cc *ConnectorController) ConnectorRedirectRegisterRouters(r *gin.Engine) {
|
||||
_ = plugin.CallConnector(func(fn plugin.Connector) error {
|
||||
// user login url
|
||||
r.GET(oauthLoginRouterPrefix+fn.ConnectorSlugName(), fn.ConnectorSender)
|
||||
// oauth redirect url
|
||||
r.GET(oauthRedirectRouterPrefix+fn.ConnectorSlugName(), fn.ConnectorReceiver)
|
||||
return nil
|
||||
})
|
||||
r.GET("/answer/api/v1/oauth/info", cc.ConnectorsInfo)
|
||||
}
|
||||
|
||||
func (cc *ConnectorController) ConnectorsInfo(ctx *gin.Context) {
|
||||
general, err := cc.siteInfoService.GetSiteGeneral(ctx)
|
||||
if err != nil {
|
||||
handler.HandleResponse(ctx, err, nil)
|
||||
return
|
||||
}
|
||||
|
||||
resp := make([]*schema.ConnectorInfoResp, 0)
|
||||
err = plugin.CallConnector(func(fn plugin.Connector) error {
|
||||
resp = append(resp, &schema.ConnectorInfoResp{
|
||||
Name: fn.ConnectorSlugName(),
|
||||
Icon: fn.ConnectorLogo(),
|
||||
Link: fmt.Sprintf("%s%s%s", general.SiteUrl, oauthLoginRouterPrefix, fn.ConnectorSlugName()),
|
||||
})
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
handler.HandleResponse(ctx, err, nil)
|
||||
return
|
||||
}
|
||||
handler.HandleResponse(ctx, nil, resp)
|
||||
}
|
|
@ -24,4 +24,5 @@ var ProviderSetController = wire.NewSet(
|
|||
NewUploadController,
|
||||
NewActivityController,
|
||||
NewTemplateController,
|
||||
NewConnectorController,
|
||||
)
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
package plugin
|
||||
|
||||
// Info presents the plugin information
|
||||
type Info struct {
|
||||
Name string
|
||||
Description string
|
||||
Author string
|
||||
Version string
|
||||
Disabled bool
|
||||
}
|
||||
|
||||
// Base is the base plugin
|
||||
type Base interface {
|
||||
// Info returns the plugin information
|
||||
Info() Info
|
||||
}
|
||||
|
||||
var (
|
||||
// CallBase is a function that calls all registered base plugins
|
||||
CallBase,
|
||||
registerBase = MakePlugin[Base]()
|
||||
)
|
|
@ -0,0 +1,54 @@
|
|||
package plugin
|
||||
|
||||
import "time"
|
||||
|
||||
type Connector interface {
|
||||
Base
|
||||
|
||||
// ConnectorLogo presents the logo binary data of the connector
|
||||
ConnectorLogo() []byte
|
||||
|
||||
// ConnectorLogoContentType presents the content type of the logo
|
||||
// e.g. image/png, image/jpeg, image/gif
|
||||
ConnectorLogoContentType() string
|
||||
|
||||
// ConnectorName presents the name of the connector
|
||||
// e.g. Facebook, Twitter, Instagram
|
||||
ConnectorName() string
|
||||
|
||||
// ConnectorSlugName presents the slug name of the connector
|
||||
// Please use lowercase and hyphen as the separator
|
||||
// e.g. facebook, twitter, instagram
|
||||
ConnectorSlugName() string
|
||||
|
||||
// ConnectorSender presents the sender of the connector
|
||||
// It handles the start endpoint of the connector
|
||||
ConnectorSender(ctx *GinContext)
|
||||
ConnectorReceiver(ctx *GinContext)
|
||||
|
||||
//ConnectorLoginURL() (loginURL string)
|
||||
//ConnectorLoginUserInfo(code string) (userInfo *UserExternalLogin)
|
||||
}
|
||||
|
||||
type UserExternalLogin struct {
|
||||
Provider string
|
||||
ExternalID string
|
||||
Email string
|
||||
Name string
|
||||
FirstName string
|
||||
LastName string
|
||||
NickName string
|
||||
Description string
|
||||
AvatarUrl string
|
||||
Location string
|
||||
AccessToken string
|
||||
AccessTokenSecret string
|
||||
RefreshToken string
|
||||
ExpiresAt time.Time
|
||||
}
|
||||
|
||||
var (
|
||||
// CallConnector is a function that calls all registered connectors
|
||||
CallConnector,
|
||||
registerConnector = MakePlugin[Connector]()
|
||||
)
|
|
@ -0,0 +1,96 @@
|
|||
package connector
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/answerdev/answer/internal/plugin"
|
||||
"github.com/go-resty/resty/v2"
|
||||
"github.com/google/go-github/v48/github"
|
||||
"golang.org/x/oauth2"
|
||||
oauth2GitHub "golang.org/x/oauth2/github"
|
||||
)
|
||||
|
||||
type GitHub struct {
|
||||
ClientID string
|
||||
ClientSecret string
|
||||
}
|
||||
|
||||
func init() {
|
||||
plugin.Register(&GitHub{
|
||||
ClientID: os.Getenv("GITHUB_CLIENT_ID"),
|
||||
ClientSecret: os.Getenv("GITHUB_CLIENT_SECRET"),
|
||||
})
|
||||
}
|
||||
|
||||
func (g *GitHub) Info() plugin.Info {
|
||||
return plugin.Info{
|
||||
Name: "github connector",
|
||||
Description: "github connector plugin",
|
||||
Version: "0.0.1",
|
||||
}
|
||||
}
|
||||
|
||||
func (g *GitHub) ConnectorLogo() []byte {
|
||||
response, err := resty.New().R().Get("https://cdn-icons-png.flaticon.com/32/25/25231.png")
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return response.Body()
|
||||
}
|
||||
|
||||
func (g *GitHub) ConnectorLogoContentType() string {
|
||||
return "image/png"
|
||||
}
|
||||
|
||||
func (g *GitHub) ConnectorName() string {
|
||||
return "GitHubConnector"
|
||||
}
|
||||
|
||||
func (g *GitHub) ConnectorSlugName() string {
|
||||
return "github"
|
||||
}
|
||||
|
||||
func (g *GitHub) ConnectorSender(ctx *plugin.GinContext) {
|
||||
oauth2Config := &oauth2.Config{
|
||||
ClientID: g.ClientID,
|
||||
ClientSecret: g.ClientSecret,
|
||||
Endpoint: oauth2GitHub.Endpoint,
|
||||
RedirectURL: "http://127.0.0.1:8080/answer/api/v1/oauth/redirect/github",
|
||||
Scopes: nil,
|
||||
}
|
||||
ctx.Redirect(http.StatusFound, oauth2Config.AuthCodeURL(""))
|
||||
}
|
||||
|
||||
func (g *GitHub) ConnectorReceiver(ctx *plugin.GinContext) {
|
||||
code := ctx.Query("code")
|
||||
//state := ctx.Query("state")
|
||||
|
||||
oauth2Config := &oauth2.Config{
|
||||
ClientID: g.ClientID,
|
||||
ClientSecret: g.ClientSecret,
|
||||
Endpoint: oauth2GitHub.Endpoint,
|
||||
}
|
||||
token, err := oauth2Config.Exchange(context.Background(), code+"1")
|
||||
if err != nil {
|
||||
ctx.Error(err)
|
||||
ctx.Redirect(http.StatusFound, "/50x")
|
||||
return
|
||||
}
|
||||
|
||||
cli := github.NewClient(oauth2.NewClient(context.Background(), oauth2.StaticTokenSource(
|
||||
&oauth2.Token{AccessToken: token.AccessToken},
|
||||
)))
|
||||
userInfo, _, err := cli.Users.Get(context.Background(), "")
|
||||
if err != nil {
|
||||
ctx.Error(err)
|
||||
ctx.Redirect(http.StatusFound, "/50x")
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("user info is :%+v", userInfo)
|
||||
ctx.Redirect(http.StatusFound, "/")
|
||||
return
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package connector
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/answerdev/answer/internal/plugin"
|
||||
)
|
||||
|
||||
type Google struct {
|
||||
}
|
||||
|
||||
func (g *Google) Info() plugin.Info {
|
||||
return plugin.Info{
|
||||
Name: "google connector",
|
||||
Description: "google connector plugin",
|
||||
Version: "0.0.1",
|
||||
}
|
||||
}
|
||||
|
||||
func (g *Google) ConnectorLogo() []byte {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *Google) ConnectorLogoContentType() string {
|
||||
return "image/png"
|
||||
}
|
||||
|
||||
func (g *Google) ConnectorName() string {
|
||||
return "google"
|
||||
}
|
||||
|
||||
func (g *Google) ConnectorSlugName() string {
|
||||
return "google"
|
||||
}
|
||||
|
||||
func (g *Google) ConnectorSender(ctx *plugin.GinContext) {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (g *Google) ConnectorReceiver(ctx *plugin.GinContext) {
|
||||
ctx.String(http.StatusOK, "OK123")
|
||||
}
|
||||
|
||||
func init() {
|
||||
plugin.Register(&Google{})
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package plugin
|
||||
|
||||
type Filter interface {
|
||||
Base
|
||||
FilterText(text string) (err error)
|
||||
}
|
||||
|
||||
var (
|
||||
// CallFilter is a function that calls all registered parsers
|
||||
CallFilter,
|
||||
registerFilter = MakePlugin[Filter]()
|
||||
)
|
|
@ -0,0 +1,12 @@
|
|||
package plugin
|
||||
|
||||
type Parser interface {
|
||||
Base
|
||||
Parse(text string) (string, error)
|
||||
}
|
||||
|
||||
var (
|
||||
// CallParser is a function that calls all registered parsers
|
||||
CallParser,
|
||||
registerParser = MakePlugin[Parser]()
|
||||
)
|
|
@ -0,0 +1,69 @@
|
|||
package plugin
|
||||
|
||||
import "github.com/gin-gonic/gin"
|
||||
|
||||
// GinContext is a wrapper of gin.Context
|
||||
// We export it to make it easy to use in plugins
|
||||
type GinContext = gin.Context
|
||||
|
||||
// Register registers a plugin
|
||||
func Register(p Base) {
|
||||
registerBase(p)
|
||||
|
||||
switch pType := p.(type) {
|
||||
case Connector:
|
||||
registerConnector(pType)
|
||||
case Parser:
|
||||
registerParser(pType)
|
||||
case Filter:
|
||||
registerFilter(pType)
|
||||
}
|
||||
}
|
||||
|
||||
// Dump returns all registered plugins infos
|
||||
func Dump() []Info {
|
||||
var infos []Info
|
||||
|
||||
CallBase(func(p Base) error {
|
||||
infos = append(infos, p.Info())
|
||||
return nil
|
||||
})
|
||||
|
||||
return infos
|
||||
}
|
||||
|
||||
type Stack[T Base] struct {
|
||||
plugins []T
|
||||
}
|
||||
|
||||
type RegisterFn[T Base] func(p T)
|
||||
type Caller[T Base] func(p T) error
|
||||
type CallFn[T Base] func(fn Caller[T]) error
|
||||
|
||||
// MakePlugin creates a plugin caller and register stack manager
|
||||
// It returns a register function and a caller function
|
||||
// The register function is used to register a plugin, it will be called in the plugin's init function
|
||||
// The caller function is used to call all registered plugins
|
||||
func MakePlugin[T Base]() (CallFn[T], RegisterFn[T]) {
|
||||
stack := Stack[T]{}
|
||||
|
||||
call := func(fn Caller[T]) error {
|
||||
for _, p := range stack.plugins {
|
||||
// If the plugin is disabled, skip it
|
||||
if p.Info().Disabled {
|
||||
continue
|
||||
}
|
||||
|
||||
if err := fn(p); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
register := func(p T) {
|
||||
stack.plugins = append(stack.plugins, p)
|
||||
}
|
||||
|
||||
return call, register
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package sample
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/answerdev/answer/internal/plugin"
|
||||
)
|
||||
|
||||
type FilterSample struct {
|
||||
}
|
||||
|
||||
func (s *FilterSample) Info() plugin.Info {
|
||||
return plugin.Info{
|
||||
Name: "filter sample",
|
||||
Description: "filter sample plugin",
|
||||
Version: "0.0.1",
|
||||
}
|
||||
}
|
||||
|
||||
func (s *FilterSample) FilterText(data string) (err error) {
|
||||
if strings.Contains(data, "violent") {
|
||||
return fmt.Errorf("bloody and violent words cannot appear in this website")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
plugin.Register(&FilterSample{})
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package sample
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/answerdev/answer/internal/plugin"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestFilterSample_FilterText(t *testing.T) {
|
||||
// try to call filter plugin for filter text that are not allowed
|
||||
err := plugin.CallFilter(func(fn plugin.Filter) error {
|
||||
return fn.FilterText("bloody and violent words")
|
||||
})
|
||||
assert.Error(t, err)
|
||||
fmt.Println(err)
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package sample
|
||||
|
||||
import "github.com/answerdev/answer/internal/plugin"
|
||||
|
||||
type Sample struct {
|
||||
}
|
||||
|
||||
func (s *Sample) Info() plugin.Info {
|
||||
return plugin.Info{
|
||||
Name: "sample",
|
||||
Description: "sample plugin",
|
||||
Version: "0.0.1",
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
plugin.Register(&Sample{})
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package router
|
||||
|
||||
import (
|
||||
"github.com/answerdev/answer/internal/controller"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type PluginAPIRouter struct {
|
||||
connectorController *controller.ConnectorController
|
||||
}
|
||||
|
||||
func NewPluginAPIRouter(
|
||||
connectorController *controller.ConnectorController,
|
||||
) *PluginAPIRouter {
|
||||
return &PluginAPIRouter{
|
||||
connectorController: connectorController,
|
||||
}
|
||||
}
|
||||
|
||||
func (pr *PluginAPIRouter) RegisterConnector(r *gin.Engine) {
|
||||
pr.connectorController.ConnectorRedirectRegisterRouters(r)
|
||||
}
|
|
@ -3,4 +3,11 @@ package router
|
|||
import "github.com/google/wire"
|
||||
|
||||
// ProviderSetRouter is providers.
|
||||
var ProviderSetRouter = wire.NewSet(NewAnswerAPIRouter, NewSwaggerRouter, NewStaticRouter, NewUIRouter, NewTemplateRouter)
|
||||
var ProviderSetRouter = wire.NewSet(
|
||||
NewAnswerAPIRouter,
|
||||
NewSwaggerRouter,
|
||||
NewStaticRouter,
|
||||
NewUIRouter,
|
||||
NewTemplateRouter,
|
||||
NewPluginAPIRouter,
|
||||
)
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
package schema
|
||||
|
||||
type ConnectorInfoResp struct {
|
||||
Name string `json:"name"`
|
||||
Icon []byte `json:"icon"`
|
||||
Link string `json:"link"`
|
||||
}
|
Loading…
Reference in New Issue