blob: 53ab224b6bcbde2e85cc2bd41b855c2cae08befe [file] [log] [blame]
avm9996383f8f292021-08-24 18:26:52 +02001package main
2
3import (
4 "context"
5 "database/sql"
6 "flag"
7 "fmt"
8 "log"
9 "net"
10 "time"
11
12 _ "github.com/go-sql-driver/mysql"
13 "google.golang.org/grpc"
14 "google.golang.org/grpc/codes"
15 "google.golang.org/grpc/reflection"
16 "google.golang.org/grpc/status"
17 "google.golang.org/protobuf/proto"
18
19 pb "gomodules.avm99963.com/twpt-server/api_proto"
20 db "gomodules.avm99963.com/twpt-server/internal/db"
21)
22
23var (
24 port = flag.Int("port", 10000, "The server port")
25 dbDsn = flag.String("db", "", "MySQL/MariaDB database data source name (DSN)") // https://github.com/go-sql-driver/mysql#dsn-data-source-name
26 dbConnMaxLifetime = flag.Int("dbConnMaxLifetime", 3*60, "Maximum amount of time a connection to the database may be reused in seconds.")
27 dbMaxOpenConns = flag.Int("dbMaxOpenConns", 5, "Maximum number of open connections to the database.")
28 dbMaxIdleConns = flag.Int("dbMaxIdleConns", *dbMaxOpenConns, "Maximum number of connections to the database in the idle connection pool.")
29)
30
31type killSwitchServiceServer struct {
32 pb.UnimplementedKillSwitchServiceServer
33 dbPool *sql.DB
34}
35
36func newKillSwitchServiceServer() *killSwitchServiceServer {
37 s := &killSwitchServiceServer{}
38 db, err := sql.Open("mysql", *dbDsn)
39 if err != nil {
40 log.Fatalf("unable to open database connection: %v", err)
41 }
42 db.SetConnMaxLifetime(time.Duration(*dbConnMaxLifetime) * time.Second)
43 db.SetMaxOpenConns(*dbMaxOpenConns)
44 db.SetMaxIdleConns(*dbMaxIdleConns)
45
46 ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
47 defer cancel()
48
49 if err := db.PingContext(ctx); err != nil {
50 log.Fatalf("unable to connect to database: %v", err)
51 }
52
53 s.dbPool = db
54 return s
55}
56
57func (s *killSwitchServiceServer) GetKillSwitchStatus(ctx context.Context, req *pb.GetKillSwitchStatusRequest) (*pb.GetKillSwitchStatusResponse, error) {
58 return nil, status.Errorf(codes.Unimplemented, "Unimplemented method.")
59}
60
61func (s *killSwitchServiceServer) GetKillSwitchOverview(ctx context.Context, req *pb.GetKillSwitchOverviewRequest) (*pb.GetKillSwitchOverviewResponse, error) {
62 return nil, status.Errorf(codes.Unimplemented, "Unimplemented method.")
63}
64
65func (s *killSwitchServiceServer) SyncFeatures(ctx context.Context, req *pb.SyncFeaturesRequest) (*pb.SyncFeaturesResponse, error) {
66 log.Println("Syncing features...")
67
68 for _, feature := range req.Features {
69 existingFeature, err := db.GetFeatureByCodename(s.dbPool, ctx, feature.Codename)
70 if err != nil {
71 return nil, status.Errorf(codes.Unavailable, err.Error())
72 }
73 // If the feature didn't exist in the db, add it. Otherwise, update it if applicable.
74 if existingFeature == nil {
75 if err := db.AddFeature(s.dbPool, ctx, feature); err != nil {
76 return nil, status.Error(codes.Unavailable, err.Error())
77 }
78 } else {
79 canonicalExistingFeature := *existingFeature
80 canonicalExistingFeature.Id = 0
81 if !proto.Equal(&canonicalExistingFeature, feature) {
82 if err := db.UpdateFeature(s.dbPool, ctx, existingFeature.Id, feature); err != nil {
83 return nil, status.Error(codes.Unavailable, err.Error())
84 }
85 }
86 }
87 }
88
89 res := &pb.SyncFeaturesResponse{}
90 return res, nil
91}
92
93func (s *killSwitchServiceServer) ListFeatures(ctx context.Context, req *pb.ListFeaturesRequest) (*pb.ListFeaturesResponse, error) {
94 features, err := db.ListFeatures(s.dbPool, ctx, req.WithDeprecatedFeatures)
95 if err != nil {
96 return nil, status.Errorf(codes.Unavailable, err.Error())
97 }
98 res := &pb.ListFeaturesResponse{
99 Features: features,
100 }
101 return res, nil
102}
103
104func (s *killSwitchServiceServer) EnableKillSwitch(ctx context.Context, req *pb.EnableKillSwitchRequest) (*pb.EnableKillSwitchResponse, error) {
105 return nil, status.Errorf(codes.Unimplemented, "Unimplemented method.")
106}
107
108func (s *killSwitchServiceServer) DisableKillSwitch(ctx context.Context, req *pb.DisableKillSwitchRequest) (*pb.DisableKillSwitchResponse, error) {
109 return nil, status.Errorf(codes.Unimplemented, "Unimplemented method.")
110}
111
112func (s *killSwitchServiceServer) ListAuthorizedUsers(ctx context.Context, req *pb.ListAuthorizedUsersRequest) (*pb.ListAuthorizedUsersResponse, error) {
113 return nil, status.Errorf(codes.Unimplemented, "Unimplemented method.")
114}
115
116func (s *killSwitchServiceServer) AddAuthorizedUser(ctx context.Context, req *pb.AddAuthorizedUserRequest) (*pb.AddAuthorizedUserResponse, error) {
117 return nil, status.Errorf(codes.Unimplemented, "Unimplemented method.")
118}
119
120func (s *killSwitchServiceServer) UpdateAuthorizedUser(ctx context.Context, req *pb.UpdateAuthorizedUserRequest) (*pb.UpdateAuthorizedUserResponse, error) {
121 return nil, status.Errorf(codes.Unimplemented, "Unimplemented method.")
122}
123
124func (s *killSwitchServiceServer) DeleteAuthorizedUser(ctx context.Context, req *pb.DeleteAuthorizedUserRequest) (*pb.DeleteAuthorizedUserResponse, error) {
125 return nil, status.Errorf(codes.Unimplemented, "Unimplemented method.")
126}
127
128func main() {
129 flag.Parse()
130
131 lis, err := net.Listen("tcp", fmt.Sprintf("localhost:%d", *port))
132 if err != nil {
133 log.Fatalf("Failed to listen: %v", err)
134 }
135
136 grpcServer := grpc.NewServer()
137 pb.RegisterKillSwitchServiceServer(grpcServer, newKillSwitchServiceServer())
138 // Register reflection service on gRPC server.
139 reflection.Register(grpcServer)
140 grpcServer.Serve(lis)
141}