package api import ( "context" "crypto/rand" "crypto/rsa" "crypto/tls" "crypto/x509" "encoding/json" "encoding/pem" "fmt" "log" "math/big" "time" "git.pyer.club/kingecg/gotidb/pkg/manager" "git.pyer.club/kingecg/gotidb/pkg/model" "github.com/quic-go/quic-go" ) // QUICServer represents the QUIC protocol server type QUICServer struct { listener quic.Listener manager *manager.DataManager tlsConfig *tls.Config quicConfig *quic.Config } // WriteRequest represents a write request from client type WriteRequest struct { Points []DataPoint `json:"points"` BatchID string `json:"batch_id"` } // DataPoint represents a single data point type DataPoint struct { DeviceID string `json:"device_id"` MetricCode string `json:"metric_code"` Labels map[string]string `json:"labels"` Value float64 `json:"value"` Timestamp int64 `json:"timestamp"` } // WriteResponse represents the response to a write request type WriteResponse struct { Success bool `json:"success"` Error string `json:"error,omitempty"` BatchID string `json:"batch_id"` PointsCount int `json:"points_count"` } // NewQUICServer creates a new QUIC server instance func NewQUICServer(dm *manager.DataManager) (*QUICServer, error) { // Generate self-signed certificate for development // In production, you should use proper certificates tlsConfig, err := generateTLSConfig() if err != nil { return nil, fmt.Errorf("failed to generate TLS config: %v", err) } quicConfig := &quic.Config{ MaxIdleTimeout: 30 * time.Second, KeepAlivePeriod: 10 * time.Second, } return &QUICServer{ manager: dm, tlsConfig: tlsConfig, quicConfig: quicConfig, }, nil } // Start starts the QUIC server func (s *QUICServer) Start(addr string) error { listener, err := quic.ListenAddr(addr, s.tlsConfig, s.quicConfig) if err != nil { return fmt.Errorf("failed to create QUIC listener: %v", err) } s.listener = listener go s.acceptConnections() return nil } // Stop stops the QUIC server func (s *QUICServer) Stop(ctx context.Context) error { if s.listener != nil { return s.listener.Close() } return nil } func (s *QUICServer) acceptConnections() { for { conn, err := s.listener.Accept(context.Background()) if err != nil { log.Printf("Failed to accept QUIC connection: %v", err) continue } go s.handleConnection(conn) } } func (s *QUICServer) handleConnection(conn quic.Connection) { for { stream, err := conn.AcceptStream(context.Background()) if err != nil { log.Printf("Failed to accept QUIC stream: %v", err) return } go s.handleStream(stream) } } func (s *QUICServer) handleStream(stream quic.Stream) { defer stream.Close() var req WriteRequest decoder := json.NewDecoder(stream) if err := decoder.Decode(&req); err != nil { s.sendError(stream, "Failed to decode request", err) return } response := WriteResponse{ BatchID: req.BatchID, PointsCount: len(req.Points), } // Process each data point for _, point := range req.Points { id := manager.CreateDataPoint( point.DeviceID, point.MetricCode, point.Labels, point.Value, ) value := model.DataValue{ Timestamp: point.Timestamp, Value: point.Value, } if err := s.manager.Write(context.Background(), id, value); err != nil { s.sendError(stream, "Failed to write data point", err) return } } response.Success = true encoder := json.NewEncoder(stream) if err := encoder.Encode(response); err != nil { log.Printf("Failed to send response: %v", err) } } func (s *QUICServer) sendError(stream quic.Stream, msg string, err error) { response := WriteResponse{ Success: false, Error: fmt.Sprintf("%s: %v", msg, err), } encoder := json.NewEncoder(stream) if err := encoder.Encode(response); err != nil { log.Printf("Failed to send error response: %v", err) } } // generateTLSConfig generates a self-signed certificate for development func generateTLSConfig() (*tls.Config, error) { key, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { return nil, err } template := x509.Certificate{ SerialNumber: big.NewInt(1), NotBefore: time.Now(), NotAfter: time.Now().Add(time.Hour * 24 * 180), // Valid for 180 days KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, } certDER, err := x509.CreateCertificate(rand.Reader, &template, &template, &key.PublicKey, key) if err != nil { return nil, err } keyPEM := pem.EncodeToMemory(&pem.Block{ Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key), }) certPEM := pem.EncodeToMemory(&pem.Block{ Type: "CERTIFICATE", Bytes: certDER, }) tlsCert, err := tls.X509KeyPair(certPEM, keyPEM) if err != nil { return nil, err } return &tls.Config{ Certificates: []tls.Certificate{tlsCert}, NextProtos: []string{"gotidb-quic"}, // Application protocol }, nil }