blob: 684d1d7821e1282626a4c4b3c7ce28e74eccd4cc [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) {
Adrià Vilanova Martínez25e12112021-08-25 13:48:06 +020062 killSwitches, err := db.ListKillSwitches(s.dbPool, ctx)
63 if err != nil {
64 return nil, status.Errorf(codes.Unavailable, err.Error())
65 }
66 res := &pb.GetKillSwitchOverviewResponse{
67 KillSwitches: killSwitches,
68 }
69 return res, nil
avm9996383f8f292021-08-24 18:26:52 +020070}
71
72func (s *killSwitchServiceServer) SyncFeatures(ctx context.Context, req *pb.SyncFeaturesRequest) (*pb.SyncFeaturesResponse, error) {
73 log.Println("Syncing features...")
74
75 for _, feature := range req.Features {
76 existingFeature, err := db.GetFeatureByCodename(s.dbPool, ctx, feature.Codename)
77 if err != nil {
78 return nil, status.Errorf(codes.Unavailable, err.Error())
79 }
80 // If the feature didn't exist in the db, add it. Otherwise, update it if applicable.
81 if existingFeature == nil {
82 if err := db.AddFeature(s.dbPool, ctx, feature); err != nil {
83 return nil, status.Error(codes.Unavailable, err.Error())
84 }
85 } else {
86 canonicalExistingFeature := *existingFeature
87 canonicalExistingFeature.Id = 0
88 if !proto.Equal(&canonicalExistingFeature, feature) {
89 if err := db.UpdateFeature(s.dbPool, ctx, existingFeature.Id, feature); err != nil {
90 return nil, status.Error(codes.Unavailable, err.Error())
91 }
92 }
93 }
94 }
95
96 res := &pb.SyncFeaturesResponse{}
97 return res, nil
98}
99
100func (s *killSwitchServiceServer) ListFeatures(ctx context.Context, req *pb.ListFeaturesRequest) (*pb.ListFeaturesResponse, error) {
Adrià Vilanova Martínez25e12112021-08-25 13:48:06 +0200101 features, err := db.ListFeatures(s.dbPool, ctx, req.WithDeprecatedFeatures)
102 if err != nil {
103 return nil, status.Errorf(codes.Unavailable, err.Error())
104 }
105 res := &pb.ListFeaturesResponse{
106 Features: features,
107 }
108 return res, nil
avm9996383f8f292021-08-24 18:26:52 +0200109}
110
111func (s *killSwitchServiceServer) EnableKillSwitch(ctx context.Context, req *pb.EnableKillSwitchRequest) (*pb.EnableKillSwitchResponse, error) {
Adrià Vilanova Martínez25e12112021-08-25 13:48:06 +0200112 if req.GetKillSwitch().GetFeature().GetId() == 0 {
113 return nil, status.Errorf(codes.InvalidArgument, "feature.id must be set.")
114 }
115
116 err := db.EnableKillSwitch(s.dbPool, ctx, req.KillSwitch)
117 if err != nil {
118 return nil, status.Errorf(codes.Unavailable, err.Error())
119 }
120 res := &pb.EnableKillSwitchResponse{}
121 return res, nil
avm9996383f8f292021-08-24 18:26:52 +0200122}
123
124func (s *killSwitchServiceServer) DisableKillSwitch(ctx context.Context, req *pb.DisableKillSwitchRequest) (*pb.DisableKillSwitchResponse, error) {
Adrià Vilanova Martínez25e12112021-08-25 13:48:06 +0200125 if req.GetKillSwitchId() == 0 {
126 return nil, status.Errorf(codes.InvalidArgument, "kill_switch_id must be set.")
127 }
128
129 err := db.DisableKillSwitch(s.dbPool, ctx, req.KillSwitchId)
130 if err != nil {
131 return nil, status.Errorf(codes.Unavailable, err.Error())
132 }
133 res := &pb.DisableKillSwitchResponse{}
134 return res, nil
avm9996383f8f292021-08-24 18:26:52 +0200135}
136
137func (s *killSwitchServiceServer) ListAuthorizedUsers(ctx context.Context, req *pb.ListAuthorizedUsersRequest) (*pb.ListAuthorizedUsersResponse, error) {
Adrià Vilanova Martínez25e12112021-08-25 13:48:06 +0200138 users, err := db.ListAuthorizedUsers(s.dbPool, ctx)
139 if err != nil {
140 return nil, status.Errorf(codes.Unavailable, err.Error())
141 }
142 res := &pb.ListAuthorizedUsersResponse{
143 Users: users,
144 }
145 return res, nil
avm9996383f8f292021-08-24 18:26:52 +0200146}
147
148func (s *killSwitchServiceServer) AddAuthorizedUser(ctx context.Context, req *pb.AddAuthorizedUserRequest) (*pb.AddAuthorizedUserResponse, error) {
Adrià Vilanova Martínez25e12112021-08-25 13:48:06 +0200149 if req.GetUser().GetGoogleUid() == "" && req.GetUser().GetEmail() == "" {
150 return nil, status.Errorf(codes.InvalidArgument, "At least one of google_uid or email must be set.")
151 }
152
153 err := db.AddAuthorizedUser(s.dbPool, ctx, req.User)
154 if err != nil {
155 return nil, status.Errorf(codes.Unavailable, err.Error())
156 }
157 res := &pb.AddAuthorizedUserResponse{}
158 return res, nil
avm9996383f8f292021-08-24 18:26:52 +0200159}
160
161func (s *killSwitchServiceServer) UpdateAuthorizedUser(ctx context.Context, req *pb.UpdateAuthorizedUserRequest) (*pb.UpdateAuthorizedUserResponse, error) {
Adrià Vilanova Martínez25e12112021-08-25 13:48:06 +0200162 if req.GetUserId() == 0 {
163 return nil, status.Errorf(codes.InvalidArgument, "user_id must be greater than 0.")
164 }
165
166 if req.GetUser().GetGoogleUid() == "" && req.GetUser().GetEmail() == "" {
167 return nil, status.Errorf(codes.InvalidArgument, "At least one of google_uid or email must be set.")
168 }
169
170 err := db.UpdateAuthorizedUser(s.dbPool, ctx, req.UserId, req.User)
171 if err != nil {
172 return nil, status.Errorf(codes.Unavailable, err.Error())
173 }
174 res := &pb.UpdateAuthorizedUserResponse{}
175 return res, nil
avm9996383f8f292021-08-24 18:26:52 +0200176}
177
178func (s *killSwitchServiceServer) DeleteAuthorizedUser(ctx context.Context, req *pb.DeleteAuthorizedUserRequest) (*pb.DeleteAuthorizedUserResponse, error) {
Adrià Vilanova Martínez25e12112021-08-25 13:48:06 +0200179 if req.GetUserId() == 0 {
180 return nil, status.Errorf(codes.InvalidArgument, "user_id must be greater than 0.")
181 }
182
183 err := db.DeleteAuthorizedUser(s.dbPool, ctx, req.UserId)
184 if err != nil {
185 return nil, status.Errorf(codes.Unavailable, err.Error())
186 }
187 res := &pb.DeleteAuthorizedUserResponse{}
188 return res, nil
avm9996383f8f292021-08-24 18:26:52 +0200189}
190
191func main() {
192 flag.Parse()
193
194 lis, err := net.Listen("tcp", fmt.Sprintf("localhost:%d", *port))
195 if err != nil {
196 log.Fatalf("Failed to listen: %v", err)
197 }
198
199 grpcServer := grpc.NewServer()
200 pb.RegisterKillSwitchServiceServer(grpcServer, newKillSwitchServiceServer())
201 // Register reflection service on gRPC server.
202 reflection.Register(grpcServer)
203 grpcServer.Serve(lis)
204}