feat: implement get session by user
This commit is contained in:
4
db/sqlc/migrations/000001_init_schema.down.sql
Normal file
4
db/sqlc/migrations/000001_init_schema.down.sql
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
DROP TABLE IF EXISTS public.verification;
|
||||||
|
DROP TABLE IF EXISTS public.session;
|
||||||
|
DROP TABLE IF EXISTS public.account;
|
||||||
|
DROP TABLE IF EXISTS public."user";
|
||||||
52
db/sqlc/migrations/000001_init_schema.up.sql
Normal file
52
db/sqlc/migrations/000001_init_schema.up.sql
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS public."user" (
|
||||||
|
id text NOT NULL,
|
||||||
|
identifier text UNIQUE NOT NULL DEFAULT substr(encode(gen_random_bytes(16), 'hex'), 1, 32),
|
||||||
|
name text NOT NULL,
|
||||||
|
email text NOT NULL,
|
||||||
|
email_verified boolean DEFAULT false NOT NULL,
|
||||||
|
image text,
|
||||||
|
created_at timestamp without time zone DEFAULT now() NOT NULL,
|
||||||
|
updated_at timestamp without time zone DEFAULT now() NOT NULL,
|
||||||
|
CONSTRAINT user_pkey PRIMARY KEY (id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS public.account (
|
||||||
|
id text NOT NULL,
|
||||||
|
account_id text NOT NULL,
|
||||||
|
provider_id text NOT NULL,
|
||||||
|
user_id text NOT NULL,
|
||||||
|
access_token text,
|
||||||
|
refresh_token text,
|
||||||
|
id_token text,
|
||||||
|
access_token_expires_at timestamp without time zone,
|
||||||
|
refresh_token_expires_at timestamp without time zone,
|
||||||
|
scope text,
|
||||||
|
password text,
|
||||||
|
created_at timestamp without time zone DEFAULT now() NOT NULL,
|
||||||
|
updated_at timestamp without time zone NOT NULL,
|
||||||
|
CONSTRAINT account_pkey PRIMARY KEY (id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS public.session (
|
||||||
|
id text NOT NULL,
|
||||||
|
expires_at timestamp without time zone NOT NULL,
|
||||||
|
token text NOT NULL,
|
||||||
|
created_at timestamp without time zone DEFAULT now() NOT NULL,
|
||||||
|
updated_at timestamp without time zone NOT NULL,
|
||||||
|
ip_address text,
|
||||||
|
user_agent text,
|
||||||
|
user_id text NOT NULL,
|
||||||
|
CONSTRAINT session_pkey PRIMARY KEY (id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS public.verification (
|
||||||
|
id text NOT NULL,
|
||||||
|
identifier text NOT NULL,
|
||||||
|
value text NOT NULL,
|
||||||
|
expires_at timestamp without time zone NOT NULL,
|
||||||
|
created_at timestamp without time zone DEFAULT now() NOT NULL,
|
||||||
|
updated_at timestamp without time zone DEFAULT now() NOT NULL,
|
||||||
|
CONSTRAINT verification_pkey PRIMARY KEY (id)
|
||||||
|
);
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
DROP INDEX IF EXISTS idx_identifiers_created_at;
|
|
||||||
DROP INDEX IF EXISTS idx_identifiers_slug;
|
|
||||||
|
|
||||||
DROP TABLE IF EXISTS identifiers;
|
|
||||||
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
CREATE TABLE identifiers (
|
|
||||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
||||||
slug VARCHAR(255) UNIQUE NOT NULL,
|
|
||||||
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
|
|
||||||
updated_at TIMESTAMP NOT NULL DEFAULT NOW()
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE INDEX idx_identifiers_created_at
|
|
||||||
ON identifiers(created_at);
|
|
||||||
|
|
||||||
CREATE INDEX idx_identifiers_slug
|
|
||||||
ON identifiers(slug);
|
|
||||||
@@ -1,34 +1,6 @@
|
|||||||
-- name: CreateIdentifier :one
|
-- name: UserExistsByIdentifier :one
|
||||||
INSERT INTO identifiers (slug)
|
SELECT EXISTS (
|
||||||
VALUES ($1)
|
SELECT 1
|
||||||
RETURNING id, slug, created_at, updated_at;
|
FROM public."user"
|
||||||
|
WHERE identifier = $1
|
||||||
-- name: GetIdentifierById :one
|
) AS exists;
|
||||||
SELECT id, slug, created_at, updated_at
|
|
||||||
FROM identifiers
|
|
||||||
WHERE id = $1;
|
|
||||||
|
|
||||||
-- name: GetIdentifierBySlug :one
|
|
||||||
SELECT id, slug, created_at, updated_at
|
|
||||||
FROM identifiers
|
|
||||||
WHERE slug = $1;
|
|
||||||
|
|
||||||
-- name: ListIdentifiers :many
|
|
||||||
SELECT id, slug, created_at, updated_at
|
|
||||||
FROM identifiers
|
|
||||||
ORDER BY created_at DESC
|
|
||||||
LIMIT $1 OFFSET $2;
|
|
||||||
|
|
||||||
-- name: DeleteIdentifier :exec
|
|
||||||
DELETE FROM identifiers
|
|
||||||
WHERE id = $1;
|
|
||||||
|
|
||||||
-- name: UpdateIdentifierSlug :one
|
|
||||||
UPDATE identifiers
|
|
||||||
SET slug = $2, updated_at = NOW()
|
|
||||||
WHERE id = $1
|
|
||||||
RETURNING id, slug, created_at, updated_at;
|
|
||||||
|
|
||||||
-- name: CountIdentifiers :one
|
|
||||||
SELECT COUNT(*) FROM identifiers;
|
|
||||||
|
|
||||||
@@ -5,13 +5,52 @@
|
|||||||
package repository
|
package repository
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/google/uuid"
|
|
||||||
"github.com/jackc/pgx/v5/pgtype"
|
"github.com/jackc/pgx/v5/pgtype"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Identifier struct {
|
type Account struct {
|
||||||
ID uuid.UUID
|
ID string
|
||||||
Slug string
|
AccountID string
|
||||||
|
ProviderID string
|
||||||
|
UserID string
|
||||||
|
AccessToken pgtype.Text
|
||||||
|
RefreshToken pgtype.Text
|
||||||
|
IDToken pgtype.Text
|
||||||
|
AccessTokenExpiresAt pgtype.Timestamp
|
||||||
|
RefreshTokenExpiresAt pgtype.Timestamp
|
||||||
|
Scope pgtype.Text
|
||||||
|
Password pgtype.Text
|
||||||
|
CreatedAt pgtype.Timestamp
|
||||||
|
UpdatedAt pgtype.Timestamp
|
||||||
|
}
|
||||||
|
|
||||||
|
type Session struct {
|
||||||
|
ID string
|
||||||
|
ExpiresAt pgtype.Timestamp
|
||||||
|
Token string
|
||||||
|
CreatedAt pgtype.Timestamp
|
||||||
|
UpdatedAt pgtype.Timestamp
|
||||||
|
IpAddress pgtype.Text
|
||||||
|
UserAgent pgtype.Text
|
||||||
|
UserID string
|
||||||
|
}
|
||||||
|
|
||||||
|
type User struct {
|
||||||
|
ID string
|
||||||
|
Identifier string
|
||||||
|
Name string
|
||||||
|
Email string
|
||||||
|
EmailVerified bool
|
||||||
|
Image pgtype.Text
|
||||||
|
CreatedAt pgtype.Timestamp
|
||||||
|
UpdatedAt pgtype.Timestamp
|
||||||
|
}
|
||||||
|
|
||||||
|
type Verification struct {
|
||||||
|
ID string
|
||||||
|
Identifier string
|
||||||
|
Value string
|
||||||
|
ExpiresAt pgtype.Timestamp
|
||||||
CreatedAt pgtype.Timestamp
|
CreatedAt pgtype.Timestamp
|
||||||
UpdatedAt pgtype.Timestamp
|
UpdatedAt pgtype.Timestamp
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,142 +7,19 @@ package repository
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const countIdentifiers = `-- name: CountIdentifiers :one
|
const userExistsByIdentifier = `-- name: UserExistsByIdentifier :one
|
||||||
SELECT COUNT(*) FROM identifiers
|
SELECT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM public."user"
|
||||||
|
WHERE identifier = $1
|
||||||
|
) AS exists
|
||||||
`
|
`
|
||||||
|
|
||||||
func (q *Queries) CountIdentifiers(ctx context.Context) (int64, error) {
|
func (q *Queries) UserExistsByIdentifier(ctx context.Context, identifier string) (bool, error) {
|
||||||
row := q.db.QueryRow(ctx, countIdentifiers)
|
row := q.db.QueryRow(ctx, userExistsByIdentifier, identifier)
|
||||||
var count int64
|
var exists bool
|
||||||
err := row.Scan(&count)
|
err := row.Scan(&exists)
|
||||||
return count, err
|
return exists, err
|
||||||
}
|
|
||||||
|
|
||||||
const createIdentifier = `-- name: CreateIdentifier :one
|
|
||||||
INSERT INTO identifiers (slug)
|
|
||||||
VALUES ($1)
|
|
||||||
RETURNING id, slug, created_at, updated_at
|
|
||||||
`
|
|
||||||
|
|
||||||
func (q *Queries) CreateIdentifier(ctx context.Context, slug string) (Identifier, error) {
|
|
||||||
row := q.db.QueryRow(ctx, createIdentifier, slug)
|
|
||||||
var i Identifier
|
|
||||||
err := row.Scan(
|
|
||||||
&i.ID,
|
|
||||||
&i.Slug,
|
|
||||||
&i.CreatedAt,
|
|
||||||
&i.UpdatedAt,
|
|
||||||
)
|
|
||||||
return i, err
|
|
||||||
}
|
|
||||||
|
|
||||||
const deleteIdentifier = `-- name: DeleteIdentifier :exec
|
|
||||||
DELETE FROM identifiers
|
|
||||||
WHERE id = $1
|
|
||||||
`
|
|
||||||
|
|
||||||
func (q *Queries) DeleteIdentifier(ctx context.Context, id uuid.UUID) error {
|
|
||||||
_, err := q.db.Exec(ctx, deleteIdentifier, id)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
const getIdentifierById = `-- name: GetIdentifierById :one
|
|
||||||
SELECT id, slug, created_at, updated_at
|
|
||||||
FROM identifiers
|
|
||||||
WHERE id = $1
|
|
||||||
`
|
|
||||||
|
|
||||||
func (q *Queries) GetIdentifierById(ctx context.Context, id uuid.UUID) (Identifier, error) {
|
|
||||||
row := q.db.QueryRow(ctx, getIdentifierById, id)
|
|
||||||
var i Identifier
|
|
||||||
err := row.Scan(
|
|
||||||
&i.ID,
|
|
||||||
&i.Slug,
|
|
||||||
&i.CreatedAt,
|
|
||||||
&i.UpdatedAt,
|
|
||||||
)
|
|
||||||
return i, err
|
|
||||||
}
|
|
||||||
|
|
||||||
const getIdentifierBySlug = `-- name: GetIdentifierBySlug :one
|
|
||||||
SELECT id, slug, created_at, updated_at
|
|
||||||
FROM identifiers
|
|
||||||
WHERE slug = $1
|
|
||||||
`
|
|
||||||
|
|
||||||
func (q *Queries) GetIdentifierBySlug(ctx context.Context, slug string) (Identifier, error) {
|
|
||||||
row := q.db.QueryRow(ctx, getIdentifierBySlug, slug)
|
|
||||||
var i Identifier
|
|
||||||
err := row.Scan(
|
|
||||||
&i.ID,
|
|
||||||
&i.Slug,
|
|
||||||
&i.CreatedAt,
|
|
||||||
&i.UpdatedAt,
|
|
||||||
)
|
|
||||||
return i, err
|
|
||||||
}
|
|
||||||
|
|
||||||
const listIdentifiers = `-- name: ListIdentifiers :many
|
|
||||||
SELECT id, slug, created_at, updated_at
|
|
||||||
FROM identifiers
|
|
||||||
ORDER BY created_at DESC
|
|
||||||
LIMIT $1 OFFSET $2
|
|
||||||
`
|
|
||||||
|
|
||||||
type ListIdentifiersParams struct {
|
|
||||||
Limit int32
|
|
||||||
Offset int32
|
|
||||||
}
|
|
||||||
|
|
||||||
func (q *Queries) ListIdentifiers(ctx context.Context, arg ListIdentifiersParams) ([]Identifier, error) {
|
|
||||||
rows, err := q.db.Query(ctx, listIdentifiers, arg.Limit, arg.Offset)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
var items []Identifier
|
|
||||||
for rows.Next() {
|
|
||||||
var i Identifier
|
|
||||||
if err := rows.Scan(
|
|
||||||
&i.ID,
|
|
||||||
&i.Slug,
|
|
||||||
&i.CreatedAt,
|
|
||||||
&i.UpdatedAt,
|
|
||||||
); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
items = append(items, i)
|
|
||||||
}
|
|
||||||
if err := rows.Err(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return items, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
const updateIdentifierSlug = `-- name: UpdateIdentifierSlug :one
|
|
||||||
UPDATE identifiers
|
|
||||||
SET slug = $2, updated_at = NOW()
|
|
||||||
WHERE id = $1
|
|
||||||
RETURNING id, slug, created_at, updated_at
|
|
||||||
`
|
|
||||||
|
|
||||||
type UpdateIdentifierSlugParams struct {
|
|
||||||
ID uuid.UUID
|
|
||||||
Slug string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (q *Queries) UpdateIdentifierSlug(ctx context.Context, arg UpdateIdentifierSlugParams) (Identifier, error) {
|
|
||||||
row := q.db.QueryRow(ctx, updateIdentifierSlug, arg.ID, arg.Slug)
|
|
||||||
var i Identifier
|
|
||||||
err := row.Scan(
|
|
||||||
&i.ID,
|
|
||||||
&i.Slug,
|
|
||||||
&i.CreatedAt,
|
|
||||||
&i.UpdatedAt,
|
|
||||||
)
|
|
||||||
return i, err
|
|
||||||
}
|
}
|
||||||
|
|||||||
154
server/server.go
154
server/server.go
@@ -30,17 +30,92 @@ type Server struct {
|
|||||||
Subscribers map[string]*Subscriber
|
Subscribers map[string]*Subscriber
|
||||||
mu *sync.RWMutex
|
mu *sync.RWMutex
|
||||||
authToken string
|
authToken string
|
||||||
|
broadcastChan chan *proto.Controller
|
||||||
|
broadcastResultChan chan []SubscriberResult
|
||||||
|
notifyAllCancel context.CancelFunc
|
||||||
proto.UnimplementedEventServiceServer
|
proto.UnimplementedEventServiceServer
|
||||||
proto.UnimplementedSlugChangeServer
|
proto.UnimplementedSlugChangeServer
|
||||||
|
proto.UnimplementedUserServiceServer
|
||||||
|
proto.UnimplementedUserSessionsServer
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(database *repository.Queries, authToken string) *Server {
|
func New(database *repository.Queries, authToken string) *Server {
|
||||||
return &Server{
|
broadcastChan := make(chan *proto.Controller, 10)
|
||||||
|
broadcastResultChan := make(chan []SubscriberResult, 10)
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
|
srv := &Server{
|
||||||
Database: database,
|
Database: database,
|
||||||
Subscribers: make(map[string]*Subscriber),
|
Subscribers: make(map[string]*Subscriber),
|
||||||
mu: new(sync.RWMutex),
|
mu: new(sync.RWMutex),
|
||||||
authToken: authToken,
|
authToken: authToken,
|
||||||
|
broadcastChan: broadcastChan,
|
||||||
|
broadcastResultChan: broadcastResultChan,
|
||||||
|
notifyAllCancel: cancel,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
go srv.notifyAllSubscriber(ctx, broadcastChan, broadcastResultChan)
|
||||||
|
|
||||||
|
return srv
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) GetSession(ctx context.Context, req *proto.GetSessionRequest) (*proto.GetSessionsResponse, error) {
|
||||||
|
if req == nil {
|
||||||
|
return nil, status.Error(codes.InvalidArgument, "request is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
controllerReq := &proto.Controller{
|
||||||
|
Type: proto.EventType_GET_SESSIONS,
|
||||||
|
Payload: &proto.Controller_GetSessionsEvent{
|
||||||
|
GetSessionsEvent: &proto.GetSessionsEvent{
|
||||||
|
Identity: req.GetIdentity(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, ctx.Err()
|
||||||
|
case s.broadcastChan <- controllerReq:
|
||||||
|
}
|
||||||
|
|
||||||
|
var results []SubscriberResult
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, ctx.Err()
|
||||||
|
case results = <-s.broadcastResultChan:
|
||||||
|
}
|
||||||
|
responses := make([]*proto.Detail, 0, len(results))
|
||||||
|
for _, result := range results {
|
||||||
|
for _, detail := range result.Response.Payload.(*proto.Client_GetSessionsEvent).GetSessionsEvent.Details {
|
||||||
|
responses = append(responses, detail)
|
||||||
|
}
|
||||||
|
responses = append(responses)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(responses) == 0 {
|
||||||
|
return nil, status.Error(codes.NotFound, "no subscriber responded")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &proto.GetSessionsResponse{Details: responses}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) Check(ctx context.Context, request *proto.CheckRequest) (*proto.CheckResponse, error) {
|
||||||
|
exist, err := s.Database.UserExistsByIdentifier(ctx, request.GetAuthToken())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if exist {
|
||||||
|
return &proto.CheckResponse{
|
||||||
|
Response: proto.AuthorizationResponse_MESSAGE_TYPE_AUTHORIZED,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &proto.CheckResponse{
|
||||||
|
Response: proto.AuthorizationResponse_MESSAGE_TYPE_UNAUTHORIZED,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) Subscribe(event grpc.BidiStreamingServer[proto.Client, proto.Controller]) error {
|
func (s *Server) Subscribe(event grpc.BidiStreamingServer[proto.Client, proto.Controller]) error {
|
||||||
@@ -124,6 +199,21 @@ func processEventStream(ctx context.Context, requestChan *Subscriber, event grpc
|
|||||||
case requestChan.client <- recv:
|
case requestChan.client <- recv:
|
||||||
}
|
}
|
||||||
log.Printf("Received slug change event: %v", recv)
|
log.Printf("Received slug change event: %v", recv)
|
||||||
|
case proto.EventType_GET_SESSIONS:
|
||||||
|
log.Printf("Processing session event")
|
||||||
|
if err := event.Send(request); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
recv, err := event.Recv()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
case requestChan.client <- recv:
|
||||||
|
}
|
||||||
|
log.Printf("Received SESSIONS event: %v", recv)
|
||||||
default:
|
default:
|
||||||
log.Printf("Unknown event type: %v", request.GetType())
|
log.Printf("Unknown event type: %v", request.GetType())
|
||||||
}
|
}
|
||||||
@@ -220,6 +310,66 @@ func (s *Server) RequestChangeSlug(ctx context.Context, request *proto.ChangeSlu
|
|||||||
return (*proto.ChangeSlugResponse)(response.SlugEventResponse), nil
|
return (*proto.ChangeSlugResponse)(response.SlugEventResponse), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SubscriberResult struct {
|
||||||
|
Identity string
|
||||||
|
Response *proto.Client
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) notifyAllSubscriber(ctx context.Context, recvChan <-chan *proto.Controller, resultChan chan<- []SubscriberResult) {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
case controllerReq, ok := <-recvChan:
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if controllerReq == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
s.mu.RLock()
|
||||||
|
subs := make(map[string]*Subscriber, len(s.Subscribers))
|
||||||
|
for id, sub := range s.Subscribers {
|
||||||
|
subs[id] = sub
|
||||||
|
}
|
||||||
|
s.mu.RUnlock()
|
||||||
|
results := make([]SubscriberResult, 0, len(subs))
|
||||||
|
for id, sub := range subs {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
case <-sub.done:
|
||||||
|
results = append(results, SubscriberResult{Identity: id, Err: status.Error(codes.Canceled, "subscriber removed")})
|
||||||
|
continue
|
||||||
|
case sub.controller <- controllerReq:
|
||||||
|
default:
|
||||||
|
results = append(results, SubscriberResult{Identity: id, Err: status.Error(codes.Unavailable, "controller channel blocked")})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
case <-sub.done:
|
||||||
|
results = append(results, SubscriberResult{Identity: id, Err: status.Error(codes.Canceled, "subscriber removed")})
|
||||||
|
case resp, ok := <-sub.client:
|
||||||
|
if !ok {
|
||||||
|
results = append(results, SubscriberResult{Identity: id, Err: status.Error(codes.FailedPrecondition, "client channel closed")})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
results = append(results, SubscriberResult{Identity: id, Response: resp})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
case resultChan <- results:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Server) ListenAndServe(Addr string) error {
|
func (s *Server) ListenAndServe(Addr string) error {
|
||||||
listener, err := net.Listen("tcp", Addr)
|
listener, err := net.Listen("tcp", Addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -246,6 +396,8 @@ func (s *Server) ListenAndServe(Addr string) error {
|
|||||||
|
|
||||||
proto.RegisterSlugChangeServer(grpcServer, s)
|
proto.RegisterSlugChangeServer(grpcServer, s)
|
||||||
proto.RegisterEventServiceServer(grpcServer, s)
|
proto.RegisterEventServiceServer(grpcServer, s)
|
||||||
|
proto.RegisterUserServiceServer(grpcServer, s)
|
||||||
|
proto.RegisterUserSessionsServer(grpcServer, s)
|
||||||
|
|
||||||
healthServer := health.NewServer()
|
healthServer := health.NewServer()
|
||||||
grpc_health_v1.RegisterHealthServer(grpcServer, healthServer)
|
grpc_health_v1.RegisterHealthServer(grpcServer, healthServer)
|
||||||
|
|||||||
Reference in New Issue
Block a user