this repo has no description

add cassandra, dockerfile, migrations

+1359 -39
+64
cmd/database/main.go
··· 1 + package main 2 + 3 + import ( 4 + "context" 5 + "fmt" 6 + "log" 7 + "os" 8 + 9 + "github.com/bluesky-social/go-util/pkg/telemetry" 10 + "github.com/urfave/cli/v2" 11 + "github.com/vylet-app/go/database/server" 12 + ) 13 + 14 + func main() { 15 + app := cli.App{ 16 + Name: "vylet-database", 17 + Flags: []cli.Flag{ 18 + telemetry.CLIFlagDebug, 19 + telemetry.CLIFlagMetricsListenAddress, 20 + &cli.StringFlag{ 21 + Name: "listen-addr", 22 + Value: ":9090", 23 + EnvVars: []string{"VYLET_DATABASE_LISTEN_ADDR"}, 24 + }, 25 + &cli.StringSliceFlag{ 26 + Name: "cassandra-addrs", 27 + Value: cli.NewStringSlice("127.0.0.1"), 28 + }, 29 + &cli.StringFlag{ 30 + Name: "cassandra-keyspace", 31 + Value: "vylet", 32 + }, 33 + }, 34 + Action: run, 35 + } 36 + 37 + if err := app.Run(os.Args); err != nil { 38 + log.Fatal(err) 39 + } 40 + } 41 + 42 + func run(cmd *cli.Context) error { 43 + ctx := context.Background() 44 + 45 + logger := telemetry.StartLogger(cmd) 46 + telemetry.StartMetrics(cmd) 47 + 48 + server, err := server.New(&server.Args{ 49 + Logger: logger, 50 + 51 + ListenAddr: cmd.String("listen-addr"), 52 + CassandraAddrs: cmd.StringSlice("cassandra-addrs"), 53 + CassandraKeyspace: cmd.String("cassandra-keyspace"), 54 + }) 55 + if err != nil { 56 + return fmt.Errorf("failed to create new server: %w", err) 57 + } 58 + 59 + if err := server.Run(ctx); err != nil { 60 + return fmt.Errorf("failed to run server: %w", err) 61 + } 62 + 63 + return nil 64 + }
+112
cmd/database/migrate/main.go
··· 1 + package main 2 + 3 + import ( 4 + "fmt" 5 + "log" 6 + "os" 7 + "time" 8 + 9 + "github.com/gocql/gocql" 10 + "github.com/urfave/cli/v2" 11 + "github.com/vylet-app/go/database/server" 12 + ) 13 + 14 + func main() { 15 + app := &cli.App{ 16 + Name: "migrate", 17 + Usage: "Cassandra database migration tool", 18 + Flags: []cli.Flag{ 19 + &cli.StringFlag{ 20 + Name: "migrations", 21 + Aliases: []string{"m"}, 22 + Value: "./migrations", 23 + Usage: "Path to migrations directory", 24 + EnvVars: []string{"VYLET_DATABASE_MIGRATIONS_PATH"}, 25 + }, 26 + &cli.StringSliceFlag{ 27 + Name: "cassandra-addrs", 28 + Value: cli.NewStringSlice("127.0.0.1"), 29 + Usage: "Comma-separated Cassandra hosts", 30 + EnvVars: []string{"VYLET_DATABASE_CASSANDRA_ADDRS", "VYLET_DATABASE_CASSANDRA_HOSTS"}, 31 + }, 32 + &cli.StringFlag{ 33 + Name: "cassandra-keyspace", 34 + Aliases: []string{"k"}, 35 + Value: "vylet", 36 + Usage: "Cassandra keyspace", 37 + EnvVars: []string{"VYLET_DATABASE_CASSANDRA_KEYSPACE"}, 38 + }, 39 + }, 40 + Commands: []*cli.Command{ 41 + { 42 + Name: "up", 43 + Aliases: []string{"u"}, 44 + Usage: "Run migrations", 45 + Action: runMigrationsUp, 46 + }, 47 + { 48 + Name: "down", 49 + Aliases: []string{"d"}, 50 + Usage: "Rollback last migration", 51 + Action: runMigrationsDown, 52 + }, 53 + }, 54 + Action: func(c *cli.Context) error { 55 + // Default action if no command specified 56 + return runMigrationsUp(c) 57 + }, 58 + } 59 + 60 + if err := app.Run(os.Args); err != nil { 61 + log.Fatal(err) 62 + } 63 + } 64 + 65 + func runMigrationsUp(c *cli.Context) error { 66 + session, err := connectCassandra(c) 67 + if err != nil { 68 + return err 69 + } 70 + defer session.Close() 71 + 72 + migrationsPath := c.String("migrations") 73 + log.Println("Running migrations...") 74 + if err := server.RunMigrations(session, migrationsPath); err != nil { 75 + log.Fatalf("Migration failed: %v", err) 76 + } 77 + log.Println("Migrations completed successfully") 78 + return nil 79 + } 80 + 81 + func runMigrationsDown(c *cli.Context) error { 82 + session, err := connectCassandra(c) 83 + if err != nil { 84 + return err 85 + } 86 + defer session.Close() 87 + 88 + migrationsPath := c.String("migrations") 89 + log.Println("Rolling back last migration...") 90 + if err := server.RollbackMigration(session, migrationsPath); err != nil { 91 + log.Fatalf("Rollback failed: %v", err) 92 + } 93 + log.Println("Rollback completed successfully") 94 + return nil 95 + } 96 + 97 + func connectCassandra(c *cli.Context) (*gocql.Session, error) { 98 + fmt.Println(c.StringSlice("cassandra-addrs")) 99 + cluster := gocql.NewCluster(c.StringSlice("cassandra-addrs")...) 100 + cluster.Keyspace = c.String("cassandra-keyspace") 101 + cluster.Consistency = gocql.Quorum 102 + cluster.ProtoVersion = 4 103 + cluster.ConnectTimeout = time.Second * 10 104 + cluster.Timeout = time.Second * 10 105 + 106 + session, err := cluster.CreateSession() 107 + if err != nil { 108 + return nil, fmt.Errorf("failed to connect to cassandra: %w", err) 109 + } 110 + 111 + return session, nil 112 + }
+20
database/proto/buf.gen.yaml
··· 1 + version: v2 2 + managed: 3 + enabled: true 4 + override: 5 + - file_option: go_package_prefix 6 + module: buf.build/bufbuild/confluent 7 + value: buf.build/gen/go/bufbuild/confluent/protocolbuffers/go 8 + disable: 9 + - file_option: go_package 10 + module: buf.build/bufbuild/protovalidate 11 + plugins: 12 + - remote: buf.build/protocolbuffers/go:v1.36.6 13 + out: . 14 + opt: paths=source_relative 15 + 16 + - remote: buf.build/grpc/go 17 + out: . 18 + opt: 19 + - paths=source_relative 20 +
+9
database/proto/buf.lock
··· 1 + # Generated by buf. DO NOT EDIT. 2 + version: v2 3 + deps: 4 + - name: buf.build/bufbuild/confluent 5 + commit: 65369e65bbcd4715989b6fbe53cb8a71 6 + digest: b5:4c002e10c90c1b84be15e7f1f95508ad07499225faf79305f9e03bd7b0dc174e5720b6f3762e30dee4d703cba4c7522c4242b58770c28c2d881c35e86dc114fb 7 + - name: buf.build/bufbuild/protovalidate 8 + commit: 52f32327d4b045a79293a6ad4e7e1236 9 + digest: b5:cbabc98d4b7b7b0447c9b15f68eeb8a7a44ef8516cb386ac5f66e7fd4062cd6723ed3f452ad8c384b851f79e33d26e7f8a94e2b807282b3def1cd966c7eace97
+14
database/proto/buf.yaml
··· 1 + # For details on buf.yaml configuration, visit https://buf.build/docs/configuration/v2/buf-yaml 2 + version: v2 3 + breaking: 4 + use: 5 + - FILE 6 + lint: 7 + use: 8 + - STANDARD 9 + except: 10 + - PACKAGE_VERSION_SUFFIX 11 + - PACKAGE_DIRECTORY_MATCH 12 + deps: 13 + - buf.build/bufbuild/confluent 14 + - buf.build/bufbuild/protovalidate
+276
database/proto/profile.pb.go
··· 1 + // Code generated by protoc-gen-go. DO NOT EDIT. 2 + // versions: 3 + // protoc-gen-go v1.36.6 4 + // protoc (unknown) 5 + // source: profile.proto 6 + 7 + package vyletdatabase 8 + 9 + import ( 10 + _ "buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go/buf/validate" 11 + protoreflect "google.golang.org/protobuf/reflect/protoreflect" 12 + protoimpl "google.golang.org/protobuf/runtime/protoimpl" 13 + _ "google.golang.org/protobuf/types/known/timestamppb" 14 + reflect "reflect" 15 + sync "sync" 16 + unsafe "unsafe" 17 + ) 18 + 19 + const ( 20 + // Verify that this generated code is sufficiently up-to-date. 21 + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) 22 + // Verify that runtime/protoimpl is sufficiently up-to-date. 23 + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) 24 + ) 25 + 26 + type GetProfileRequest struct { 27 + state protoimpl.MessageState `protogen:"open.v1"` 28 + Did string `protobuf:"bytes,1,opt,name=did,proto3" json:"did,omitempty"` 29 + unknownFields protoimpl.UnknownFields 30 + sizeCache protoimpl.SizeCache 31 + } 32 + 33 + func (x *GetProfileRequest) Reset() { 34 + *x = GetProfileRequest{} 35 + mi := &file_profile_proto_msgTypes[0] 36 + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 37 + ms.StoreMessageInfo(mi) 38 + } 39 + 40 + func (x *GetProfileRequest) String() string { 41 + return protoimpl.X.MessageStringOf(x) 42 + } 43 + 44 + func (*GetProfileRequest) ProtoMessage() {} 45 + 46 + func (x *GetProfileRequest) ProtoReflect() protoreflect.Message { 47 + mi := &file_profile_proto_msgTypes[0] 48 + if x != nil { 49 + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 50 + if ms.LoadMessageInfo() == nil { 51 + ms.StoreMessageInfo(mi) 52 + } 53 + return ms 54 + } 55 + return mi.MessageOf(x) 56 + } 57 + 58 + // Deprecated: Use GetProfileRequest.ProtoReflect.Descriptor instead. 59 + func (*GetProfileRequest) Descriptor() ([]byte, []int) { 60 + return file_profile_proto_rawDescGZIP(), []int{0} 61 + } 62 + 63 + func (x *GetProfileRequest) GetDid() string { 64 + if x != nil { 65 + return x.Did 66 + } 67 + return "" 68 + } 69 + 70 + type GetProfileResponse struct { 71 + state protoimpl.MessageState `protogen:"open.v1"` 72 + Did string `protobuf:"bytes,1,opt,name=did,proto3" json:"did,omitempty"` 73 + unknownFields protoimpl.UnknownFields 74 + sizeCache protoimpl.SizeCache 75 + } 76 + 77 + func (x *GetProfileResponse) Reset() { 78 + *x = GetProfileResponse{} 79 + mi := &file_profile_proto_msgTypes[1] 80 + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 81 + ms.StoreMessageInfo(mi) 82 + } 83 + 84 + func (x *GetProfileResponse) String() string { 85 + return protoimpl.X.MessageStringOf(x) 86 + } 87 + 88 + func (*GetProfileResponse) ProtoMessage() {} 89 + 90 + func (x *GetProfileResponse) ProtoReflect() protoreflect.Message { 91 + mi := &file_profile_proto_msgTypes[1] 92 + if x != nil { 93 + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 94 + if ms.LoadMessageInfo() == nil { 95 + ms.StoreMessageInfo(mi) 96 + } 97 + return ms 98 + } 99 + return mi.MessageOf(x) 100 + } 101 + 102 + // Deprecated: Use GetProfileResponse.ProtoReflect.Descriptor instead. 103 + func (*GetProfileResponse) Descriptor() ([]byte, []int) { 104 + return file_profile_proto_rawDescGZIP(), []int{1} 105 + } 106 + 107 + func (x *GetProfileResponse) GetDid() string { 108 + if x != nil { 109 + return x.Did 110 + } 111 + return "" 112 + } 113 + 114 + type CreateProfileRequest struct { 115 + state protoimpl.MessageState `protogen:"open.v1"` 116 + Did string `protobuf:"bytes,1,opt,name=did,proto3" json:"did,omitempty"` 117 + unknownFields protoimpl.UnknownFields 118 + sizeCache protoimpl.SizeCache 119 + } 120 + 121 + func (x *CreateProfileRequest) Reset() { 122 + *x = CreateProfileRequest{} 123 + mi := &file_profile_proto_msgTypes[2] 124 + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 125 + ms.StoreMessageInfo(mi) 126 + } 127 + 128 + func (x *CreateProfileRequest) String() string { 129 + return protoimpl.X.MessageStringOf(x) 130 + } 131 + 132 + func (*CreateProfileRequest) ProtoMessage() {} 133 + 134 + func (x *CreateProfileRequest) ProtoReflect() protoreflect.Message { 135 + mi := &file_profile_proto_msgTypes[2] 136 + if x != nil { 137 + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 138 + if ms.LoadMessageInfo() == nil { 139 + ms.StoreMessageInfo(mi) 140 + } 141 + return ms 142 + } 143 + return mi.MessageOf(x) 144 + } 145 + 146 + // Deprecated: Use CreateProfileRequest.ProtoReflect.Descriptor instead. 147 + func (*CreateProfileRequest) Descriptor() ([]byte, []int) { 148 + return file_profile_proto_rawDescGZIP(), []int{2} 149 + } 150 + 151 + func (x *CreateProfileRequest) GetDid() string { 152 + if x != nil { 153 + return x.Did 154 + } 155 + return "" 156 + } 157 + 158 + type CreateProfileResponse struct { 159 + state protoimpl.MessageState `protogen:"open.v1"` 160 + Error *string `protobuf:"bytes,1,opt,name=error,proto3,oneof" json:"error,omitempty"` 161 + unknownFields protoimpl.UnknownFields 162 + sizeCache protoimpl.SizeCache 163 + } 164 + 165 + func (x *CreateProfileResponse) Reset() { 166 + *x = CreateProfileResponse{} 167 + mi := &file_profile_proto_msgTypes[3] 168 + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 169 + ms.StoreMessageInfo(mi) 170 + } 171 + 172 + func (x *CreateProfileResponse) String() string { 173 + return protoimpl.X.MessageStringOf(x) 174 + } 175 + 176 + func (*CreateProfileResponse) ProtoMessage() {} 177 + 178 + func (x *CreateProfileResponse) ProtoReflect() protoreflect.Message { 179 + mi := &file_profile_proto_msgTypes[3] 180 + if x != nil { 181 + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 182 + if ms.LoadMessageInfo() == nil { 183 + ms.StoreMessageInfo(mi) 184 + } 185 + return ms 186 + } 187 + return mi.MessageOf(x) 188 + } 189 + 190 + // Deprecated: Use CreateProfileResponse.ProtoReflect.Descriptor instead. 191 + func (*CreateProfileResponse) Descriptor() ([]byte, []int) { 192 + return file_profile_proto_rawDescGZIP(), []int{3} 193 + } 194 + 195 + func (x *CreateProfileResponse) GetError() string { 196 + if x != nil && x.Error != nil { 197 + return *x.Error 198 + } 199 + return "" 200 + } 201 + 202 + var File_profile_proto protoreflect.FileDescriptor 203 + 204 + const file_profile_proto_rawDesc = "" + 205 + "\n" + 206 + "\rprofile.proto\x12\rvyletdatabase\x1a\x1bbuf/validate/validate.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"-\n" + 207 + "\x11GetProfileRequest\x12\x18\n" + 208 + "\x03did\x18\x01 \x01(\tB\x06\xbaH\x03\xc8\x01\x01R\x03did\".\n" + 209 + "\x12GetProfileResponse\x12\x18\n" + 210 + "\x03did\x18\x01 \x01(\tB\x06\xbaH\x03\xc8\x01\x01R\x03did\"0\n" + 211 + "\x14CreateProfileRequest\x12\x18\n" + 212 + "\x03did\x18\x01 \x01(\tB\x06\xbaH\x03\xc8\x01\x01R\x03did\"<\n" + 213 + "\x15CreateProfileResponse\x12\x19\n" + 214 + "\x05error\x18\x01 \x01(\tH\x00R\x05error\x88\x01\x01B\b\n" + 215 + "\x06_error2\xbf\x01\n" + 216 + "\x0eProfileService\x12Q\n" + 217 + "\n" + 218 + "GetProfile\x12 .vyletdatabase.GetProfileRequest\x1a!.vyletdatabase.GetProfileResponse\x12Z\n" + 219 + "\rCreateProfile\x12#.vyletdatabase.CreateProfileRequest\x1a$.vyletdatabase.CreateProfileResponseB\x87\x01\n" + 220 + "\x11com.vyletdatabaseB\fProfileProtoP\x01Z\x10./;vyletdatabase\xa2\x02\x03VXX\xaa\x02\rVyletdatabase\xca\x02\rVyletdatabase\xe2\x02\x19Vyletdatabase\\GPBMetadata\xea\x02\rVyletdatabaseb\x06proto3" 221 + 222 + var ( 223 + file_profile_proto_rawDescOnce sync.Once 224 + file_profile_proto_rawDescData []byte 225 + ) 226 + 227 + func file_profile_proto_rawDescGZIP() []byte { 228 + file_profile_proto_rawDescOnce.Do(func() { 229 + file_profile_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_profile_proto_rawDesc), len(file_profile_proto_rawDesc))) 230 + }) 231 + return file_profile_proto_rawDescData 232 + } 233 + 234 + var file_profile_proto_msgTypes = make([]protoimpl.MessageInfo, 4) 235 + var file_profile_proto_goTypes = []any{ 236 + (*GetProfileRequest)(nil), // 0: vyletdatabase.GetProfileRequest 237 + (*GetProfileResponse)(nil), // 1: vyletdatabase.GetProfileResponse 238 + (*CreateProfileRequest)(nil), // 2: vyletdatabase.CreateProfileRequest 239 + (*CreateProfileResponse)(nil), // 3: vyletdatabase.CreateProfileResponse 240 + } 241 + var file_profile_proto_depIdxs = []int32{ 242 + 0, // 0: vyletdatabase.ProfileService.GetProfile:input_type -> vyletdatabase.GetProfileRequest 243 + 2, // 1: vyletdatabase.ProfileService.CreateProfile:input_type -> vyletdatabase.CreateProfileRequest 244 + 1, // 2: vyletdatabase.ProfileService.GetProfile:output_type -> vyletdatabase.GetProfileResponse 245 + 3, // 3: vyletdatabase.ProfileService.CreateProfile:output_type -> vyletdatabase.CreateProfileResponse 246 + 2, // [2:4] is the sub-list for method output_type 247 + 0, // [0:2] is the sub-list for method input_type 248 + 0, // [0:0] is the sub-list for extension type_name 249 + 0, // [0:0] is the sub-list for extension extendee 250 + 0, // [0:0] is the sub-list for field type_name 251 + } 252 + 253 + func init() { file_profile_proto_init() } 254 + func file_profile_proto_init() { 255 + if File_profile_proto != nil { 256 + return 257 + } 258 + file_profile_proto_msgTypes[3].OneofWrappers = []any{} 259 + type x struct{} 260 + out := protoimpl.TypeBuilder{ 261 + File: protoimpl.DescBuilder{ 262 + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), 263 + RawDescriptor: unsafe.Slice(unsafe.StringData(file_profile_proto_rawDesc), len(file_profile_proto_rawDesc)), 264 + NumEnums: 0, 265 + NumMessages: 4, 266 + NumExtensions: 0, 267 + NumServices: 1, 268 + }, 269 + GoTypes: file_profile_proto_goTypes, 270 + DependencyIndexes: file_profile_proto_depIdxs, 271 + MessageInfos: file_profile_proto_msgTypes, 272 + }.Build() 273 + File_profile_proto = out.File 274 + file_profile_proto_goTypes = nil 275 + file_profile_proto_depIdxs = nil 276 + }
+36
database/proto/profile.proto
··· 1 + syntax = "proto3"; 2 + 3 + package vyletdatabase; 4 + option go_package = "./;vyletdatabase"; 5 + 6 + import "buf/validate/validate.proto"; 7 + 8 + import "google/protobuf/timestamp.proto"; 9 + 10 + service ProfileService { 11 + rpc GetProfile(GetProfileRequest) returns (GetProfileResponse); 12 + rpc CreateProfile(CreateProfileRequest) returns (CreateProfileResponse); 13 + } 14 + 15 + message GetProfileRequest { 16 + string did = 1 [ 17 + (buf.validate.field).required = true 18 + ]; 19 + } 20 + 21 + message GetProfileResponse { 22 + string did = 1 [ 23 + (buf.validate.field).required = true 24 + ]; 25 + } 26 + 27 + message CreateProfileRequest { 28 + string did = 1 [ 29 + (buf.validate.field).required = true 30 + ]; 31 + } 32 + 33 + message CreateProfileResponse { 34 + optional string error = 1; 35 + } 36 +
+159
database/proto/profile_grpc.pb.go
··· 1 + // Code generated by protoc-gen-go-grpc. DO NOT EDIT. 2 + // versions: 3 + // - protoc-gen-go-grpc v1.6.0 4 + // - protoc (unknown) 5 + // source: profile.proto 6 + 7 + package vyletdatabase 8 + 9 + import ( 10 + context "context" 11 + grpc "google.golang.org/grpc" 12 + codes "google.golang.org/grpc/codes" 13 + status "google.golang.org/grpc/status" 14 + ) 15 + 16 + // This is a compile-time assertion to ensure that this generated file 17 + // is compatible with the grpc package it is being compiled against. 18 + // Requires gRPC-Go v1.64.0 or later. 19 + const _ = grpc.SupportPackageIsVersion9 20 + 21 + const ( 22 + ProfileService_GetProfile_FullMethodName = "/vyletdatabase.ProfileService/GetProfile" 23 + ProfileService_CreateProfile_FullMethodName = "/vyletdatabase.ProfileService/CreateProfile" 24 + ) 25 + 26 + // ProfileServiceClient is the client API for ProfileService service. 27 + // 28 + // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. 29 + type ProfileServiceClient interface { 30 + GetProfile(ctx context.Context, in *GetProfileRequest, opts ...grpc.CallOption) (*GetProfileResponse, error) 31 + CreateProfile(ctx context.Context, in *CreateProfileRequest, opts ...grpc.CallOption) (*CreateProfileResponse, error) 32 + } 33 + 34 + type profileServiceClient struct { 35 + cc grpc.ClientConnInterface 36 + } 37 + 38 + func NewProfileServiceClient(cc grpc.ClientConnInterface) ProfileServiceClient { 39 + return &profileServiceClient{cc} 40 + } 41 + 42 + func (c *profileServiceClient) GetProfile(ctx context.Context, in *GetProfileRequest, opts ...grpc.CallOption) (*GetProfileResponse, error) { 43 + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) 44 + out := new(GetProfileResponse) 45 + err := c.cc.Invoke(ctx, ProfileService_GetProfile_FullMethodName, in, out, cOpts...) 46 + if err != nil { 47 + return nil, err 48 + } 49 + return out, nil 50 + } 51 + 52 + func (c *profileServiceClient) CreateProfile(ctx context.Context, in *CreateProfileRequest, opts ...grpc.CallOption) (*CreateProfileResponse, error) { 53 + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) 54 + out := new(CreateProfileResponse) 55 + err := c.cc.Invoke(ctx, ProfileService_CreateProfile_FullMethodName, in, out, cOpts...) 56 + if err != nil { 57 + return nil, err 58 + } 59 + return out, nil 60 + } 61 + 62 + // ProfileServiceServer is the server API for ProfileService service. 63 + // All implementations must embed UnimplementedProfileServiceServer 64 + // for forward compatibility. 65 + type ProfileServiceServer interface { 66 + GetProfile(context.Context, *GetProfileRequest) (*GetProfileResponse, error) 67 + CreateProfile(context.Context, *CreateProfileRequest) (*CreateProfileResponse, error) 68 + mustEmbedUnimplementedProfileServiceServer() 69 + } 70 + 71 + // UnimplementedProfileServiceServer must be embedded to have 72 + // forward compatible implementations. 73 + // 74 + // NOTE: this should be embedded by value instead of pointer to avoid a nil 75 + // pointer dereference when methods are called. 76 + type UnimplementedProfileServiceServer struct{} 77 + 78 + func (UnimplementedProfileServiceServer) GetProfile(context.Context, *GetProfileRequest) (*GetProfileResponse, error) { 79 + return nil, status.Error(codes.Unimplemented, "method GetProfile not implemented") 80 + } 81 + func (UnimplementedProfileServiceServer) CreateProfile(context.Context, *CreateProfileRequest) (*CreateProfileResponse, error) { 82 + return nil, status.Error(codes.Unimplemented, "method CreateProfile not implemented") 83 + } 84 + func (UnimplementedProfileServiceServer) mustEmbedUnimplementedProfileServiceServer() {} 85 + func (UnimplementedProfileServiceServer) testEmbeddedByValue() {} 86 + 87 + // UnsafeProfileServiceServer may be embedded to opt out of forward compatibility for this service. 88 + // Use of this interface is not recommended, as added methods to ProfileServiceServer will 89 + // result in compilation errors. 90 + type UnsafeProfileServiceServer interface { 91 + mustEmbedUnimplementedProfileServiceServer() 92 + } 93 + 94 + func RegisterProfileServiceServer(s grpc.ServiceRegistrar, srv ProfileServiceServer) { 95 + // If the following call panics, it indicates UnimplementedProfileServiceServer was 96 + // embedded by pointer and is nil. This will cause panics if an 97 + // unimplemented method is ever invoked, so we test this at initialization 98 + // time to prevent it from happening at runtime later due to I/O. 99 + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { 100 + t.testEmbeddedByValue() 101 + } 102 + s.RegisterService(&ProfileService_ServiceDesc, srv) 103 + } 104 + 105 + func _ProfileService_GetProfile_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 106 + in := new(GetProfileRequest) 107 + if err := dec(in); err != nil { 108 + return nil, err 109 + } 110 + if interceptor == nil { 111 + return srv.(ProfileServiceServer).GetProfile(ctx, in) 112 + } 113 + info := &grpc.UnaryServerInfo{ 114 + Server: srv, 115 + FullMethod: ProfileService_GetProfile_FullMethodName, 116 + } 117 + handler := func(ctx context.Context, req interface{}) (interface{}, error) { 118 + return srv.(ProfileServiceServer).GetProfile(ctx, req.(*GetProfileRequest)) 119 + } 120 + return interceptor(ctx, in, info, handler) 121 + } 122 + 123 + func _ProfileService_CreateProfile_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 124 + in := new(CreateProfileRequest) 125 + if err := dec(in); err != nil { 126 + return nil, err 127 + } 128 + if interceptor == nil { 129 + return srv.(ProfileServiceServer).CreateProfile(ctx, in) 130 + } 131 + info := &grpc.UnaryServerInfo{ 132 + Server: srv, 133 + FullMethod: ProfileService_CreateProfile_FullMethodName, 134 + } 135 + handler := func(ctx context.Context, req interface{}) (interface{}, error) { 136 + return srv.(ProfileServiceServer).CreateProfile(ctx, req.(*CreateProfileRequest)) 137 + } 138 + return interceptor(ctx, in, info, handler) 139 + } 140 + 141 + // ProfileService_ServiceDesc is the grpc.ServiceDesc for ProfileService service. 142 + // It's only intended for direct use with grpc.RegisterService, 143 + // and not to be introspected or modified (even as a copy) 144 + var ProfileService_ServiceDesc = grpc.ServiceDesc{ 145 + ServiceName: "vyletdatabase.ProfileService", 146 + HandlerType: (*ProfileServiceServer)(nil), 147 + Methods: []grpc.MethodDesc{ 148 + { 149 + MethodName: "GetProfile", 150 + Handler: _ProfileService_GetProfile_Handler, 151 + }, 152 + { 153 + MethodName: "CreateProfile", 154 + Handler: _ProfileService_CreateProfile_Handler, 155 + }, 156 + }, 157 + Streams: []grpc.StreamDesc{}, 158 + Metadata: "profile.proto", 159 + }
+79
database/server/migrations.go
··· 1 + package server 2 + 3 + import ( 4 + "fmt" 5 + "log" 6 + 7 + "github.com/gocql/gocql" 8 + "github.com/golang-migrate/migrate/v4" 9 + "github.com/golang-migrate/migrate/v4/database/cassandra" 10 + _ "github.com/golang-migrate/migrate/v4/source/file" 11 + ) 12 + 13 + // RunMigrations applies database migrations 14 + func RunMigrations(session *gocql.Session, migrationsPath string) error { 15 + driver, err := cassandra.WithInstance(session, &cassandra.Config{ 16 + KeyspaceName: session.Query("").Consistency(gocql.One).GetConsistency().String(), 17 + }) 18 + if err != nil { 19 + return fmt.Errorf("failed to create migration driver: %w", err) 20 + } 21 + 22 + m, err := migrate.NewWithDatabaseInstance( 23 + fmt.Sprintf("file://%s", migrationsPath), 24 + "cassandra", 25 + driver, 26 + ) 27 + if err != nil { 28 + return fmt.Errorf("failed to create migrate instance: %w", err) 29 + } 30 + 31 + // Get current version 32 + version, dirty, err := m.Version() 33 + if err != nil && err != migrate.ErrNilVersion { 34 + return fmt.Errorf("failed to get migration version: %w", err) 35 + } 36 + 37 + if dirty { 38 + log.Printf("WARNING: Database is in dirty state at version %d", version) 39 + return fmt.Errorf("database is in dirty state, manual intervention required") 40 + } 41 + 42 + log.Printf("Current migration version: %d", version) 43 + 44 + // Run migrations 45 + if err := m.Up(); err != nil && err != migrate.ErrNoChange { 46 + return fmt.Errorf("failed to run migrations: %w", err) 47 + } 48 + 49 + newVersion, _, _ := m.Version() 50 + log.Printf("Migrations complete. Current version: %d", newVersion) 51 + 52 + return nil 53 + } 54 + 55 + // RollbackMigration rolls back the last migration 56 + func RollbackMigration(session *gocql.Session, migrationsPath string) error { 57 + driver, err := cassandra.WithInstance(session, &cassandra.Config{}) 58 + if err != nil { 59 + return fmt.Errorf("failed to create migration driver: %w", err) 60 + } 61 + 62 + m, err := migrate.NewWithDatabaseInstance( 63 + fmt.Sprintf("file://%s", migrationsPath), 64 + "cassandra", 65 + driver, 66 + ) 67 + if err != nil { 68 + return fmt.Errorf("failed to create migrate instance: %w", err) 69 + } 70 + 71 + if err := m.Steps(-1); err != nil { 72 + return fmt.Errorf("failed to rollback migration: %w", err) 73 + } 74 + 75 + version, _, _ := m.Version() 76 + log.Printf("Rolled back to version: %d", version) 77 + 78 + return nil 79 + }
+15
database/server/profile.go
··· 1 + package server 2 + 3 + import ( 4 + "context" 5 + 6 + vyletdatabase "github.com/vylet-app/go/database/proto" 7 + ) 8 + 9 + func (s *Server) CreateProfile(ctx context.Context, req *vyletdatabase.CreateProfileRequest) (*vyletdatabase.CreateProfileResponse, error) { 10 + return nil, nil 11 + } 12 + 13 + func (s *Server) GetProfile(ctx context.Context, req *vyletdatabase.GetProfileRequest) (*vyletdatabase.GetProfileResponse, error) { 14 + return nil, nil 15 + }
+176
database/server/server.go
··· 1 + package server 2 + 3 + import ( 4 + "context" 5 + "crypto/ecdsa" 6 + "crypto/elliptic" 7 + "crypto/rand" 8 + "crypto/tls" 9 + "crypto/x509" 10 + "crypto/x509/pkix" 11 + "fmt" 12 + "log/slog" 13 + "math/big" 14 + "net" 15 + "net/http" 16 + "os" 17 + "os/signal" 18 + "syscall" 19 + "time" 20 + 21 + "github.com/gocql/gocql" 22 + vyletdatabase "github.com/vylet-app/go/database/proto" 23 + "google.golang.org/grpc" 24 + "google.golang.org/grpc/credentials" 25 + ) 26 + 27 + const ( 28 + grpcTimeout = 10 * time.Minute 29 + ) 30 + 31 + type Server struct { 32 + vyletdatabase.UnimplementedProfileServiceServer 33 + 34 + logger *slog.Logger 35 + 36 + listenerAddr string 37 + grpcServer *grpc.Server 38 + 39 + cqlSession *gocql.Session 40 + 41 + cassandraAddrs []string 42 + cassandraKeyspace string 43 + } 44 + 45 + type Args struct { 46 + Logger *slog.Logger 47 + 48 + ListenAddr string 49 + 50 + CassandraAddrs []string 51 + CassandraKeyspace string 52 + } 53 + 54 + func New(args *Args) (*Server, error) { 55 + if args.Logger == nil { 56 + args.Logger = slog.Default() 57 + } 58 + 59 + logger := args.Logger 60 + 61 + certificate, err := GenerateTLSCertificate("localhost") 62 + if err != nil { 63 + return nil, fmt.Errorf("failed to generate TLS certificate: %w", err) 64 + } 65 + 66 + tlsConfig := &tls.Config{ 67 + Certificates: []tls.Certificate{*certificate}, 68 + MinVersion: tls.VersionTLS13, 69 + } 70 + creds := credentials.NewTLS(tlsConfig) 71 + 72 + grpcServer := grpc.NewServer( 73 + grpc.Creds(creds), 74 + grpc.MaxConcurrentStreams(100_000), 75 + grpc.ConnectionTimeout(grpcTimeout), 76 + ) 77 + 78 + cluster := gocql.NewCluster(args.CassandraAddrs...) 79 + cluster.Keyspace = args.CassandraKeyspace 80 + cluster.Consistency = gocql.Quorum 81 + cluster.ProtoVersion = 4 82 + cluster.ConnectTimeout = time.Second * 10 83 + cluster.Timeout = time.Second * 10 84 + 85 + session, err := cluster.CreateSession() 86 + if err != nil { 87 + return nil, fmt.Errorf("failed to connect to cassandra: %w", err) 88 + } 89 + 90 + server := Server{ 91 + logger: logger, 92 + 93 + cassandraAddrs: args.CassandraAddrs, 94 + cassandraKeyspace: args.CassandraKeyspace, 95 + 96 + cqlSession: session, 97 + 98 + grpcServer: grpcServer, 99 + } 100 + 101 + server.registerServices() 102 + 103 + return &server, nil 104 + } 105 + 106 + func (s *Server) Run(ctx context.Context) error { 107 + logger := s.logger.With("name", "Run") 108 + 109 + listener, err := net.Listen("tcp", s.listenerAddr) 110 + if err != nil { 111 + return fmt.Errorf("failed to listen: %w", err) 112 + } 113 + 114 + logger.Info("running gRPC server") 115 + 116 + grpcServerErr := make(chan error, 1) 117 + go func() { 118 + logger.Info("starting gRPC server") 119 + 120 + if err := s.grpcServer.Serve(listener); err != nil { 121 + if err != http.ErrServerClosed { 122 + logger.Error("gRPC server shutdown with error", "err", err) 123 + grpcServerErr <- err 124 + return 125 + } 126 + logger.Info("gRPC server shutdown") 127 + grpcServerErr <- nil 128 + } 129 + }() 130 + 131 + signals := make(chan os.Signal, 1) 132 + signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM) 133 + 134 + select { 135 + case sig := <-signals: 136 + logger.Info("received exit signal", "signal", sig) 137 + case <-ctx.Done(): 138 + logger.Info("context cancelled") 139 + case err := <-grpcServerErr: 140 + logger.Error("received grpc server error", "err", err) 141 + } 142 + 143 + s.grpcServer.GracefulStop() 144 + s.cqlSession.Close() 145 + 146 + logger.Info("gRPC server shut down") 147 + 148 + return nil 149 + } 150 + 151 + func (s *Server) registerServices() { 152 + vyletdatabase.RegisterProfileServiceServer(s.grpcServer, s) 153 + } 154 + 155 + func GenerateTLSCertificate(commonName string) (*tls.Certificate, error) { 156 + privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) 157 + if err != nil { 158 + return nil, err 159 + } 160 + template := x509.Certificate{ 161 + SerialNumber: big.NewInt(time.Now().UnixMilli()), 162 + Subject: pkix.Name{ 163 + CommonName: commonName, 164 + }, 165 + NotBefore: time.Now(), 166 + NotAfter: time.Now().Add(10 * 365 * 24 * time.Hour), 167 + } 168 + certificate, err := x509.CreateCertificate(rand.Reader, &template, &template, privateKey.Public(), privateKey) 169 + if err != nil { 170 + return nil, err 171 + } 172 + return &tls.Certificate{ 173 + Certificate: [][]byte{certificate}, 174 + PrivateKey: privateKey, 175 + }, nil 176 + }
+115
docker-compose.yaml
··· 1 + services: 2 + zookeeper: 3 + image: confluentinc/cp-zookeeper:7.6.0 4 + hostname: zookeeper 5 + container_name: zookeeper 6 + ports: 7 + - "2181:2181" 8 + environment: 9 + ZOOKEEPER_CLIENT_PORT: 2181 10 + ZOOKEEPER_TICK_TIME: 2000 11 + volumes: 12 + - zookeeper-data:/var/lib/zookeeper/data 13 + - zookeeper-logs:/var/lib/zookeeper/log 14 + 15 + kafka1: 16 + image: confluentinc/cp-kafka:7.6.0 17 + hostname: kafka1 18 + container_name: kafka1 19 + depends_on: 20 + - zookeeper 21 + ports: 22 + - "9092:9092" 23 + - "9101:9101" 24 + environment: 25 + KAFKA_BROKER_ID: 1 26 + KAFKA_ZOOKEEPER_CONNECT: 'zookeeper:2181' 27 + KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT 28 + KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka1:29092,PLAINTEXT_HOST://localhost:9092 29 + KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 3 30 + KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 2 31 + KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 3 32 + KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: 0 33 + KAFKA_JMX_PORT: 9101 34 + KAFKA_JMX_HOSTNAME: localhost 35 + KAFKA_AUTO_CREATE_TOPICS_ENABLE: 'true' 36 + volumes: 37 + - kafka1-data:/var/lib/kafka/data 38 + 39 + kafka2: 40 + image: confluentinc/cp-kafka:7.6.0 41 + hostname: kafka2 42 + container_name: kafka2 43 + depends_on: 44 + - zookeeper 45 + ports: 46 + - "9093:9093" 47 + - "9102:9102" 48 + environment: 49 + KAFKA_BROKER_ID: 2 50 + KAFKA_ZOOKEEPER_CONNECT: 'zookeeper:2181' 51 + KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT 52 + KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka2:29093,PLAINTEXT_HOST://localhost:9093 53 + KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 3 54 + KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 2 55 + KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 3 56 + KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: 0 57 + KAFKA_JMX_PORT: 9102 58 + KAFKA_JMX_HOSTNAME: localhost 59 + KAFKA_AUTO_CREATE_TOPICS_ENABLE: 'true' 60 + volumes: 61 + - kafka2-data:/var/lib/kafka/data 62 + 63 + kafka3: 64 + image: confluentinc/cp-kafka:7.6.0 65 + hostname: kafka3 66 + container_name: kafka3 67 + depends_on: 68 + - zookeeper 69 + ports: 70 + - "9094:9094" 71 + - "9103:9103" 72 + environment: 73 + KAFKA_BROKER_ID: 3 74 + KAFKA_ZOOKEEPER_CONNECT: 'zookeeper:2181' 75 + KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT 76 + KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka3:29094,PLAINTEXT_HOST://localhost:9094 77 + KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 3 78 + KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 2 79 + KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 3 80 + KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: 0 81 + KAFKA_JMX_PORT: 9103 82 + KAFKA_JMX_HOSTNAME: localhost 83 + KAFKA_AUTO_CREATE_TOPICS_ENABLE: 'true' 84 + volumes: 85 + - kafka3-data:/var/lib/kafka/data 86 + 87 + cassandra: 88 + image: cassandra:5.0 89 + hostname: cassandra 90 + container_name: cassandra 91 + ports: 92 + - "9042:9042" # CQL native transport port 93 + - "7000:7000" # Inter-node cluster communication 94 + - "7199:7199" # JMX monitoring port 95 + environment: 96 + CASSANDRA_CLUSTER_NAME: 'MyCluster' 97 + CASSANDRA_DC: 'dc1' 98 + CASSANDRA_RACK: 'rack1' 99 + CASSANDRA_ENDPOINT_SNITCH: 'GossipingPropertyFileSnitch' 100 + CASSANDRA_NUM_TOKENS: 256 101 + volumes: 102 + - cassandra-data:/var/lib/cassandra 103 + healthcheck: 104 + test: ["CMD-SHELL", "cqlsh -e 'describe cluster'"] 105 + interval: 30s 106 + timeout: 10s 107 + retries: 5 108 + 109 + volumes: 110 + zookeeper-data: 111 + zookeeper-logs: 112 + kafka1-data: 113 + kafka2-data: 114 + kafka3-data: 115 + cassandra-data:
+22 -11
go.mod
··· 6 6 buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.9-20250717185734-6c6e0d3c608e.1 7 7 github.com/bluesky-social/go-util v0.0.0-20251012040650-2ebbf57f5934 8 8 github.com/bluesky-social/indigo v0.0.0-20251206005924-d49b45419635 9 + github.com/gocql/gocql v1.7.0 10 + github.com/golang-migrate/migrate/v4 v4.19.1 9 11 github.com/gorilla/websocket v1.5.1 10 12 github.com/ipfs/go-cid v0.4.1 11 13 github.com/prometheus/client_golang v1.23.2 ··· 13 15 github.com/urfave/cli/v2 v2.27.7 14 16 github.com/whyrusleeping/cbor-gen v0.2.1-0.20241030202151-b7a6831be65e 15 17 golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 18 + google.golang.org/grpc v1.74.2 16 19 google.golang.org/protobuf v1.36.9 17 20 ) 18 21 ··· 24 27 github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect 25 28 github.com/earthboundkid/versioninfo/v2 v2.24.1 // indirect 26 29 github.com/felixge/httpsnoop v1.0.4 // indirect 27 - github.com/go-logr/logr v1.4.1 // indirect 30 + github.com/go-logr/logr v1.4.3 // indirect 28 31 github.com/go-logr/stdr v1.2.2 // indirect 29 32 github.com/gogo/protobuf v1.3.2 // indirect 33 + github.com/golang/snappy v0.0.4 // indirect 30 34 github.com/google/uuid v1.6.0 // indirect 35 + github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed // indirect 31 36 github.com/hashicorp/go-cleanhttp v0.5.2 // indirect 32 37 github.com/hashicorp/go-retryablehttp v0.7.7 // indirect 33 38 github.com/hashicorp/golang-lru v1.0.2 // indirect ··· 71 76 github.com/prometheus/client_model v0.6.2 // indirect 72 77 github.com/prometheus/common v0.66.1 // indirect 73 78 github.com/prometheus/procfs v0.16.1 // indirect 79 + github.com/puzpuzpuz/xsync/v3 v3.5.1 // indirect 74 80 github.com/rs/xid v1.6.0 // indirect 75 81 github.com/russross/blackfriday/v2 v2.1.0 // indirect 76 82 github.com/spaolacci/murmur3 v1.1.0 // indirect ··· 79 85 github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect 80 86 gitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b // indirect 81 87 gitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02 // indirect 82 - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect 83 - go.opentelemetry.io/otel v1.24.0 // indirect 84 - go.opentelemetry.io/otel/metric v1.24.0 // indirect 85 - go.opentelemetry.io/otel/trace v1.24.0 // indirect 88 + go.opentelemetry.io/auto/sdk v1.1.0 // indirect 89 + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect 90 + go.opentelemetry.io/otel v1.37.0 // indirect 91 + go.opentelemetry.io/otel/metric v1.37.0 // indirect 92 + go.opentelemetry.io/otel/trace v1.37.0 // indirect 86 93 go.uber.org/atomic v1.11.0 // indirect 87 94 go.uber.org/multierr v1.11.0 // indirect 88 95 go.uber.org/zap v1.26.0 // indirect 89 96 go.yaml.in/yaml/v2 v2.4.2 // indirect 90 - golang.org/x/crypto v0.41.0 // indirect 91 - golang.org/x/mod v0.14.0 // indirect 92 - golang.org/x/net v0.43.0 // indirect 93 - golang.org/x/sys v0.35.0 // indirect 94 - golang.org/x/time v0.6.0 // indirect 95 - golang.org/x/tools v0.15.0 // indirect 97 + golang.org/x/crypto v0.45.0 // indirect 98 + golang.org/x/mod v0.29.0 // indirect 99 + golang.org/x/net v0.47.0 // indirect 100 + golang.org/x/sync v0.18.0 // indirect 101 + golang.org/x/sys v0.38.0 // indirect 102 + golang.org/x/text v0.31.0 // indirect 103 + golang.org/x/time v0.12.0 // indirect 104 + golang.org/x/tools v0.38.0 // indirect 105 + google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c // indirect 106 + gopkg.in/inf.v0 v0.9.1 // indirect 96 107 gorm.io/gorm v1.25.9 // indirect 97 108 lukechampine.com/blake3 v1.2.1 // indirect 98 109 )
+91 -28
go.sum
··· 1 1 buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.9-20250717185734-6c6e0d3c608e.1 h1:u98oQG8CHYBrOWrYdqbyNpKz4Pw02ssv03DsTInnXn8= 2 2 buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.9-20250717185734-6c6e0d3c608e.1/go.mod h1:aY3zbkNan5F+cGm9lITDP6oxJIwu0dn9KjJuJjWaHkg= 3 + github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= 4 + github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= 3 5 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 6 + github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= 7 + github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= 4 8 github.com/RussellLuo/slidingwindow v0.0.0-20200528002341-535bb99d338b h1:5/++qT1/z812ZqBvqQt6ToRswSuPZ/B33m6xVHRzADU= 5 9 github.com/RussellLuo/slidingwindow v0.0.0-20200528002341-535bb99d338b/go.mod h1:4+EPqMRApwwE/6yo6CxiHoSnBzjRr3jsqer7frxP8y4= 6 10 github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= ··· 8 12 github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= 9 13 github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= 10 14 github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 15 + github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 h1:mXoPYz/Ul5HYEDvkta6I8/rnYM5gSdSV2tJ6XbZuEtY= 16 + github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k= 11 17 github.com/bluesky-social/go-util v0.0.0-20251012040650-2ebbf57f5934 h1:btHMur2kTRgWEnCHn6LaI3BE9YRgsqTpwpJ1UdB7VEk= 12 18 github.com/bluesky-social/go-util v0.0.0-20251012040650-2ebbf57f5934/go.mod h1:LWamyZfbQGW7PaVc5jumFfjgrshJ5mXgDUnR6fK7+BI= 13 19 github.com/bluesky-social/indigo v0.0.0-20251206005924-d49b45419635 h1:kNeRrgGJH2g5OvjLqtaQ744YXqduliZYpFkJ/ld47c0= 14 20 github.com/bluesky-social/indigo v0.0.0-20251206005924-d49b45419635/go.mod h1:Pm2I1+iDXn/hLbF7XCg/DsZi6uDCiOo7hZGWprSM7k0= 21 + github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= 22 + github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= 15 23 github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= 16 24 github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 17 25 github.com/confluentinc/confluent-kafka-go/v2 v2.8.0 h1:0HlcSNWg4LpLA9nIjzUMIqWHI+w0S68UN7alXAc3TeA= 18 26 github.com/confluentinc/confluent-kafka-go/v2 v2.8.0/go.mod h1:hScqtFIGUI1wqHIgM3mjoqEou4VweGGGX7dMpcUKves= 27 + github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= 28 + github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= 29 + github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE= 30 + github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= 19 31 github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 20 32 github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo= 21 33 github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= 22 34 github.com/cskr/pubsub v1.0.2 h1:vlOzMhl6PFn60gRlTQQsIfVwaPB/B/8MziK8FhEPt/0= 23 35 github.com/cskr/pubsub v1.0.2/go.mod h1:/8MzYXk/NJAz782G8RPkFzXTZVu63VotefPnR9TIRis= 24 36 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 25 - github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 26 37 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 38 + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= 39 + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 27 40 github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= 28 41 github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= 42 + github.com/dhui/dktest v0.4.6 h1:+DPKyScKSEp3VLtbMDHcUq6V5Lm5zfZZVb0Sk7Ahom4= 43 + github.com/dhui/dktest v0.4.6/go.mod h1:JHTSYDtKkvFNFHJKqCzVzqXecyv+tKt8EzceOmQOgbU= 44 + github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= 45 + github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= 46 + github.com/docker/docker v28.3.3+incompatible h1:Dypm25kh4rmk49v1eiVbsAtpAsYURjYkaKubwuBdxEI= 47 + github.com/docker/docker v28.3.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= 48 + github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= 49 + github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= 50 + github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= 51 + github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= 29 52 github.com/earthboundkid/versioninfo/v2 v2.24.1 h1:SJTMHaoUx3GzjjnUO1QzP3ZXK6Ee/nbWyCm58eY3oUg= 30 53 github.com/earthboundkid/versioninfo/v2 v2.24.1/go.mod h1:VcWEooDEuyUJnMfbdTh0uFN4cfEIg+kHMuWB2CDCLjw= 31 54 github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= ··· 35 58 github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= 36 59 github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= 37 60 github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= 38 - github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= 39 - github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= 61 + github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= 62 + github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= 40 63 github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= 41 64 github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= 42 65 github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg= 43 66 github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= 44 67 github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0= 68 + github.com/gocql/gocql v1.7.0 h1:O+7U7/1gSN7QTEAaMEsJc1Oq2QHXvCWoF3DFK9HDHus= 69 + github.com/gocql/gocql v1.7.0/go.mod h1:vnlvXyFZeLBF0Wy+RS8hrOdbn0UWsWtdg07XJnFxZ+4= 45 70 github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= 46 71 github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= 72 + github.com/golang-migrate/migrate/v4 v4.19.1 h1:OCyb44lFuQfYXYLx1SCxPZQGU7mcaZ7gH9yH4jSFbBA= 73 + github.com/golang-migrate/migrate/v4 v4.19.1/go.mod h1:CTcgfjxhaUtsLipnLoQRWCrjYXycRz/g5+RWDuYgPrE= 74 + github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= 75 + github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= 76 + github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 77 + github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= 78 + github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 47 79 github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= 48 80 github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= 49 81 github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= ··· 55 87 github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 56 88 github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= 57 89 github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= 90 + github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8= 91 + github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= 58 92 github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= 59 93 github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= 60 94 github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= ··· 154 188 github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 155 189 github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= 156 190 github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= 191 + github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= 192 + github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= 157 193 github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= 158 194 github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= 159 195 github.com/libp2p/go-cidranger v1.1.0 h1:ewPN8EZ0dd1LSnrtuwd4709PXVcITVeuwbag38yPW7c= ··· 185 221 github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= 186 222 github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= 187 223 github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= 224 + github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= 225 + github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= 226 + github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= 227 + github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= 228 + github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= 229 + github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= 188 230 github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= 189 231 github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= 190 232 github.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aGkbLYxPE= ··· 209 251 github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU= 210 252 github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= 211 253 github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= 254 + github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= 255 + github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= 256 + github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= 257 + github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= 212 258 github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= 213 259 github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= 214 260 github.com/petar/GoLLRB v0.0.0-20210522233825-ae3b015fd3e9 h1:1/WtZae0yGtPq+TI6+Tv1WTxkukpXeMlviSxvL7SRgk= ··· 216 262 github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU= 217 263 github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= 218 264 github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 219 - github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 265 + github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 266 + github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 220 267 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 268 + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= 269 + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 221 270 github.com/polydawn/refmt v0.89.1-0.20221221234430-40501e09de1f h1:VXTQfuJj9vKR4TCkEuWIckKvdHFeJH/huIFJ9/cXOB0= 222 271 github.com/polydawn/refmt v0.89.1-0.20221221234430-40501e09de1f/go.mod h1:/zvteZs/GwLtCgZ4BL6CBsk9IKIlexP43ObX9AxTqTw= 223 272 github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= ··· 231 280 github.com/puzpuzpuz/xsync/v3 v3.5.1 h1:GJYJZwO6IdxN/IKbneznS6yPkVC+c3zyY/j19c++5Fg= 232 281 github.com/puzpuzpuz/xsync/v3 v3.5.1/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA= 233 282 github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 234 - github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= 235 - github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= 283 + github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= 284 + github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= 236 285 github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU= 237 286 github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= 238 287 github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= ··· 279 328 gitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b/go.mod h1:/y/V339mxv2sZmYYR64O07VuCpdNZqCTwO8ZcouTMI8= 280 329 gitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02 h1:qwDnMxjkyLmAFgcfgTnfJrmYKWhHnci3GjDqcZp1M3Q= 281 330 gitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02/go.mod h1:JTnUj0mpYiAsuZLmKjTx/ex3AtMowcCgnE7YNyCEP0I= 282 - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= 283 - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= 284 - go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= 285 - go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= 286 - go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI= 287 - go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= 288 - go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI= 289 - go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= 331 + go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= 332 + go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= 333 + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus= 334 + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q= 335 + go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ= 336 + go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I= 337 + go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE= 338 + go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E= 339 + go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs= 340 + go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY= 341 + go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis= 342 + go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4= 343 + go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4= 344 + go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0= 290 345 go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= 291 346 go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= 292 347 go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= ··· 309 364 golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 310 365 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 311 366 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 312 - golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= 313 - golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= 367 + golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= 368 + golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= 314 369 golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 h1:hNQpMuAJe5CtcUqCXaWga3FHu+kQvCqcsoVaQgSV60o= 315 370 golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= 316 371 golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= ··· 318 373 golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 319 374 golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 320 375 golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 321 - golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= 322 - golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 376 + golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA= 377 + golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= 323 378 golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 324 379 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 325 380 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 326 381 golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 327 382 golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 328 383 golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= 329 - golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= 330 - golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= 384 + golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= 385 + golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= 331 386 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 332 387 golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 333 388 golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 334 389 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 335 - golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610= 336 - golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= 390 + golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= 391 + golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= 337 392 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 338 393 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 339 394 golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= ··· 343 398 golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 344 399 golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 345 400 golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 346 - golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= 347 - golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= 401 + golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= 402 + golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= 348 403 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 349 404 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 350 405 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 351 - golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= 352 - golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= 406 + golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= 407 + golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= 408 + golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= 409 + golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= 353 410 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 354 411 golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 355 412 golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= ··· 360 417 golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 361 418 golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 362 419 golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 363 - golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8= 364 - golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk= 420 + golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ= 421 + golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= 365 422 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 366 423 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 367 424 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 368 425 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 369 426 golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= 370 427 golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= 428 + google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c h1:qXWI/sQtv5UKboZ/zUk7h+mrf/lXORyI+n9DKDAusdg= 429 + google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c/go.mod h1:gw1tLEfykwDz2ET4a12jcXt4couGAm7IwsVaTy0Sflo= 430 + google.golang.org/grpc v1.74.2 h1:WoosgB65DlWVC9FqI82dGsZhWFNBSLjQ84bjROOpMu4= 431 + google.golang.org/grpc v1.74.2/go.mod h1:CtQ+BGjaAIXHs/5YS3i473GqwBBa1zGQNevxdeBEXrM= 371 432 google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw= 372 433 google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= 373 434 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= ··· 375 436 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 376 437 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 377 438 gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 439 + gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= 440 + gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= 378 441 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 379 442 gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 380 443 gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
+11
indexer/handler.go
··· 1 + package indexer 2 + 3 + import ( 4 + "context" 5 + 6 + vyletkafka "github.com/vylet-app/go/bus/proto" 7 + ) 8 + 9 + func (s *Server) handleEvent(ctx context.Context, evt *vyletkafka.FirehoseEvent) error { 10 + return nil 11 + }
+87
indexer/server.go
··· 1 + package indexer 2 + 3 + import ( 4 + "context" 5 + "fmt" 6 + "log/slog" 7 + "os" 8 + "os/signal" 9 + "syscall" 10 + "time" 11 + 12 + "github.com/bluesky-social/go-util/pkg/bus/consumer" 13 + vyletkafka "github.com/vylet-app/go/bus/proto" 14 + ) 15 + 16 + type Server struct { 17 + logger *slog.Logger 18 + 19 + consumer *consumer.Consumer[*vyletkafka.FirehoseEvent] 20 + } 21 + 22 + type Args struct { 23 + Logger *slog.Logger 24 + 25 + BootstrapServers []string 26 + InputTopic string 27 + ConsumerGroup string 28 + } 29 + 30 + func New(args *Args) (*Server, error) { 31 + if args.Logger == nil { 32 + args.Logger = slog.Default() 33 + } 34 + 35 + logger := args.Logger 36 + 37 + busConsumer, err := consumer.New( 38 + logger.With("component", "consumer"), 39 + args.BootstrapServers, 40 + args.InputTopic, 41 + args.ConsumerGroup, 42 + consumer.WithOffset[*vyletkafka.FirehoseEvent](consumer.OffsetStart), 43 + ) 44 + if err != nil { 45 + return nil, fmt.Errorf("failed to create new consumer: %w", err) 46 + } 47 + 48 + server := Server{ 49 + logger: logger, 50 + 51 + consumer: busConsumer, 52 + } 53 + 54 + return &server, nil 55 + } 56 + 57 + func (s *Server) Run(ctx context.Context) error { 58 + logger := s.logger.With("name", "Run") 59 + 60 + shutdownConsumer := make(chan struct{}, 1) 61 + consumerShutdown := make(chan struct{}, 1) 62 + 63 + go func() { 64 + 65 + }() 66 + 67 + signals := make(chan os.Signal, 1) 68 + signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM) 69 + 70 + select { 71 + case sig := <-signals: 72 + logger.Info("received exit signal", "signal", sig) 73 + close(shutdownConsumer) 74 + case <-ctx.Done(): 75 + logger.Info("context cancelled") 76 + close(shutdownConsumer) 77 + case <-consumerShutdown: 78 + logger.Warn("consumer shut down unexpectedly") 79 + } 80 + 81 + _, cancel := context.WithTimeout(context.Background(), 10*time.Second) 82 + defer cancel() 83 + 84 + s.consumer.Close() 85 + 86 + return nil 87 + }
+28
justfile
··· 2 2 3 3 lexdir := "../lexicons" 4 4 5 + default: 6 + @just --list 7 + 5 8 lexgen: 6 9 go run ./cmd/lexgen/ --build-file cmd/lexgen/vylet.json {{lexdir}} 7 10 8 11 cborgen: 9 12 go run ./gen 13 + 14 + migrate-up: 15 + go run ./cmd/database/migrate up 16 + 17 + migrate-down: 18 + go run ./cmd/database/migrate down 19 + 20 + migrate-create name: 21 + #!/usr/bin/env bash 22 + timestamp=$(date +%s) 23 + touch migrations/${timestamp}_{{name}}.up.cql 24 + touch migrations/${timestamp}_{{name}}.down.cql 25 + echo "Created migrations/${timestamp}_{{name}}.up.cql" 26 + echo "Created migrations/${timestamp}_{{name}}.down.cql" 27 + 28 + deps: 29 + go mod download 30 + go install google.golang.org/protobuf/cmd/protoc-gen-go@latest 31 + go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest 32 + 33 + cassandra-setup: 34 + ./scripts/setup-cassandra.sh 35 + 36 + cassandra-shell: 37 + docker exec -it cassandra cqlsh
+1
migrations/000001_create_profiles_table.down.cql
··· 1 + DROP TABLE IF EXISTS profiles;
+5
migrations/000001_create_profiles_table.up.cql
··· 1 + CREATE TABLE IF NOT EXISTS profiles ( 2 + did TEXT PRIMARY KEY, 3 + created_at TIMESTAMP, 4 + updated_at TIMESTAMP 5 + );
+9
scripts/init.cql
··· 1 + -- Create keyspace if it doesn't exist 2 + CREATE KEYSPACE IF NOT EXISTS vylet 3 + WITH replication = { 4 + 'class': 'SimpleStrategy', 5 + 'replication_factor': 1 6 + }; 7 + 8 + -- Use the keyspace 9 + USE vylet;
+30
scripts/setup-cassandra.sh
··· 1 + #!/bin/bash 2 + 3 + echo "Waiting for Cassandra to be ready..." 4 + max_attempts=30 5 + attempt=0 6 + 7 + while [ $attempt -lt $max_attempts ]; do 8 + if docker exec cassandra cqlsh -e "describe cluster" > /dev/null 2>&1; then 9 + echo "Cassandra is ready!" 10 + break 11 + fi 12 + attempt=$((attempt + 1)) 13 + echo "Attempt $attempt/$max_attempts - Cassandra not ready yet..." 14 + sleep 2 15 + done 16 + 17 + if [ $attempt -eq $max_attempts ]; then 18 + echo "Cassandra failed to start within expected time" 19 + exit 1 20 + fi 21 + 22 + echo "Running initialization script..." 23 + docker exec -i cassandra cqlsh < scripts/init.cql 24 + 25 + echo "Cassandra setup complete!" 26 + echo "" 27 + echo "You can verify with:" 28 + echo " docker exec -it cassandra cqlsh" 29 + echo " USE vylet;" 30 + echo " DESCRIBE TABLES;"