更新为postgre储存

This commit is contained in:
WuYuuuub
2026-04-28 20:26:52 +08:00
parent 8457cf1450
commit ac5b11b385
7 changed files with 557 additions and 234 deletions

View File

@@ -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"})
}