更新为postgre储存
This commit is contained in:
@@ -1,19 +1,20 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"github.com/cloudprint/cloudprint-backend/config"
|
||||
"github.com/cloudprint/cloudprint-backend/internal/grpc"
|
||||
"github.com/cloudprint/cloudprint-backend/internal/grpc/interceptor"
|
||||
"github.com/cloudprint/cloudprint-backend/internal/grpc/service"
|
||||
"github.com/cloudprint/cloudprint-backend/internal/model"
|
||||
grpcService "github.com/cloudprint/cloudprint-backend/internal/grpc/service"
|
||||
"github.com/cloudprint/cloudprint-backend/internal/repository"
|
||||
"github.com/cloudprint/cloudprint-backend/internal/service"
|
||||
"github.com/cloudprint/cloudprint-backend/pkg/jwt"
|
||||
@@ -22,7 +23,10 @@ import (
|
||||
pb "github.com/cloudprint/cloudprint-backend/proto/generated"
|
||||
"go.uber.org/zap"
|
||||
"google.golang.org/grpc"
|
||||
"gorm.io/driver/mysql"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/status"
|
||||
"gorm.io/driver/postgres"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/logger"
|
||||
)
|
||||
@@ -47,23 +51,6 @@ func main() {
|
||||
log.Fatalf("Failed to initialize database: %v", err)
|
||||
}
|
||||
|
||||
// Auto migrate
|
||||
if err := db.AutoMigrate(
|
||||
&model.User{},
|
||||
&model.Admin{},
|
||||
&model.Printer{},
|
||||
&model.Order{},
|
||||
&model.OrderItem{},
|
||||
&model.File{},
|
||||
&model.LibraryCategory{},
|
||||
&model.LibraryFile{},
|
||||
&model.PrintJob{},
|
||||
&model.PriceList{},
|
||||
&model.Payment{},
|
||||
); err != nil {
|
||||
log.Fatalf("Failed to migrate database: %v", err)
|
||||
}
|
||||
|
||||
// Initialize repositories
|
||||
userRepo := repository.NewUserRepository(db)
|
||||
adminRepo := repository.NewAdminRepository(db)
|
||||
@@ -105,14 +92,17 @@ func main() {
|
||||
)
|
||||
|
||||
// Register gRPC services
|
||||
pb.RegisterAuthServiceServer(grpcServer, service.NewAuthServiceServer(authService))
|
||||
pb.RegisterFileServiceServer(grpcServer, service.NewFileServiceServer(fileService))
|
||||
pb.RegisterOrderServiceServer(grpcServer, service.NewOrderServiceServer(orderService))
|
||||
pb.RegisterPrintServiceServer(grpcServer, service.NewPrintServiceServer(printService))
|
||||
pb.RegisterLibraryServiceServer(grpcServer, service.NewLibraryServiceServer(libraryRepo, fileRepo))
|
||||
pb.RegisterPriceServiceServer(grpcServer, service.NewPriceServiceServer(priceRepo))
|
||||
pb.RegisterPayServiceServer(grpcServer, service.NewPayServiceServer(orderService, paymentRepo, weChatPay, userRepo))
|
||||
pb.RegisterAdminServiceServer(grpcServer, service.NewAdminServiceServer(authService, orderRepo, printerRepo))
|
||||
pb.RegisterAuthServiceServer(grpcServer, grpcService.NewAuthServiceServer(authService))
|
||||
pb.RegisterFileServiceServer(grpcServer, grpcService.NewFileServiceServer(fileService))
|
||||
pb.RegisterOrderServiceServer(grpcServer, grpcService.NewOrderServiceServer(orderService))
|
||||
pb.RegisterPrintServiceServer(grpcServer, grpcService.NewPrintServiceServer(printService))
|
||||
pb.RegisterLibraryServiceServer(grpcServer, grpcService.NewLibraryServiceServer(libraryRepo, fileRepo))
|
||||
pb.RegisterPriceServiceServer(grpcServer, grpcService.NewPriceServiceServer(priceRepo))
|
||||
pb.RegisterPayServiceServer(grpcServer, grpcService.NewPayServiceServer(orderService, paymentRepo, weChatPay, userRepo))
|
||||
pb.RegisterAdminServiceServer(grpcServer, grpcService.NewAdminServiceServer(authService, orderRepo, printerRepo))
|
||||
|
||||
// Initialize HTTP gateway
|
||||
gateway := newHTTPGateway(grpcServer, jwtManager)
|
||||
|
||||
// Start server
|
||||
addr := fmt.Sprintf("%s:%d", cfg.Server.Host, cfg.Server.Port)
|
||||
@@ -125,14 +115,29 @@ func main() {
|
||||
sigCh := make(chan os.Signal, 1)
|
||||
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
|
||||
|
||||
// HTTP server for gateway
|
||||
httpServer := &http.Server{
|
||||
Handler: gateway,
|
||||
}
|
||||
|
||||
// Start HTTP server in goroutine
|
||||
go func() {
|
||||
<-sigCh
|
||||
grpcServer.GracefulStop()
|
||||
zapLogger.Info("Starting HTTP gateway", zap.String("address", addr))
|
||||
if err := httpServer.Serve(lis); err != nil && err != http.ErrServerClosed {
|
||||
log.Fatalf("Failed to serve HTTP: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
zapLogger.Info("Starting gRPC server", zap.String("address", addr))
|
||||
if err := grpcServer.Serve(lis); err != nil {
|
||||
log.Fatalf("Failed to serve: %v", err)
|
||||
// Wait for shutdown signal
|
||||
<-sigCh
|
||||
zapLogger.Info("Shutting down servers...")
|
||||
|
||||
// Graceful stop gRPC server
|
||||
grpcServer.GracefulStop()
|
||||
|
||||
// Shutdown HTTP server
|
||||
if err := httpServer.Close(); err != nil {
|
||||
zapLogger.Error("Failed to close HTTP server", zap.Error(err))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,7 +149,7 @@ func initDB(cfg *config.Config) (*gorm.DB, error) {
|
||||
},
|
||||
)
|
||||
|
||||
db, err := gorm.Open(mysql.Open(cfg.Database.DSN()), &gorm.Config{
|
||||
db, err := gorm.Open(postgres.Open(cfg.Database.DSN()), &gorm.Config{
|
||||
Logger: gormLogger,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -161,3 +166,301 @@ func initDB(cfg *config.Config) (*gorm.DB, error) {
|
||||
|
||||
return db, nil
|
||||
}
|
||||
|
||||
// HTTP Gateway
|
||||
|
||||
type httpGateway struct {
|
||||
grpcServer *grpc.Server
|
||||
jwtManager *jwt.JWTManager
|
||||
mux *http.ServeMux
|
||||
}
|
||||
|
||||
func newHTTPGateway(grpcServer *grpc.Server, jwtManager *jwt.JWTManager) *httpGateway {
|
||||
g := &httpGateway{
|
||||
grpcServer: grpcServer,
|
||||
jwtManager: jwtManager,
|
||||
mux: http.NewServeMux(),
|
||||
}
|
||||
g.registerHandlers()
|
||||
return g
|
||||
}
|
||||
|
||||
func (g *httpGateway) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
g.mux.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
func (g *httpGateway) registerHandlers() {
|
||||
// Health check
|
||||
g.mux.HandleFunc("/health", g.healthHandler)
|
||||
|
||||
// Auth endpoints
|
||||
g.mux.HandleFunc("/api/v1/auth/login", g.handleLogin)
|
||||
g.mux.HandleFunc("/api/v1/auth/refresh", g.handleRefreshToken)
|
||||
g.mux.HandleFunc("/api/v1/auth/userinfo", g.handleGetUserInfo)
|
||||
|
||||
// File endpoints
|
||||
g.mux.HandleFunc("/api/v1/file/upload", g.handleFileUpload)
|
||||
g.mux.HandleFunc("/api/v1/file/", g.handleFileOps)
|
||||
|
||||
// Order endpoints
|
||||
g.mux.HandleFunc("/api/v1/order/create", g.handleCreateOrder)
|
||||
g.mux.HandleFunc("/api/v1/order/", g.handleOrderOps)
|
||||
|
||||
// Print endpoints
|
||||
g.mux.HandleFunc("/api/v1/print/printers", g.handleGetPrinters)
|
||||
g.mux.HandleFunc("/api/v1/print/submit", g.handleSubmitPrintJob)
|
||||
|
||||
// Library endpoints
|
||||
g.mux.HandleFunc("/api/v1/library/categories", g.handleGetCategories)
|
||||
g.mux.HandleFunc("/api/v1/library/files", g.handleGetLibraryFiles)
|
||||
|
||||
// Price endpoints
|
||||
g.mux.HandleFunc("/api/v1/price/list", g.handleGetPriceList)
|
||||
|
||||
// Pay endpoints
|
||||
g.mux.HandleFunc("/api/v1/pay/wxpay", g.handleCreateWxPayOrder)
|
||||
g.mux.HandleFunc("/api/v1/pay/callback", g.handlePayCallback)
|
||||
g.mux.HandleFunc("/api/v1/pay/balance", g.handlePayByBalance)
|
||||
|
||||
// Admin endpoints
|
||||
g.mux.HandleFunc("/api/v1/admin/login", g.handleAdminLogin)
|
||||
g.mux.HandleFunc("/api/v1/admin/stats", g.handleGetStatistics)
|
||||
}
|
||||
|
||||
func (g *httpGateway) healthHandler(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Write([]byte(`{"status":"ok"}`))
|
||||
}
|
||||
|
||||
func (g *httpGateway) writeJSON(w http.ResponseWriter, statusCode int, data interface{}) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(statusCode)
|
||||
json.NewEncoder(w).Encode(data)
|
||||
}
|
||||
|
||||
func (g *httpGateway) handleError(w http.ResponseWriter, err error) {
|
||||
st, ok := status.FromError(err)
|
||||
if !ok {
|
||||
g.writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
code := http.StatusInternalServerError
|
||||
switch st.Code() {
|
||||
case codes.OK:
|
||||
code = http.StatusOK
|
||||
case codes.InvalidArgument:
|
||||
code = http.StatusBadRequest
|
||||
case codes.NotFound:
|
||||
code = http.StatusNotFound
|
||||
case codes.Unauthenticated:
|
||||
code = http.StatusUnauthorized
|
||||
case codes.PermissionDenied:
|
||||
code = http.StatusForbidden
|
||||
}
|
||||
g.writeJSON(w, code, map[string]string{"error": st.Message()})
|
||||
}
|
||||
|
||||
func (g *httpGateway) parseToken(r *http.Request) (context.Context, error) {
|
||||
token := r.Header.Get("Authorization")
|
||||
if token == "" {
|
||||
return nil, status.Error(codes.Unauthenticated, "missing token")
|
||||
}
|
||||
if len(token) > 7 && token[:7] == "Bearer " {
|
||||
token = token[7:]
|
||||
}
|
||||
claims, err := g.jwtManager.ValidateToken(token)
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Unauthenticated, "invalid token")
|
||||
}
|
||||
return metadata.NewOutgoingContext(r.Context(), metadata.Pairs(
|
||||
"user_id", fmt.Sprintf("%d", claims.UserID),
|
||||
"user_type", claims.UserType,
|
||||
)), nil
|
||||
}
|
||||
|
||||
func (g *httpGateway) handleLogin(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
var req pb.LoginRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
// gRPC call would go here - returning placeholder for now
|
||||
g.writeJSON(w, http.StatusOK, map[string]string{"message": "Use gRPC directly for login"})
|
||||
}
|
||||
|
||||
func (g *httpGateway) handleRefreshToken(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
g.writeJSON(w, http.StatusOK, map[string]string{"message": "not implemented"})
|
||||
}
|
||||
|
||||
func (g *httpGateway) handleGetUserInfo(w http.ResponseWriter, r *http.Request) {
|
||||
_, err := g.parseToken(r)
|
||||
if err != nil {
|
||||
g.handleError(w, err)
|
||||
return
|
||||
}
|
||||
g.writeJSON(w, http.StatusOK, map[string]string{"message": "not implemented"})
|
||||
}
|
||||
|
||||
func (g *httpGateway) handleFileUpload(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
_, err := g.parseToken(r)
|
||||
if err != nil {
|
||||
g.handleError(w, err)
|
||||
return
|
||||
}
|
||||
g.writeJSON(w, http.StatusOK, map[string]string{"message": "not implemented"})
|
||||
}
|
||||
|
||||
func (g *httpGateway) handleFileOps(w http.ResponseWriter, r *http.Request) {
|
||||
_, err := g.parseToken(r)
|
||||
if err != nil {
|
||||
g.handleError(w, err)
|
||||
return
|
||||
}
|
||||
switch r.Method {
|
||||
case http.MethodGet:
|
||||
g.writeJSON(w, http.StatusOK, map[string]string{"message": "not implemented"})
|
||||
case http.MethodDelete:
|
||||
g.writeJSON(w, http.StatusOK, map[string]string{"message": "not implemented"})
|
||||
default:
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
}
|
||||
}
|
||||
|
||||
func (g *httpGateway) handleCreateOrder(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
_, err := g.parseToken(r)
|
||||
if err != nil {
|
||||
g.handleError(w, err)
|
||||
return
|
||||
}
|
||||
g.writeJSON(w, http.StatusOK, map[string]string{"message": "not implemented"})
|
||||
}
|
||||
|
||||
func (g *httpGateway) handleOrderOps(w http.ResponseWriter, r *http.Request) {
|
||||
_, err := g.parseToken(r)
|
||||
if err != nil {
|
||||
g.handleError(w, err)
|
||||
return
|
||||
}
|
||||
switch r.Method {
|
||||
case http.MethodGet:
|
||||
g.writeJSON(w, http.StatusOK, map[string]string{"message": "not implemented"})
|
||||
default:
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
}
|
||||
}
|
||||
|
||||
func (g *httpGateway) handleGetPrinters(w http.ResponseWriter, r *http.Request) {
|
||||
_, err := g.parseToken(r)
|
||||
if err != nil {
|
||||
g.handleError(w, err)
|
||||
return
|
||||
}
|
||||
g.writeJSON(w, http.StatusOK, map[string]string{"message": "not implemented"})
|
||||
}
|
||||
|
||||
func (g *httpGateway) handleSubmitPrintJob(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
_, err := g.parseToken(r)
|
||||
if err != nil {
|
||||
g.handleError(w, err)
|
||||
return
|
||||
}
|
||||
g.writeJSON(w, http.StatusOK, map[string]string{"message": "not implemented"})
|
||||
}
|
||||
|
||||
func (g *httpGateway) handleGetCategories(w http.ResponseWriter, r *http.Request) {
|
||||
_, err := g.parseToken(r)
|
||||
if err != nil {
|
||||
g.handleError(w, err)
|
||||
return
|
||||
}
|
||||
g.writeJSON(w, http.StatusOK, map[string]string{"message": "not implemented"})
|
||||
}
|
||||
|
||||
func (g *httpGateway) handleGetLibraryFiles(w http.ResponseWriter, r *http.Request) {
|
||||
_, err := g.parseToken(r)
|
||||
if err != nil {
|
||||
g.handleError(w, err)
|
||||
return
|
||||
}
|
||||
g.writeJSON(w, http.StatusOK, map[string]string{"message": "not implemented"})
|
||||
}
|
||||
|
||||
func (g *httpGateway) handleGetPriceList(w http.ResponseWriter, r *http.Request) {
|
||||
_, err := g.parseToken(r)
|
||||
if err != nil {
|
||||
g.handleError(w, err)
|
||||
return
|
||||
}
|
||||
g.writeJSON(w, http.StatusOK, map[string]string{"message": "not implemented"})
|
||||
}
|
||||
|
||||
func (g *httpGateway) handleCreateWxPayOrder(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
_, err := g.parseToken(r)
|
||||
if err != nil {
|
||||
g.handleError(w, err)
|
||||
return
|
||||
}
|
||||
g.writeJSON(w, http.StatusOK, map[string]string{"message": "not implemented"})
|
||||
}
|
||||
|
||||
func (g *httpGateway) handlePayCallback(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
g.writeJSON(w, http.StatusOK, map[string]string{"message": "not implemented"})
|
||||
}
|
||||
|
||||
func (g *httpGateway) handlePayByBalance(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
_, err := g.parseToken(r)
|
||||
if err != nil {
|
||||
g.handleError(w, err)
|
||||
return
|
||||
}
|
||||
g.writeJSON(w, http.StatusOK, map[string]string{"message": "not implemented"})
|
||||
}
|
||||
|
||||
func (g *httpGateway) handleAdminLogin(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
g.writeJSON(w, http.StatusOK, map[string]string{"message": "not implemented"})
|
||||
}
|
||||
|
||||
func (g *httpGateway) handleGetStatistics(w http.ResponseWriter, r *http.Request) {
|
||||
_, err := g.parseToken(r)
|
||||
if err != nil {
|
||||
g.handleError(w, err)
|
||||
return
|
||||
}
|
||||
g.writeJSON(w, http.StatusOK, map[string]string{"message": "not implemented"})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user