Skip to content

Commit

Permalink
minor: add restful and grpc server
Browse files Browse the repository at this point in the history
  • Loading branch information
jinrenjie committed Sep 8, 2024
1 parent 8fdc1e7 commit 49848ab
Show file tree
Hide file tree
Showing 13 changed files with 449 additions and 1 deletion.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,6 @@
.idea
.DS_Store
.focusly.yaml
spa/src/gen/*
docs/api/*
internal/gen/*
16 changes: 15 additions & 1 deletion api/routes/register.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
package routes

import (
"connectrpc.com/vanguard"
"github.com/betterde/focusly/docs"
"github.com/betterde/focusly/internal/gen/svc/v1/svcv1connect"
"github.com/betterde/focusly/internal/grpc/services"
"github.com/betterde/focusly/internal/journal"
"github.com/betterde/focusly/internal/response"
"github.com/betterde/focusly/internal/utils"
"github.com/betterde/focusly/spa"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/adaptor"
"github.com/gofiber/fiber/v2/middleware/filesystem"
"github.com/gofiber/swagger"
)
Expand All @@ -29,7 +35,15 @@ func RegisterRoutes(app *fiber.App) {
DocExpansion: "none",
})).Name("Swagger UI")

// Embed SPA static resource
userService := &services.UserService{}

path, handler := svcv1connect.NewUserServiceHandler(userService)
transcoder, err := vanguard.NewTranscoder([]*vanguard.Service{vanguard.NewService(path, handler)})
if err != nil {
journal.Logger.Panic(err)
}
app.Post("/v1/*", adaptor.HTTPHandler(utils.CamelToSnake(transcoder))).Name("User service route") // Embed SPA static resource

app.Get("*", filesystem.New(filesystem.Config{
Root: spa.Serve(),
Index: "index.html",
Expand Down
36 changes: 36 additions & 0 deletions buf.gen.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
version: v2

managed:
enabled: true
disable:
- file_option: go_package
module: buf.build/googleapis/googleapis
override:
- file_option: go_package_prefix
value: github.com/betterde/focusly/internal/gen

plugins:
- remote: buf.build/protocolbuffers/go
out: internal/gen
opt: paths=source_relative
- remote: buf.build/grpc/go
out: internal/gen
opt: paths=source_relative
- remote: buf.build/grpc-ecosystem/gateway
out: internal/gen
opt: paths=source_relative
- remote: buf.build/connectrpc/go:v1.12.0
out: internal/gen
opt: paths=source_relative
- local: protoc-gen-es
out: spa/src/gen
opt: target=ts
- local: protoc-gen-connect-es
out: spa/src/gen
opt: target=ts
- local: protoc-gen-connect-query
out: spa/src/gen
opt: target=ts
- remote: buf.build/community/google-gnostic-openapi:v0.7.0
out: docs/api
opt: paths=source_relative
6 changes: 6 additions & 0 deletions buf.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Generated by buf. DO NOT EDIT.
version: v2
deps:
- name: buf.build/googleapis/googleapis
commit: e7f8d366f5264595bcc4cd4139af9973
digest: b5:0cd69a689ee320ed815663d57d1bc3a1d6823224a7a717d46fee3a68197c25a6f5f932c0b0e49f8370c70c247a6635969a6a54af5345cafd51e0667298768aca
5 changes: 5 additions & 0 deletions buf.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
version: v2
deps:
- buf.build/googleapis/googleapis
modules:
- path: internal/proto
36 changes: 36 additions & 0 deletions internal/grpc/server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package grpc

import (
"errors"
"github.com/betterde/focusly/internal/gen/svc/v1/svcv1connect"
"github.com/betterde/focusly/internal/grpc/services"
"github.com/betterde/focusly/internal/journal"
"golang.org/x/net/http2"
"golang.org/x/net/http2/h2c"
"net/http"
)

var ServerInstance *http.Server

func Serve() {
mux := http.NewServeMux()
ServerInstance = &http.Server{
Addr: ":8080",
Handler: h2c.NewHandler(mux, &http2.Server{}),
}

userService := &services.UserService{}

path, handler := svcv1connect.NewUserServiceHandler(userService)
mux.Handle(path, handler)

go func() {
if err := ServerInstance.ListenAndServe(); err != nil {
if errors.Is(err, http.ErrServerClosed) {
journal.Logger.Info(err.Error())
} else {
journal.Logger.Error(err)
}
}
}()
}
27 changes: 27 additions & 0 deletions internal/grpc/services/user.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package services

import (
"connectrpc.com/connect"
"context"
"fmt"
svcv1 "github.com/betterde/focusly/internal/gen/svc/v1"
"github.com/betterde/focusly/internal/journal"
)

type UserService struct{}

func (svc *UserService) SignInWithSSOProvider(ctx context.Context, req *connect.Request[svcv1.SignInWithSSOProviderRequest]) (*connect.Response[svcv1.SignInWithSSOProviderResponse], error) {
journal.Logger.Infof("req: %+v", req)
res := connect.NewResponse(&svcv1.SignInWithSSOProviderResponse{
Payload: fmt.Sprintf("Your username is %s", req.Msg.Username),
})
res.Header().Set("User-Version", "v1")
return res, nil
}
func (svc *UserService) Echo(ctx context.Context, req *connect.Request[svcv1.EchoRequest]) (*connect.Response[svcv1.EchoResponse], error) {
res := connect.NewResponse(&svcv1.EchoResponse{
Payload: req.Msg.Payload,
})
res.Header().Set("User-Version", "v1")
return res, nil
}
52 changes: 52 additions & 0 deletions internal/journal/logger.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package journal

import (
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"os"
)

type Environment string

var (
Logger *zap.SugaredLogger
atomicLevel zap.AtomicLevel
)

func InitLogger() {
atomicLevel = zap.NewAtomicLevel()

encoderConfig := zapcore.EncoderConfig{
TimeKey: "ts",
NameKey: "logger",
LevelKey: "level",
CallerKey: "caller",
FunctionKey: zapcore.OmitKey,
MessageKey: "msg",
StacktraceKey: "stacktrace",
LineEnding: zapcore.DefaultLineEnding,
EncodeLevel: zapcore.LowercaseLevelEncoder,
EncodeTime: zapcore.ISO8601TimeEncoder,
EncodeDuration: zapcore.SecondsDurationEncoder,
EncodeCaller: zapcore.ShortCallerEncoder,
}

logger := zap.New(zapcore.NewCore(
zapcore.NewJSONEncoder(encoderConfig),
zapcore.Lock(os.Stdout),
atomicLevel,
))

Logger = logger.Sugar()
}

func SetLevel(lvl string) error {
level, err := zapcore.ParseLevel(lvl)
if err != nil {
return err
}

atomicLevel.SetLevel(level)

return nil
}
23 changes: 23 additions & 0 deletions internal/proto/svc/v1/project.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
syntax = "proto3";

package svc.v1;

import "google/api/annotations.proto";

message CreateProjectRequest {
string name = 1;
}

message CreateProjectResponse {
string id = 1;
string name = 2;
}

service ProjectService {
rpc CreateProject(CreateProjectRequest) returns (CreateProjectResponse) {
option (google.api.http) = {
post: "/v1/project",
body: "*",
};
}
}
46 changes: 46 additions & 0 deletions internal/proto/svc/v1/user.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
syntax = "proto3";

package svc.v1;

import "google/api/annotations.proto";

message User {
string id = 1;
string name = 2;
string role = 3;
string email = 4;
string avatar = 5;
}

message SignInWithSSOProviderRequest {
string username = 1;
string password = 2;
}

message SignInWithSSOProviderResponse {
string payload = 1;
}

message EchoRequest {
string payload = 1;
}

message EchoResponse {
string payload = 1;
}

service UserService {
rpc SignInWithSSOProvider(SignInWithSSOProviderRequest) returns (SignInWithSSOProviderResponse) {
option (google.api.http) = {
post: "/v1/auth/sign-in",
body: "*"
};
};

rpc Echo(EchoRequest) returns (EchoResponse) {
option (google.api.http) = {
post: "/v1/auth/echo",
body: "*"
};
};
}
83 changes: 83 additions & 0 deletions internal/response/responder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package response

import (
"net/http"
)

type (
Response struct {
Code int `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data"`
}
)

// Success Sending a successful response
func Success(message string, data interface{}) Response {
if data == nil {
return Response{
Code: http.StatusOK,
Message: message,
Data: struct{}{},
}
}

return Response{
Code: http.StatusOK,
Message: message,
Data: data,
}
}

// UnAuthenticated Authentication Failure
func UnAuthenticated(message string) Response {
return Response{
Code: http.StatusUnauthorized,
Message: message,
Data: struct{}{},
}
}

// NotFound Sending a not found response
func NotFound(message string) Response {
return Response{
Code: http.StatusNotFound,
Message: message,
Data: struct{}{},
}
}

// ValidationError Sending a validation error response
func ValidationError(message string, err error) Response {
return Response{
Code: http.StatusUnprocessableEntity,
Message: message,
Data: struct{}{},
}
}

// InternalServerError Sending a internal server error response
func InternalServerError(message string, err error) Response {
return Response{
Code: http.StatusInternalServerError,
Message: message,
Data: struct{}{},
}
}

// Send Sending a basic response
func Send(code int, message string, data interface{}) Response {
if data == nil {
return Response{
Code: code,
Message: message,
Data: struct{}{},
}
}

return Response{
Code: code,
Message: message,
Data: data,
}
}
24 changes: 24 additions & 0 deletions internal/utils/route.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package utils

import "net/http"

var camelToSnakeMapping = map[string]string{}

func CamelToSnake(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodGet {
q := r.URL.Query()
for camel, snake := range camelToSnakeMapping {
if q.Has(camel) {
v := q.Get(camel)
q.Del(camel)
q.Set(snake, v)
}
}

r.URL.RawQuery = q.Encode()
}

h.ServeHTTP(w, r)
})
}
Loading

0 comments on commit 49848ab

Please sign in to comment.