|
|
|
|
package service
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"fmt"
|
|
|
|
|
"net/http"
|
|
|
|
|
"strings"
|
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
"moredoc/conf"
|
|
|
|
|
"moredoc/middleware/auth"
|
|
|
|
|
"moredoc/middleware/jsonpb"
|
|
|
|
|
"moredoc/model"
|
|
|
|
|
"moredoc/service/serve"
|
|
|
|
|
|
|
|
|
|
"github.com/gin-contrib/cors"
|
|
|
|
|
"github.com/gin-contrib/gzip"
|
|
|
|
|
"github.com/gin-contrib/static"
|
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
|
grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
|
|
|
|
|
grpc_recovery "github.com/grpc-ecosystem/go-grpc-middleware/recovery"
|
|
|
|
|
"github.com/grpc-ecosystem/grpc-gateway/runtime"
|
|
|
|
|
"go.uber.org/zap"
|
|
|
|
|
"golang.org/x/net/http2"
|
|
|
|
|
"golang.org/x/net/http2/h2c"
|
|
|
|
|
"google.golang.org/grpc"
|
|
|
|
|
_ "google.golang.org/grpc/encoding/gzip" // grpc gzip
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// Run start server
|
|
|
|
|
func Run(cfg *conf.Config, logger *zap.Logger) {
|
|
|
|
|
|
|
|
|
|
size := 100 * 1024 * 1024 // 100MB
|
|
|
|
|
dialOpts := []grpc.DialOption{
|
|
|
|
|
grpc.WithInsecure(),
|
|
|
|
|
grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(size)),
|
|
|
|
|
grpc.WithDefaultCallOptions(grpc.UseCompressor("gzip")),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auth := auth.NewAuth(&cfg.JWT)
|
|
|
|
|
|
|
|
|
|
grpcServer := grpc.NewServer(
|
|
|
|
|
grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer(
|
|
|
|
|
auth.AuthUnaryServerInterceptor(),
|
|
|
|
|
grpc_recovery.UnaryServerInterceptor(),
|
|
|
|
|
)),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
gwmux := runtime.NewServeMux(
|
|
|
|
|
runtime.WithMarshalerOption(
|
|
|
|
|
runtime.MIMEWildcard,
|
|
|
|
|
&jsonpb.JSONPb{OrigName: true, EmitDefaults: true, EnumsAsInts: true},
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
dbModel, err := model.NewDBModel(&cfg.Database, logger)
|
|
|
|
|
if err != nil {
|
|
|
|
|
logger.Fatal("NewDBModel", zap.Error(err))
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 每次启动时,都对dist中的title进行一次处理,以替换掉关键字 moredoc
|
|
|
|
|
go dbModel.InitSEO()
|
|
|
|
|
dbModel.RunTasks()
|
|
|
|
|
|
|
|
|
|
if cfg.Level != "debug" {
|
|
|
|
|
gin.SetMode(gin.ReleaseMode)
|
|
|
|
|
}
|
|
|
|
|
app := gin.New()
|
|
|
|
|
app.Use(
|
|
|
|
|
gzip.Gzip(gzip.BestCompression, gzip.WithExcludedExtensions([]string{".svg", ".png", ".gif", ".jpeg", ".jpg", ".ico"})), // gzip
|
|
|
|
|
gin.Recovery(), // recovery
|
|
|
|
|
// cors.Default(), // allows all origins
|
|
|
|
|
cors.New(cors.Config{
|
|
|
|
|
AllowMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"},
|
|
|
|
|
AllowHeaders: []string{"*"},
|
|
|
|
|
AllowOrigins: []string{"*"}, // Referrer Policy: strict-origin-when-cross-origin
|
|
|
|
|
AllowCredentials: false,
|
|
|
|
|
MaxAge: 12 * time.Hour,
|
|
|
|
|
}),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
endpoint := fmt.Sprintf("localhost:%v", cfg.Port)
|
|
|
|
|
err = serve.RegisterGRPCService(dbModel, logger, endpoint, auth, grpcServer, gwmux, dialOpts...)
|
|
|
|
|
if err != nil {
|
|
|
|
|
logger.Fatal("registerAPIService", zap.Error(err))
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
serve.RegisterGinRouter(app, dbModel, logger, auth)
|
|
|
|
|
|
|
|
|
|
// 根目录访问静态文件,要放在 grpc 服务的前面
|
|
|
|
|
// 可以在 dist 目录下创建一个 index.html 文件并添加内容,然后访问 http://ip:port
|
|
|
|
|
app.Use(static.Serve("/uploads", static.LocalFile("./uploads", true)))
|
|
|
|
|
app.Use(static.Serve("/sitemap", static.LocalFile("./sitemap", true)))
|
|
|
|
|
app.Use(static.Serve("/", static.LocalFile("./dist", true)))
|
|
|
|
|
app.NoRoute(wrapH(grpcHandlerFunc(grpcServer, gwmux))) // grpcServer and grpcGatewayServer
|
|
|
|
|
|
|
|
|
|
addr := fmt.Sprintf(":%v", cfg.Port)
|
|
|
|
|
logger.Info("server start", zap.Int("port", cfg.Port))
|
|
|
|
|
err = app.Run(addr)
|
|
|
|
|
if err != nil {
|
|
|
|
|
logger.Fatal(err.Error())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// See: https://github.com/philips/grpc-gateway-example/issues/22
|
|
|
|
|
func grpcHandlerFunc(grpcServer *grpc.Server, otherHandler http.Handler) http.Handler {
|
|
|
|
|
return h2c.NewHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
// if r.ProtoMajor == 2 && strings.Contains(r.Header.Get("Content-Type"), "application/grpc") {
|
|
|
|
|
if r.ProtoMajor == 2 { // grpc 请求
|
|
|
|
|
grpcServer.ServeHTTP(w, r)
|
|
|
|
|
} else if !strings.HasPrefix(r.URL.Path, "/api/") {
|
|
|
|
|
http.ServeFile(w, r, "./dist/index.html")
|
|
|
|
|
} else {
|
|
|
|
|
// 如 /api/v1/xxxx ,/api/v2/xxxx
|
|
|
|
|
otherHandler.ServeHTTP(w, r)
|
|
|
|
|
}
|
|
|
|
|
}), &http2.Server{})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// wrapH overwrite gin.WrapH
|
|
|
|
|
func wrapH(h http.Handler) gin.HandlerFunc {
|
|
|
|
|
return func(c *gin.Context) {
|
|
|
|
|
c.Status(http.StatusOK) // reset 404
|
|
|
|
|
h.ServeHTTP(c.Writer, c.Request)
|
|
|
|
|
}
|
|
|
|
|
}
|