149 lines
3.7 KiB
Go
149 lines
3.7 KiB
Go
package server
|
|
|
|
import (
|
|
"context"
|
|
"log"
|
|
mathrand "math/rand"
|
|
"net"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
"git.fossy.my.id/bagas/tunnel-please-controller/db/sqlc/repository"
|
|
proto "git.fossy.my.id/bagas/tunnel-please-grpc/gen"
|
|
"github.com/google/uuid"
|
|
"google.golang.org/grpc"
|
|
"google.golang.org/grpc/health"
|
|
"google.golang.org/grpc/health/grpc_health_v1"
|
|
"google.golang.org/grpc/reflection"
|
|
"google.golang.org/protobuf/types/known/emptypb"
|
|
)
|
|
|
|
type Server struct {
|
|
Database *repository.Queries
|
|
Subscriber []chan *proto.Event
|
|
mu sync.RWMutex
|
|
proto.UnimplementedIdentityServer
|
|
proto.UnimplementedEventServiceServer
|
|
proto.UnimplementedSlugServer
|
|
}
|
|
|
|
func (s *Server) ChangeSlug(ctx context.Context, request *proto.ChangeSlugRequest) (*proto.ChangeSlugResponse, error) {
|
|
s.NotifyAllSubscriber(&proto.Event{
|
|
Type: proto.EventType_SLUG_CHANGE,
|
|
TimestampUnixMs: time.Now().Unix(),
|
|
Data: &proto.Event_DataEvent{DataEvent: &proto.SlugChangeEvent{
|
|
Old: request.GetOld(),
|
|
New: request.GetNew(),
|
|
}},
|
|
})
|
|
return &proto.ChangeSlugResponse{}, nil
|
|
}
|
|
|
|
func (s *Server) Subscribe(request *emptypb.Empty, g grpc.ServerStreamingServer[proto.Event]) error {
|
|
sr := make(chan *proto.Event)
|
|
s.AddSubscriberChan(sr)
|
|
defer s.RemoveSubscriberChan(sr)
|
|
|
|
for ev := range sr {
|
|
if err := g.Send(ev); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *Server) NotifyAllSubscriber(event *proto.Event) {
|
|
for _, subs := range s.Subscriber {
|
|
subs <- event
|
|
}
|
|
}
|
|
|
|
func (s *Server) AddSubscriberChan(event chan *proto.Event) {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
s.Subscriber = append(s.Subscriber, event)
|
|
}
|
|
|
|
func (s *Server) RemoveSubscriberChan(ch chan *proto.Event) {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
if len(s.Subscriber) == 0 || ch == nil {
|
|
return
|
|
}
|
|
newSubs := s.Subscriber[:0]
|
|
for _, c := range s.Subscriber {
|
|
if c == ch {
|
|
continue
|
|
}
|
|
newSubs = append(newSubs, c)
|
|
}
|
|
s.Subscriber = newSubs
|
|
|
|
close(ch)
|
|
}
|
|
|
|
func (s *Server) Get(ctx context.Context, request *proto.IdentifierRequest) (*proto.IdentifierResponse, error) {
|
|
parse, err := uuid.Parse(request.GetId())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
data, err := s.Database.GetIdentifierById(ctx, parse)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &proto.IdentifierResponse{
|
|
Id: data.ID.String(),
|
|
Slug: data.Slug,
|
|
}, nil
|
|
}
|
|
|
|
func (s *Server) Create(ctx context.Context, request *emptypb.Empty) (*proto.IdentifierResponse, error) {
|
|
createIdentifier, err := s.Database.CreateIdentifier(ctx, GenerateRandomString(32))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &proto.IdentifierResponse{
|
|
Id: createIdentifier.ID.String(),
|
|
Slug: createIdentifier.Slug,
|
|
}, nil
|
|
}
|
|
|
|
func GenerateRandomString(length int) string {
|
|
const charset = "abcdefghijklmnopqrstuvwxyz"
|
|
seededRand := mathrand.New(mathrand.NewSource(time.Now().UnixNano() + int64(mathrand.Intn(9999))))
|
|
var result strings.Builder
|
|
for i := 0; i < length; i++ {
|
|
randomIndex := seededRand.Intn(len(charset))
|
|
result.WriteString(string(charset[randomIndex]))
|
|
}
|
|
return result.String()
|
|
}
|
|
|
|
func New(database *repository.Queries) *Server {
|
|
return &Server{Database: database, Subscriber: make([]chan *proto.Event, 0)}
|
|
}
|
|
|
|
func (s *Server) ListenAndServe(Addr string) error {
|
|
listener, err := net.Listen("tcp", Addr)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
grpcServer := grpc.NewServer()
|
|
reflection.Register(grpcServer)
|
|
|
|
proto.RegisterIdentityServer(grpcServer, s)
|
|
proto.RegisterEventServiceServer(grpcServer, s)
|
|
proto.RegisterSlugServer(grpcServer, s)
|
|
|
|
healthServer := health.NewServer()
|
|
grpc_health_v1.RegisterHealthServer(grpcServer, healthServer)
|
|
healthServer.SetServingStatus("", grpc_health_v1.HealthCheckResponse_SERVING)
|
|
|
|
if err := grpcServer.Serve(listener); err != nil {
|
|
log.Fatalf("failed to serve: %v", err)
|
|
}
|
|
return nil
|
|
}
|