Skip to content
This repository was archived by the owner on Jul 15, 2023. It is now read-only.

Commit ca25774

Browse files
authored
Merge pull request #3 from iguagile/api
implement api server
2 parents 4a42656 + 8d62251 commit ca25774

File tree

9 files changed

+680
-154
lines changed

9 files changed

+680
-154
lines changed

Dockerfile

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
FROM golang:alpine AS build
2+
ENV GO111MODULE=on
3+
ENV GOOS=linux
4+
ENV CGO_ENABLED=0
5+
6+
7+
WORKDIR $GOPATH/src/iguagile/iguagile-api
8+
9+
COPY . .
10+
11+
12+
RUN go build -a -o out cli/main.go && \
13+
cp out /app
14+
15+
FROM alpine
16+
RUN apk add --no-cache tzdata ca-certificates
17+
COPY --from=build /app /app
18+
19+
EXPOSE 80
20+
21+
CMD ["/app"]

api.go

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
package api
2+
3+
import (
4+
"context"
5+
"log"
6+
"os"
7+
"sync"
8+
"time"
9+
10+
"github.com/gomodule/redigo/redis"
11+
"github.com/labstack/echo"
12+
"github.com/labstack/echo/middleware"
13+
)
14+
15+
// RoomAPIServer is room api server.
16+
type RoomAPIServer struct {
17+
// Address is room api server address.
18+
Address string
19+
20+
// BaseUri is base uri of room api.
21+
BaseUri string
22+
23+
// RedisHost is redis address.
24+
RedisHost string
25+
26+
// MaxUser is max value of room capacity.
27+
MaxUser int
28+
29+
ServerDeadLine time.Duration
30+
RoomDeadLine time.Duration
31+
Logger *log.Logger
32+
33+
serverManager *ServerManager
34+
roomManager *RoomManager
35+
}
36+
37+
const (
38+
defaultAddress = ":80"
39+
defaultBaseUri = "/api/v1"
40+
defaultRedisHost = ":6379"
41+
defaultMaxUser = 70
42+
defaultServerDeadline = time.Minute * 5
43+
defaultRoomDeadline = time.Minute * 5
44+
)
45+
46+
// NewRoomAPIServer is an instance of RoomAPIServer.
47+
func NewRoomAPIServer() *RoomAPIServer {
48+
return &RoomAPIServer{
49+
Address: defaultAddress,
50+
BaseUri: defaultBaseUri,
51+
RedisHost: defaultRedisHost,
52+
MaxUser: defaultMaxUser,
53+
ServerDeadLine: defaultServerDeadline,
54+
RoomDeadLine: defaultRoomDeadline,
55+
Logger: log.New(os.Stdout, "iguagile-room-api ", log.Lshortfile),
56+
serverManager: &ServerManager{servers: &sync.Map{}},
57+
roomManager: &RoomManager{rooms: &sync.Map{}},
58+
}
59+
}
60+
61+
// Server is room server information.
62+
type Server struct {
63+
Host string `json:"server"`
64+
Port int `json:"port"`
65+
ServerID int `json:"-"`
66+
Load int `json:"-"`
67+
APIPort int `json:"-"`
68+
Token []byte `json:"-"`
69+
updated time.Time `json:"-"`
70+
}
71+
72+
// Room is room information.
73+
type Room struct {
74+
RoomID int `json:"room_id"`
75+
RequirePassword bool `json:"require_password"`
76+
MaxUser int `json:"max_user"`
77+
ConnectedUser int `json:"connected_user"`
78+
Server Server `json:"server"`
79+
Token string `json:"token"`
80+
Information map[string]string `json:"information"`
81+
ApplicationName string `json:"-"`
82+
Version string `json:"-"`
83+
updated time.Time `json:"-"`
84+
}
85+
86+
// RoomAPIResponse is api response.
87+
type RoomAPIResponse struct {
88+
Success bool `json:"success"`
89+
Result interface{} `json:"result"`
90+
Error string `json:"error"`
91+
}
92+
93+
// CreateRoomRequest is api request.
94+
type CreateRoomRequest struct {
95+
ApplicationName string `json:"application_name"`
96+
Version string `json:"version"`
97+
Password string `json:"password"`
98+
MaxUser int `json:"max_user"`
99+
Information map[string]string `json:"information"`
100+
}
101+
102+
const iguagileAPIVersion = "v1"
103+
104+
// Start starts an room api server.
105+
func (s *RoomAPIServer) Start() error {
106+
redisConn, err := redis.Dial("tcp", s.RedisHost)
107+
if err != nil {
108+
return err
109+
}
110+
111+
psc := redis.PubSubConn{Conn: redisConn}
112+
if err := psc.Subscribe(channelServer, channelRoom); err != nil {
113+
return err
114+
}
115+
116+
ctx, cancel := context.WithCancel(context.Background())
117+
defer cancel()
118+
119+
go func(ctx context.Context, psc redis.PubSubConn) {
120+
if err := s.subscribe(ctx, psc); err != nil {
121+
s.Logger.Println(err)
122+
}
123+
124+
for {
125+
select {
126+
case <-ctx.Done():
127+
return
128+
default:
129+
}
130+
131+
redisConn, err := redis.Dial("tcp", s.RedisHost)
132+
if err != nil {
133+
s.Logger.Println(err)
134+
break
135+
}
136+
137+
psc = redis.PubSubConn{Conn: redisConn}
138+
if err := psc.Subscribe(channelServer, channelRoom); err != nil {
139+
s.Logger.Println(err)
140+
break
141+
}
142+
143+
if err := s.subscribe(ctx, psc); err != nil {
144+
s.Logger.Println(err)
145+
}
146+
}
147+
}(ctx, psc)
148+
149+
go s.serverManager.DeleteUnhealthServerAtPeriodic(ctx, s.ServerDeadLine)
150+
go s.roomManager.DeleteDeadRoomAtPeriodic(ctx, s.RoomDeadLine)
151+
152+
e := echo.New()
153+
e.Use(middleware.Recover())
154+
e.Use(middleware.Logger())
155+
g := e.Group(s.BaseUri)
156+
g.Add(echo.POST, "/rooms", s.roomCreateHandler)
157+
g.Add(echo.GET, "/rooms", s.roomListHandler)
158+
g.Use(func(next echo.HandlerFunc) echo.HandlerFunc {
159+
return func(c echo.Context) error {
160+
c.Response().Header().Add("X-IGUAGILE-API", iguagileAPIVersion)
161+
return next(c)
162+
}
163+
})
164+
165+
return e.Start(s.Address)
166+
}

cli/main.go

Lines changed: 9 additions & 142 deletions
Original file line numberDiff line numberDiff line change
@@ -2,150 +2,17 @@ package main
22

33
import (
44
"log"
5-
"math"
6-
"sync"
5+
"os"
76

8-
"github.com/labstack/echo"
9-
"github.com/labstack/echo/middleware"
10-
"github.com/minami14/idgo"
7+
api "github.com/iguagile/iguagile-api"
118
)
129

13-
type Server struct {
14-
Host string `json:"server"`
15-
Port int `json:"port"`
16-
}
17-
18-
type Room struct {
19-
RoomID int `json:"room_id"`
20-
RequirePassword bool `json:"require_password"`
21-
MaxUser int `json:"max_user"`
22-
ConnectedUser int `json:"connected_user"`
23-
Server Server `json:"server"`
24-
ApplicationName string
25-
Version string
26-
}
27-
28-
type APIResponse struct {
29-
Success bool `json:"success"`
30-
Result interface{} `json:"result"`
31-
Error string `json:"error"`
32-
}
33-
34-
type CreateRoomRequest struct {
35-
ApplicationName string `json:"application_name"`
36-
Version string `json:"version"`
37-
Password string `json:"password"`
38-
MaxUser int `json:"max_user"`
39-
}
40-
41-
type RoomManager struct {
42-
rooms *sync.Map
43-
}
44-
45-
func (m *RoomManager) Store(room *Room) {
46-
m.rooms.Store(room.RoomID, room)
47-
}
48-
49-
func (m *RoomManager) Search(name, version string) []*Room {
50-
rooms := make([]*Room, 0)
51-
m.rooms.Range(func(key, value interface{}) bool {
52-
room := value.(*Room)
53-
if room.ApplicationName == name && room.Version == version {
54-
rooms = append(rooms, room)
55-
}
56-
return true
57-
})
58-
59-
return rooms
60-
}
61-
62-
var roomManager = &RoomManager{
63-
rooms: &sync.Map{},
64-
}
65-
66-
var generator *idgo.IDGenerator
67-
68-
const iguagileAPIVersion = "v1"
69-
70-
const maxUser = 70
71-
7210
func main() {
73-
store, err := idgo.NewLocalStore(math.MaxInt16)
74-
if err != nil {
75-
log.Fatal(err)
76-
}
77-
78-
generator, err = idgo.NewIDGenerator(store)
79-
if err != nil {
80-
log.Fatal(err)
81-
}
82-
83-
e := echo.New()
84-
e.Use(middleware.Recover())
85-
e.Use(middleware.Logger())
86-
g := e.Group("/api/v1")
87-
g.Add(echo.POST, "/rooms", roomCreateHandler)
88-
g.Add(echo.GET, "/rooms", roomListHandler)
89-
g.Use(func(next echo.HandlerFunc) echo.HandlerFunc {
90-
return func(c echo.Context) error {
91-
c.Response().Header().Add("X-IGUAGILE-API", iguagileAPIVersion)
92-
return next(c)
93-
}
94-
})
95-
e.Logger.Fatal(e.Start("localhost:1323"))
96-
}
97-
98-
func roomCreateHandler(c echo.Context) error {
99-
request := &CreateRoomRequest{}
100-
if err := c.Bind(request); err != nil {
101-
return err
102-
}
103-
log.Printf("create %v\n", request)
104-
105-
if request.MaxUser > maxUser {
106-
res := APIResponse{
107-
Success: false,
108-
Error: "MaxUser exceeds the maximum value",
109-
}
110-
return c.JSON(400, res)
111-
}
112-
113-
id, err := generator.Generate()
114-
if err != nil {
115-
return err
116-
}
117-
118-
room := &Room{
119-
RoomID: id,
120-
MaxUser: request.MaxUser,
121-
RequirePassword: request.Password != "",
122-
Server: Server{
123-
Host: "localhost",
124-
Port: 4000,
125-
},
126-
ApplicationName: request.ApplicationName,
127-
Version: request.Version,
128-
}
129-
130-
roomManager.Store(room)
131-
132-
res := APIResponse{
133-
Success: true,
134-
Result: room,
135-
}
136-
return c.JSON(201, res)
137-
}
138-
139-
func roomListHandler(c echo.Context) error {
140-
name := c.QueryParam("name")
141-
version := c.QueryParam("version")
142-
log.Printf("search %s %s\n", name, version)
143-
144-
rooms := roomManager.Search(name, version)
145-
res := APIResponse{
146-
Success: true,
147-
Result: rooms,
148-
}
149-
150-
return c.JSON(200, res)
11+
apiServer := api.NewRoomAPIServer()
12+
apiServer.Address = ":80"
13+
apiServer.BaseUri = "/api/v1"
14+
apiServer.RedisHost = os.Getenv("REDIS_HOST")
15+
apiServer.MaxUser = 70
16+
apiServer.Logger = log.New(os.Stdout, "iguagile-api ", log.Lshortfile)
17+
log.Fatal(apiServer.Start())
15118
}

go.mod

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,12 @@ go 1.13
44

55
require (
66
github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
7+
github.com/golang/protobuf v1.3.3
8+
github.com/gomodule/redigo v2.0.0+incompatible
9+
github.com/google/uuid v1.1.1
10+
github.com/iguagile/iguagile-room-proto v0.0.0-20200206152432-0db3646938b5
711
github.com/labstack/echo v3.3.10+incompatible
812
github.com/labstack/gommon v0.3.0 // indirect
9-
github.com/minami14/idgo v1.1.0
1013
github.com/valyala/fasttemplate v1.1.0 // indirect
11-
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 // indirect
14+
google.golang.org/grpc v1.27.1
1215
)

0 commit comments

Comments
 (0)