-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: implement a new Push Notification service that sends push notif…
… on PostReply transactions Signed-off-by: D4ryl00 <[email protected]>
- Loading branch information
Showing
24 changed files
with
2,777 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
build | ||
|
||
# default database path | ||
data.db |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
buf 1.30.1 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
FROM golang:1.21-alpine AS builder | ||
|
||
COPY . /app | ||
|
||
WORKDIR /app | ||
|
||
RUN go build -o notification . | ||
|
||
|
||
FROM alpine | ||
|
||
COPY --from=builder /app/notification /usr/local/bin/notification | ||
|
||
ENTRYPOINT [ "/usr/local/bin/notification" ] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
src := $(wildcard ./*.go) go.mod go.sum | ||
|
||
#### API #### | ||
|
||
api/gen: api/notificationservice.proto | ||
buf generate api | ||
touch api/gen | ||
|
||
api.build: api/gen | ||
.PHONY: api.build | ||
|
||
api.push: api.build | ||
buf push api | ||
.PHONY: api.push | ||
|
||
#### BUILD #### | ||
|
||
build/notification: $(src) | ||
mkdir -p build | ||
go build -o build/notification . | ||
|
||
build: build/notification | ||
.PHONY: build | ||
|
||
#### CLEAN #### | ||
|
||
clean: | ||
rm -rf build |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
package main | ||
|
||
import ( | ||
"bytes" | ||
"context" | ||
"encoding/gob" | ||
"time" | ||
|
||
"connectrpc.com/connect" | ||
"go.uber.org/zap" | ||
|
||
api_gen "github.com/gnolang/dsocial/tools/notification-service/api/gen/go" | ||
"github.com/pkg/errors" | ||
) | ||
|
||
func (s *notificationService) RegisterDevice(ctx context.Context, req *connect.Request[api_gen.RegisterDeviceRequest]) (*connect.Response[api_gen.RegisterDeviceResponse], error) { | ||
s.logger.Debug("RegisterDevice called", zap.String("address", req.Msg.Address), zap.String("token", req.Msg.Token)) | ||
|
||
if req.Msg.Address == "" || req.Msg.Token == "" { | ||
s.logger.Error("RegisterDevice error: missing input", zap.String("address", req.Msg.Address), zap.String("token", req.Msg.Token)) | ||
return nil, api_gen.ErrCode_ErrMissingInput | ||
} | ||
|
||
if err := s.registerDevice(req.Msg.Address, req.Msg.Token); err != nil { | ||
s.logger.Error("RegisterDevice error: failed to register device", zap.Error(err)) | ||
return nil, api_gen.ErrCode_ErrInternal | ||
} | ||
|
||
s.logger.Debug("RegisterDevice returned ok") | ||
|
||
return connect.NewResponse(&api_gen.RegisterDeviceResponse{}), nil | ||
} | ||
|
||
func (s *notificationService) registerDevice(address string, token string) error { | ||
var tokens []string | ||
var err error | ||
|
||
if tokens, err = s.getPushTokens(address); err != nil { | ||
return errors.Wrap(err, "registerDevice error: getPushToken") | ||
} | ||
|
||
for _, t := range tokens { | ||
if t == token { | ||
s.logger.Info("registerDevice: token already exists", zap.String("address", address), zap.String("token", token)) | ||
return nil | ||
} | ||
} | ||
|
||
tokens = append(tokens, token) | ||
|
||
var buffer bytes.Buffer | ||
enc := gob.NewEncoder(&buffer) | ||
if err := enc.Encode(tokens); err != nil { | ||
return errors.Wrap(err, "registerDevice error: encode") | ||
} | ||
|
||
if err := s.db.Set([]byte(address), buffer.Bytes()); err != nil { | ||
return errors.Wrap(err, "registerDevice error: DB.Set") | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// getPushTokens returns the push tokens slice for the given address | ||
// If the address has no tokens, it returns an empty string slice | ||
func (s *notificationService) getPushTokens(address string) ([]string, error) { | ||
rawTokens, err := s.db.Get([]byte(address)) | ||
if err != nil { | ||
return nil, errors.Wrap(err, "getPushTokens error: DB.Get") | ||
} | ||
|
||
// handle empty tokens | ||
if len(rawTokens) == 0 { | ||
return []string{}, nil | ||
} | ||
|
||
buffer := bytes.NewBuffer(rawTokens) | ||
var tokens []string | ||
|
||
dec := gob.NewDecoder(buffer) | ||
if err := dec.Decode(&tokens); err != nil { | ||
return nil, errors.Wrap(err, "getPushTokens error: decode") | ||
} | ||
|
||
return tokens, nil | ||
} | ||
|
||
// Hello is for debug purposes | ||
func (s *notificationService) Hello(ctx context.Context, req *connect.Request[api_gen.HelloRequest]) (*connect.Response[api_gen.HelloResponse], error) { | ||
s.logger.Debug("Hello called") | ||
defer s.logger.Debug("Hello returned ok") | ||
return connect.NewResponse(&api_gen.HelloResponse{ | ||
Greeting: "Hello " + req.Msg.Name, | ||
}), nil | ||
} | ||
|
||
// HelloStream is for debug purposes | ||
func (s *notificationService) HelloStream(ctx context.Context, req *connect.Request[api_gen.HelloStreamRequest], stream *connect.ServerStream[api_gen.HelloStreamResponse]) error { | ||
s.logger.Debug("HelloStream called") | ||
for i := 0; i < 4; i++ { | ||
if err := stream.Send(&api_gen.HelloStreamResponse{ | ||
Greeting: "Hello " + req.Msg.Name, | ||
}); err != nil { | ||
s.logger.Error("HelloStream returned error", zap.Error(err)) | ||
return err | ||
} | ||
time.Sleep(2 * time.Second) | ||
} | ||
|
||
s.logger.Debug("HelloStream returned ok") | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
version: v1 | ||
name: buf.build/gnolang/dsocial-notification | ||
breaking: | ||
use: | ||
- FILE | ||
lint: | ||
use: | ||
- DEFAULT |
50 changes: 50 additions & 0 deletions
50
tools/notification-service/api/gen/es/notificationservice_connect.d.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
// @generated by protoc-gen-connect-es v1.4.0 | ||
// @generated from file notificationservice.proto (package land.gno.dsocial.notificationservice.v1, syntax proto3) | ||
/* eslint-disable */ | ||
// @ts-nocheck | ||
|
||
import { HelloRequest, HelloResponse, HelloStreamRequest, HelloStreamResponse, RegisterDeviceRequest, RegisterDeviceResponse } from "./notificationservice_pb.js"; | ||
import { MethodKind } from "@bufbuild/protobuf"; | ||
|
||
/** | ||
* NotificationService is the service to manage Push Notifications in your app. | ||
* | ||
* @generated from service land.gno.dsocial.notificationservice.v1.NotificationService | ||
*/ | ||
export declare const NotificationService: { | ||
readonly typeName: "land.gno.dsocial.notificationservice.v1.NotificationService", | ||
readonly methods: { | ||
/** | ||
* @generated from rpc land.gno.dsocial.notificationservice.v1.NotificationService.RegisterDevice | ||
*/ | ||
readonly registerDevice: { | ||
readonly name: "RegisterDevice", | ||
readonly I: typeof RegisterDeviceRequest, | ||
readonly O: typeof RegisterDeviceResponse, | ||
readonly kind: MethodKind.Unary, | ||
}, | ||
/** | ||
* Hello is for debug purposes | ||
* | ||
* @generated from rpc land.gno.dsocial.notificationservice.v1.NotificationService.Hello | ||
*/ | ||
readonly hello: { | ||
readonly name: "Hello", | ||
readonly I: typeof HelloRequest, | ||
readonly O: typeof HelloResponse, | ||
readonly kind: MethodKind.Unary, | ||
}, | ||
/** | ||
* HelloStream is for debug purposes | ||
* | ||
* @generated from rpc land.gno.dsocial.notificationservice.v1.NotificationService.HelloStream | ||
*/ | ||
readonly helloStream: { | ||
readonly name: "HelloStream", | ||
readonly I: typeof HelloStreamRequest, | ||
readonly O: typeof HelloStreamResponse, | ||
readonly kind: MethodKind.ServerStreaming, | ||
}, | ||
} | ||
}; | ||
|
50 changes: 50 additions & 0 deletions
50
tools/notification-service/api/gen/es/notificationservice_connect.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
// @generated by protoc-gen-connect-es v1.4.0 | ||
// @generated from file notificationservice.proto (package land.gno.dsocial.notificationservice.v1, syntax proto3) | ||
/* eslint-disable */ | ||
// @ts-nocheck | ||
|
||
import { HelloRequest, HelloResponse, HelloStreamRequest, HelloStreamResponse, RegisterDeviceRequest, RegisterDeviceResponse } from "./notificationservice_pb.js"; | ||
import { MethodKind } from "@bufbuild/protobuf"; | ||
|
||
/** | ||
* NotificationService is the service to manage Push Notifications in your app. | ||
* | ||
* @generated from service land.gno.dsocial.notificationservice.v1.NotificationService | ||
*/ | ||
export const NotificationService = { | ||
typeName: "land.gno.dsocial.notificationservice.v1.NotificationService", | ||
methods: { | ||
/** | ||
* @generated from rpc land.gno.dsocial.notificationservice.v1.NotificationService.RegisterDevice | ||
*/ | ||
registerDevice: { | ||
name: "RegisterDevice", | ||
I: RegisterDeviceRequest, | ||
O: RegisterDeviceResponse, | ||
kind: MethodKind.Unary, | ||
}, | ||
/** | ||
* Hello is for debug purposes | ||
* | ||
* @generated from rpc land.gno.dsocial.notificationservice.v1.NotificationService.Hello | ||
*/ | ||
hello: { | ||
name: "Hello", | ||
I: HelloRequest, | ||
O: HelloResponse, | ||
kind: MethodKind.Unary, | ||
}, | ||
/** | ||
* HelloStream is for debug purposes | ||
* | ||
* @generated from rpc land.gno.dsocial.notificationservice.v1.NotificationService.HelloStream | ||
*/ | ||
helloStream: { | ||
name: "HelloStream", | ||
I: HelloStreamRequest, | ||
O: HelloStreamResponse, | ||
kind: MethodKind.ServerStreaming, | ||
}, | ||
} | ||
}; | ||
|
Oops, something went wrong.