mirror of
https://github.com/kevin-DL/services.git
synced 2026-01-11 19:04:35 +00:00
add the chat service (#381)
This commit is contained in:
27
chat/Makefile
Normal file
27
chat/Makefile
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
|
||||||
|
GOPATH:=$(shell go env GOPATH)
|
||||||
|
.PHONY: init
|
||||||
|
init:
|
||||||
|
go get -u github.com/golang/protobuf/proto
|
||||||
|
go get -u github.com/golang/protobuf/protoc-gen-go
|
||||||
|
go get github.com/micro/micro/v3/cmd/protoc-gen-micro
|
||||||
|
|
||||||
|
.PHONY: api
|
||||||
|
api:
|
||||||
|
protoc --openapi_out=. --proto_path=. proto/chat.proto
|
||||||
|
|
||||||
|
.PHONY: proto
|
||||||
|
proto:
|
||||||
|
protoc --proto_path=. --micro_out=. --go_out=:. proto/chat.proto
|
||||||
|
|
||||||
|
.PHONY: build
|
||||||
|
build:
|
||||||
|
go build -o chat *.go
|
||||||
|
|
||||||
|
.PHONY: test
|
||||||
|
test:
|
||||||
|
go test -v ./... -cover
|
||||||
|
|
||||||
|
.PHONY: docker
|
||||||
|
docker:
|
||||||
|
docker build . -t chat:latest
|
||||||
7
chat/README.md
Normal file
7
chat/README.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
Real time messaging
|
||||||
|
|
||||||
|
|
||||||
|
# Chat Service
|
||||||
|
|
||||||
|
The Chat service is a programmable instant messaging API service which can be used in any application to immediately create conversations.
|
||||||
|
|
||||||
174
chat/examples.json
Normal file
174
chat/examples.json
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
{
|
||||||
|
"new": [{
|
||||||
|
"title": "Create a new chat",
|
||||||
|
"description": "Create a new chat by name",
|
||||||
|
"run_check": false,
|
||||||
|
"request": {
|
||||||
|
"name": "general",
|
||||||
|
"description": "The general chat room"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"room": {
|
||||||
|
"id": "d8057208-f81a-4e14-ad7f-c29daa2bb910",
|
||||||
|
"name": "general",
|
||||||
|
"description": "The general chat room",
|
||||||
|
"created_at": "2022-02-17T16:12:43.942557998Z",
|
||||||
|
"user_ids": [],
|
||||||
|
"private": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
"list": [{
|
||||||
|
"title": "List chat rooms",
|
||||||
|
"description": "List all the chat rooms",
|
||||||
|
"run_check": false,
|
||||||
|
"request": {},
|
||||||
|
"response": {
|
||||||
|
"rooms": [{
|
||||||
|
"id": "d8057208-f81a-4e14-ad7f-c29daa2bb910",
|
||||||
|
"name": "general",
|
||||||
|
"description": "The general chat room",
|
||||||
|
"created_at": "2022-02-17T16:12:43.942557998Z",
|
||||||
|
"user_ids": [],
|
||||||
|
"private": false
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
"delete": [{
|
||||||
|
"title": "Delete a chat",
|
||||||
|
"description": "Delete a chat room",
|
||||||
|
"run_check": false,
|
||||||
|
"request": {
|
||||||
|
"room_id": "d8057208-f81a-4e14-ad7f-c29daa2bb910"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"room": {
|
||||||
|
"id": "d8057208-f81a-4e14-ad7f-c29daa2bb910",
|
||||||
|
"name": "general",
|
||||||
|
"description": "The general chat room",
|
||||||
|
"created_at": "2022-02-17T16:12:43.942557998Z",
|
||||||
|
"user_ids": [],
|
||||||
|
"private": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
"invite": [{
|
||||||
|
"title": "Invite a user",
|
||||||
|
"description": "Invite a user to a chat room",
|
||||||
|
"run_check": false,
|
||||||
|
"request": {
|
||||||
|
"room_id": "d8057208-f81a-4e14-ad7f-c29daa2bb910",
|
||||||
|
"user_id": "user-1"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"room": {
|
||||||
|
"id": "d8057208-f81a-4e14-ad7f-c29daa2bb910",
|
||||||
|
"name": "general",
|
||||||
|
"description": "The general chat room",
|
||||||
|
"created_at": "2022-02-17T16:12:43.942557998Z",
|
||||||
|
"user_ids": ["user-1"],
|
||||||
|
"private": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
"send": [{
|
||||||
|
"title": "Send a message",
|
||||||
|
"description": "Send a message to a room",
|
||||||
|
"run_check": false,
|
||||||
|
"request": {
|
||||||
|
"room_id": "d8057208-f81a-4e14-ad7f-c29daa2bb910",
|
||||||
|
"user_id": "user-1",
|
||||||
|
"client": "web",
|
||||||
|
"subject": "Random",
|
||||||
|
"text": "Hey whats up?"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"message": {
|
||||||
|
"id": "d44c6dc0-89d7-4a36-b528-cfd6c728ccef",
|
||||||
|
"client": "web",
|
||||||
|
"room_id": "d8057208-f81a-4e14-ad7f-c29daa2bb910",
|
||||||
|
"user_id": "user-1",
|
||||||
|
"sent_at": "2022-02-17T16:18:35.683008885Z",
|
||||||
|
"subject": "Random",
|
||||||
|
"text": "Hey whats up?"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
"history": [{
|
||||||
|
"title": "Get chat history",
|
||||||
|
"description": "Get chat history for a room",
|
||||||
|
"run_check": false,
|
||||||
|
"request": {
|
||||||
|
"room_id": "d8057208-f81a-4e14-ad7f-c29daa2bb910"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"messages": [{
|
||||||
|
"id": "d44c6dc0-89d7-4a36-b528-cfd6c728ccef",
|
||||||
|
"client": "web",
|
||||||
|
"room_id": "d8057208-f81a-4e14-ad7f-c29daa2bb910",
|
||||||
|
"user_id": "user-1",
|
||||||
|
"sent_at": "2022-02-17T16:18:35.683008885Z",
|
||||||
|
"subject": "Random",
|
||||||
|
"text": "Hey whats up?"
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
"join": [{
|
||||||
|
"title": "Join a room",
|
||||||
|
"description": "Join a room",
|
||||||
|
"run_check": false,
|
||||||
|
"request": {
|
||||||
|
"room_id": "d8057208-f81a-4e14-ad7f-c29daa2bb910",
|
||||||
|
"user_id": "user-2"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"messages": [{
|
||||||
|
"id": "d44c6dc0-89d7-4a36-b528-cfd6c728ccef",
|
||||||
|
"client": "web",
|
||||||
|
"room_id": "d8057208-f81a-4e14-ad7f-c29daa2bb910",
|
||||||
|
"user_id": "user-1",
|
||||||
|
"sent_at": "2022-02-17T16:18:35.683008885Z",
|
||||||
|
"subject": "Random",
|
||||||
|
"text": "Hey whats up?"
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
"kick": [{
|
||||||
|
"title": "Kick a user from a room",
|
||||||
|
"description": "Kick a user from a chat room",
|
||||||
|
"run_check": false,
|
||||||
|
"request": {
|
||||||
|
"room_id": "d8057208-f81a-4e14-ad7f-c29daa2bb910",
|
||||||
|
"user_id": "user-1"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"room": {
|
||||||
|
"id": "d8057208-f81a-4e14-ad7f-c29daa2bb910",
|
||||||
|
"name": "general",
|
||||||
|
"description": "The general chat room",
|
||||||
|
"created_at": "2022-02-17T16:12:43.942557998Z",
|
||||||
|
"user_ids": [],
|
||||||
|
"private": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
"leave": [{
|
||||||
|
"title": "Leave a room",
|
||||||
|
"description": "Leave a chat room",
|
||||||
|
"run_check": false,
|
||||||
|
"request": {
|
||||||
|
"room_id": "d8057208-f81a-4e14-ad7f-c29daa2bb910",
|
||||||
|
"user_id": "user-1"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"room": {
|
||||||
|
"id": "d8057208-f81a-4e14-ad7f-c29daa2bb910",
|
||||||
|
"name": "general",
|
||||||
|
"description": "The general chat room",
|
||||||
|
"created_at": "2022-02-17T16:12:43.942557998Z",
|
||||||
|
"user_ids": [],
|
||||||
|
"private": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
540
chat/handler/handler.go
Normal file
540
chat/handler/handler.go
Normal file
@@ -0,0 +1,540 @@
|
|||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"path"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/micro/micro/v3/service/errors"
|
||||||
|
"github.com/micro/micro/v3/service/events"
|
||||||
|
"github.com/micro/micro/v3/service/logger"
|
||||||
|
"github.com/micro/micro/v3/service/store"
|
||||||
|
pb "github.com/micro/services/chat/proto"
|
||||||
|
"github.com/micro/services/pkg/tenant"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
chatStoreKeyPrefix = "chats/"
|
||||||
|
chatEventKeyPrefix = "chats/"
|
||||||
|
messageStoreKeyPrefix = "messages/"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Chat struct{}
|
||||||
|
|
||||||
|
func (c *Chat) New(ctx context.Context, req *pb.NewRequest, rsp *pb.NewResponse) error {
|
||||||
|
// get the tenant
|
||||||
|
tenantId := tenant.Id(ctx)
|
||||||
|
|
||||||
|
// generate a unique id for the chat
|
||||||
|
roomId := uuid.New().String()
|
||||||
|
|
||||||
|
// create a new room
|
||||||
|
room := &pb.Room{
|
||||||
|
Id: roomId,
|
||||||
|
Name: req.Name,
|
||||||
|
Description: req.Description,
|
||||||
|
UserIds: req.UserIds,
|
||||||
|
Private: req.Private,
|
||||||
|
CreatedAt: time.Now().Format(time.RFC3339Nano),
|
||||||
|
}
|
||||||
|
|
||||||
|
// key to lookup the chat in the store using, e.g. "chat/usera-userb-userc"
|
||||||
|
key := path.Join(chatStoreKeyPrefix, tenantId, roomId)
|
||||||
|
|
||||||
|
// create a new record for the room
|
||||||
|
rec := store.NewRecord(key, room)
|
||||||
|
|
||||||
|
// write a record for the new room
|
||||||
|
if err := store.Write(rec); err != nil {
|
||||||
|
logger.Errorf("Error writing to the store. Key: %v. Error: %v", key, err)
|
||||||
|
return errors.InternalServerError("chat.new", "error creating chat room")
|
||||||
|
}
|
||||||
|
|
||||||
|
// return the room
|
||||||
|
rsp.Room = room
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Chat) Delete(ctx context.Context, req *pb.DeleteRequest, rsp *pb.DeleteResponse) error {
|
||||||
|
tenantId := tenant.Id(ctx)
|
||||||
|
|
||||||
|
// validate the request
|
||||||
|
if len(req.RoomId) == 0 {
|
||||||
|
return errors.BadRequest("chat.delete", "missing room id")
|
||||||
|
}
|
||||||
|
|
||||||
|
key := path.Join(chatStoreKeyPrefix, tenantId, req.RoomId)
|
||||||
|
|
||||||
|
// lookup the chat from the store to ensure it's valid
|
||||||
|
recs, err := store.Read(key, store.ReadLimit(1))
|
||||||
|
if err == store.ErrNotFound {
|
||||||
|
return errors.BadRequest("chat.delete", "room not found")
|
||||||
|
} else if err != nil {
|
||||||
|
logger.Errorf("Error reading from the store. Room ID: %v. Error: %v", req.RoomId, err)
|
||||||
|
return errors.InternalServerError("chat.delete", "error reading chat room")
|
||||||
|
}
|
||||||
|
|
||||||
|
room := new(pb.Room)
|
||||||
|
err = recs[0].Decode(room)
|
||||||
|
if err != nil {
|
||||||
|
return errors.InternalServerError("chat.delete", "error reading chat room")
|
||||||
|
}
|
||||||
|
// set response
|
||||||
|
rsp.Room = room
|
||||||
|
|
||||||
|
// delete the room
|
||||||
|
if err := store.Delete(key); err != nil {
|
||||||
|
return errors.InternalServerError("chat.delete", "error deleting chat room")
|
||||||
|
}
|
||||||
|
|
||||||
|
// get all messages
|
||||||
|
// TODO: paginate the list
|
||||||
|
key = path.Join(messageStoreKeyPrefix, tenantId, req.RoomId)
|
||||||
|
srecs, err := store.List(store.ListPrefix(key))
|
||||||
|
if err != nil {
|
||||||
|
return errors.InternalServerError("chat.delete", "failed to list messages")
|
||||||
|
}
|
||||||
|
|
||||||
|
// delete all the messages
|
||||||
|
for _, rec := range srecs {
|
||||||
|
if err := store.Delete(rec); err != nil {
|
||||||
|
return errors.InternalServerError("chat.delete", "failed to list messages")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: notify users of the event that the room is deleted
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Chat) List(ctx context.Context, req *pb.ListRequest, rsp *pb.ListResponse) error {
|
||||||
|
// get the tenant
|
||||||
|
tenantId := tenant.Id(ctx)
|
||||||
|
|
||||||
|
key := path.Join(chatStoreKeyPrefix, tenantId) + "/"
|
||||||
|
|
||||||
|
// read all the rooms from the store for the user
|
||||||
|
recs, err := store.Read(key, store.ReadPrefix())
|
||||||
|
if err != nil {
|
||||||
|
return errors.InternalServerError("chat.list", "error listing chat rooms")
|
||||||
|
}
|
||||||
|
|
||||||
|
// list all the rooms
|
||||||
|
for _, rec := range recs {
|
||||||
|
room := new(pb.Room)
|
||||||
|
err := rec.Decode(room)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(req.UserId) == 0 {
|
||||||
|
rsp.Rooms = append(rsp.Rooms, room)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if there's a user id match
|
||||||
|
for _, user := range room.UserIds {
|
||||||
|
if user == req.UserId {
|
||||||
|
rsp.Rooms = append(rsp.Rooms, room)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// History returns the historical messages in a chat
|
||||||
|
func (c *Chat) History(ctx context.Context, req *pb.HistoryRequest, rsp *pb.HistoryResponse) error {
|
||||||
|
// get the tenant
|
||||||
|
tenantId := tenant.Id(ctx)
|
||||||
|
|
||||||
|
// validate the request
|
||||||
|
if len(req.RoomId) == 0 {
|
||||||
|
return errors.BadRequest("chat.history", "missing room id")
|
||||||
|
}
|
||||||
|
|
||||||
|
key := path.Join(chatStoreKeyPrefix, tenantId, req.RoomId)
|
||||||
|
|
||||||
|
// lookup the chat from the store to ensure it's valid
|
||||||
|
if _, err := store.Read(key); err == store.ErrNotFound {
|
||||||
|
return errors.BadRequest("chat.history", "room not found")
|
||||||
|
} else if err != nil {
|
||||||
|
logger.Errorf("Error reading from the store. Room ID: %v. Error: %v", req.RoomId, err)
|
||||||
|
return errors.InternalServerError("chat.history", "error reading chat room")
|
||||||
|
}
|
||||||
|
|
||||||
|
// lookup the messages
|
||||||
|
key = path.Join(messageStoreKeyPrefix, tenantId, req.RoomId)
|
||||||
|
recs, err := store.Read(key+"/", store.ReadPrefix())
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorf("Error reading messages the store. Room ID: %v. Error: %v", req.RoomId, err)
|
||||||
|
return errors.InternalServerError("chat.history", "failed to read messages")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, rec := range recs {
|
||||||
|
msg := new(pb.Message)
|
||||||
|
err := rec.Decode(msg)
|
||||||
|
if err != nil {
|
||||||
|
return errors.InternalServerError("chat.history", "failed to decode message")
|
||||||
|
}
|
||||||
|
rsp.Messages = append(rsp.Messages, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Chat) Invite(ctx context.Context, req *pb.InviteRequest, rsp *pb.InviteResponse) error {
|
||||||
|
// get the tenant
|
||||||
|
tenantId := tenant.Id(ctx)
|
||||||
|
|
||||||
|
// validate the request
|
||||||
|
if len(req.RoomId) == 0 {
|
||||||
|
return errors.BadRequest("chat.invite", "missing room id")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(req.UserId) == 0 {
|
||||||
|
return errors.BadRequest("chat.invite", "missing user id")
|
||||||
|
}
|
||||||
|
|
||||||
|
key := path.Join(chatStoreKeyPrefix, tenantId, req.RoomId)
|
||||||
|
|
||||||
|
// lookup the chat from the store to ensure it's valid
|
||||||
|
recs, err := store.Read(key)
|
||||||
|
if err == store.ErrNotFound {
|
||||||
|
return errors.BadRequest("chat.invite", "room not found")
|
||||||
|
} else if err != nil {
|
||||||
|
logger.Errorf("Error reading from the store. Room ID: %v. Error: %v", req.RoomId, err)
|
||||||
|
return errors.InternalServerError("chat.invite", "error reading chat room")
|
||||||
|
}
|
||||||
|
|
||||||
|
// check the user is in the room
|
||||||
|
room := new(pb.Room)
|
||||||
|
err = recs[0].Decode(room)
|
||||||
|
if err != nil {
|
||||||
|
return errors.InternalServerError("chat.invite", "Error reading room")
|
||||||
|
}
|
||||||
|
|
||||||
|
var exists bool
|
||||||
|
|
||||||
|
// check the user is in the room
|
||||||
|
for _, user := range room.UserIds {
|
||||||
|
if user == req.UserId {
|
||||||
|
exists = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: send join message
|
||||||
|
if !exists {
|
||||||
|
room.UserIds = append(room.UserIds, req.UserId)
|
||||||
|
// write the record
|
||||||
|
rec := store.NewRecord(key, room)
|
||||||
|
if err := store.Write(rec); err != nil {
|
||||||
|
return errors.InternalServerError("chat.invite", "Error adding user to room")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rsp.Room = room
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send a single message to the chat, designed for ease of use via the API / CLI
|
||||||
|
func (c *Chat) Send(ctx context.Context, req *pb.SendRequest, rsp *pb.SendResponse) error {
|
||||||
|
// get the tenant
|
||||||
|
tenantId := tenant.Id(ctx)
|
||||||
|
|
||||||
|
// validate the request
|
||||||
|
if len(req.RoomId) == 0 {
|
||||||
|
return errors.BadRequest("chat.send", "missing room id")
|
||||||
|
}
|
||||||
|
if len(req.UserId) == 0 {
|
||||||
|
return errors.BadRequest("chat.send", "missing user id")
|
||||||
|
}
|
||||||
|
if len(req.Text) == 0 {
|
||||||
|
return errors.BadRequest("chat.send", "missing text")
|
||||||
|
}
|
||||||
|
|
||||||
|
// check the room exists
|
||||||
|
key := path.Join(chatStoreKeyPrefix, tenantId, req.RoomId)
|
||||||
|
|
||||||
|
// lookup the chat room from the store to ensure it's valid
|
||||||
|
recs, err := store.Read(key, store.ReadLimit(1))
|
||||||
|
if err == store.ErrNotFound {
|
||||||
|
return errors.BadRequest("chat.send", "room not found")
|
||||||
|
} else if err != nil {
|
||||||
|
logger.Errorf("Error reading from the store. Room ID: %v. Error: %v", req.RoomId, err)
|
||||||
|
return errors.InternalServerError("chat.send", "error reading chat room")
|
||||||
|
}
|
||||||
|
|
||||||
|
// decode the room
|
||||||
|
room := new(pb.Room)
|
||||||
|
err = recs[0].Decode(room)
|
||||||
|
if err != nil {
|
||||||
|
return errors.InternalServerError("chat.send", "error reading chat room")
|
||||||
|
}
|
||||||
|
|
||||||
|
var exists bool
|
||||||
|
|
||||||
|
// check the user is in the room
|
||||||
|
for _, user := range room.UserIds {
|
||||||
|
if user == req.UserId {
|
||||||
|
exists = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !exists {
|
||||||
|
return errors.BadRequest("chat.send", "user is not in the room")
|
||||||
|
}
|
||||||
|
|
||||||
|
// construct the message
|
||||||
|
msg := &pb.Message{
|
||||||
|
Id: uuid.New().String(),
|
||||||
|
Client: req.Client,
|
||||||
|
RoomId: req.RoomId,
|
||||||
|
UserId: req.UserId,
|
||||||
|
Subject: req.Subject,
|
||||||
|
Text: req.Text,
|
||||||
|
SentAt: time.Now().Format(time.RFC3339Nano),
|
||||||
|
}
|
||||||
|
|
||||||
|
// default the client id if not provided
|
||||||
|
if len(msg.Client) == 0 {
|
||||||
|
msg.Client = uuid.New().String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// create the message
|
||||||
|
if err := c.createMessage(tenantId, msg); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// return the response
|
||||||
|
rsp.Message = msg
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Chat) Join(ctx context.Context, req *pb.JoinRequest, stream pb.Chat_JoinStream) error {
|
||||||
|
// get the tenant
|
||||||
|
tenantId := tenant.Id(ctx)
|
||||||
|
|
||||||
|
// validate the request
|
||||||
|
if len(req.RoomId) == 0 {
|
||||||
|
return errors.BadRequest("chat.send", "missing room id")
|
||||||
|
}
|
||||||
|
if len(req.UserId) == 0 {
|
||||||
|
return errors.BadRequest("chat.send", "missing user id")
|
||||||
|
}
|
||||||
|
|
||||||
|
key := path.Join(chatStoreKeyPrefix, tenantId, req.RoomId)
|
||||||
|
|
||||||
|
// lookup the chat from the store to ensure it's valid
|
||||||
|
recs, err := store.Read(key, store.ReadLimit(1))
|
||||||
|
if err == store.ErrNotFound {
|
||||||
|
return errors.BadRequest("chat.join", "room not found")
|
||||||
|
} else if err != nil {
|
||||||
|
logger.Errorf("Error reading from the store. Room ID: %v. Error: %v", req.RoomId, err)
|
||||||
|
return errors.InternalServerError("chat.join", "Error reading room")
|
||||||
|
}
|
||||||
|
|
||||||
|
// check the user is in the room
|
||||||
|
room := new(pb.Room)
|
||||||
|
err = recs[0].Decode(room)
|
||||||
|
if err != nil {
|
||||||
|
return errors.InternalServerError("chat.join", "Error reading room")
|
||||||
|
}
|
||||||
|
|
||||||
|
var exists bool
|
||||||
|
|
||||||
|
// check the user is in the room
|
||||||
|
for _, user := range room.UserIds {
|
||||||
|
if user == req.UserId {
|
||||||
|
exists = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: send join message
|
||||||
|
if !exists {
|
||||||
|
room.UserIds = append(room.UserIds, req.UserId)
|
||||||
|
// write the record
|
||||||
|
rec := store.NewRecord(key, room)
|
||||||
|
if err := store.Write(rec); err != nil {
|
||||||
|
return errors.InternalServerError("chat.join", "Error adding user to room")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a channel to send errors on, because the subscriber / publisher will run in seperate go-
|
||||||
|
// routines, they need a way of returning errors to the client
|
||||||
|
errChan := make(chan error)
|
||||||
|
|
||||||
|
eventKey := path.Join(chatEventKeyPrefix, tenantId, req.RoomId)
|
||||||
|
|
||||||
|
// create an event stream to consume messages posted by other users into the chat. we'll use the
|
||||||
|
// user id as a queue to ensure each user recieves the message
|
||||||
|
evStream, err := events.Consume(eventKey, events.WithGroup(req.UserId), events.WithContext(ctx))
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorf("Error streaming events. Room ID: %v. Error: %v", req.RoomId, err)
|
||||||
|
return errors.InternalServerError("chat.join", "Error joining the room")
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
// the context has been cancelled or timed out, stop subscribing to new messages
|
||||||
|
return nil
|
||||||
|
case ev := <-evStream:
|
||||||
|
// recieved a message, unmarshal it into a message struct. if an error occurs log it and
|
||||||
|
// cancel the context
|
||||||
|
var msg pb.Message
|
||||||
|
if err := ev.Unmarshal(&msg); err != nil {
|
||||||
|
logger.Errorf("Error unmarshaling message. Room ID: %v. Error: %v", req.RoomId, err)
|
||||||
|
errChan <- err
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ignore any messages published by the current user
|
||||||
|
if msg.UserId == req.UserId {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// publish the message to the stream
|
||||||
|
if err := stream.Send(&msg); err != nil {
|
||||||
|
logger.Errorf("Error sending message to stream. ChatID: %v. Message ID: %v. Error: %v", msg.RoomId, msg.Id, err)
|
||||||
|
errChan <- err
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Chat) Kick(ctx context.Context, req *pb.KickRequest, rsp *pb.KickResponse) error {
|
||||||
|
// get the tenant
|
||||||
|
tenantId := tenant.Id(ctx)
|
||||||
|
|
||||||
|
// validate the request
|
||||||
|
if len(req.RoomId) == 0 {
|
||||||
|
return errors.BadRequest("chat.kick", "missing room id")
|
||||||
|
}
|
||||||
|
if len(req.UserId) == 0 {
|
||||||
|
return errors.BadRequest("chat.kick", "missing user id")
|
||||||
|
}
|
||||||
|
|
||||||
|
key := path.Join(chatStoreKeyPrefix, tenantId, req.RoomId)
|
||||||
|
|
||||||
|
// lookup the chat from the store to ensure it's valid
|
||||||
|
recs, err := store.Read(key, store.ReadLimit(1))
|
||||||
|
if err == store.ErrNotFound {
|
||||||
|
return errors.BadRequest("chat.kick", "room not found")
|
||||||
|
} else if err != nil {
|
||||||
|
logger.Errorf("Error reading from the store. Chat ID: %v. Error: %v", req.RoomId, err)
|
||||||
|
return errors.InternalServerError("chat.kick", "Error reading room")
|
||||||
|
}
|
||||||
|
|
||||||
|
// check the user is in the room
|
||||||
|
room := new(pb.Room)
|
||||||
|
err = recs[0].Decode(room)
|
||||||
|
if err != nil {
|
||||||
|
return errors.InternalServerError("chat.kick", "Error reading room")
|
||||||
|
}
|
||||||
|
|
||||||
|
var users []string
|
||||||
|
|
||||||
|
// check the user is in the room
|
||||||
|
for _, user := range room.UserIds {
|
||||||
|
if user == req.UserId {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
users = append(users, user)
|
||||||
|
}
|
||||||
|
|
||||||
|
room.UserIds = users
|
||||||
|
|
||||||
|
rec := store.NewRecord(key, room)
|
||||||
|
if err := store.Write(rec); err != nil {
|
||||||
|
return errors.InternalServerError("chat.kick", "Error leaveing from room")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: send leave message
|
||||||
|
// TODO: disconnect the actual event consumption
|
||||||
|
rsp.Room = room
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (c *Chat) Leave(ctx context.Context, req *pb.LeaveRequest, rsp *pb.LeaveResponse) error {
|
||||||
|
// get the tenant
|
||||||
|
tenantId := tenant.Id(ctx)
|
||||||
|
|
||||||
|
// validate the request
|
||||||
|
if len(req.RoomId) == 0 {
|
||||||
|
return errors.BadRequest("chat.leave", "missing room id")
|
||||||
|
}
|
||||||
|
if len(req.UserId) == 0 {
|
||||||
|
return errors.BadRequest("chat.leave", "missing user id")
|
||||||
|
}
|
||||||
|
|
||||||
|
key := path.Join(chatStoreKeyPrefix, tenantId, req.RoomId)
|
||||||
|
|
||||||
|
// lookup the chat from the store to ensure it's valid
|
||||||
|
recs, err := store.Read(key, store.ReadLimit(1))
|
||||||
|
if err == store.ErrNotFound {
|
||||||
|
return errors.BadRequest("chat.leave", "room not found")
|
||||||
|
} else if err != nil {
|
||||||
|
logger.Errorf("Error reading from the store. Chat ID: %v. Error: %v", req.RoomId, err)
|
||||||
|
return errors.InternalServerError("chat.leave", "Error reading room")
|
||||||
|
}
|
||||||
|
|
||||||
|
// check the user is in the room
|
||||||
|
room := new(pb.Room)
|
||||||
|
err = recs[0].Decode(room)
|
||||||
|
if err != nil {
|
||||||
|
return errors.InternalServerError("chat.leave", "Error reading room")
|
||||||
|
}
|
||||||
|
|
||||||
|
var users []string
|
||||||
|
|
||||||
|
// check the user is in the room
|
||||||
|
for _, user := range room.UserIds {
|
||||||
|
if user == req.UserId {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
users = append(users, user)
|
||||||
|
}
|
||||||
|
|
||||||
|
room.UserIds = users
|
||||||
|
|
||||||
|
rec := store.NewRecord(key, room)
|
||||||
|
if err := store.Write(rec); err != nil {
|
||||||
|
return errors.InternalServerError("chat.leave", "Error leaveing from room")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: send leave message
|
||||||
|
// TODO: disconnect the actual event consumption
|
||||||
|
rsp.Room = room
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// createMessage is a helper function which creates a message in the event stream. It handles the
|
||||||
|
// logic for ensuring client id is unique.
|
||||||
|
func (c *Chat) createMessage(tenantId string, msg *pb.Message) error {
|
||||||
|
storekey := path.Join(messageStoreKeyPrefix, tenantId, msg.RoomId, msg.Id)
|
||||||
|
eventKey := path.Join(chatEventKeyPrefix, tenantId, msg.RoomId)
|
||||||
|
|
||||||
|
// send the message to the event stream
|
||||||
|
if err := events.Publish(eventKey, msg); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a new record
|
||||||
|
rec := store.NewRecord(storekey, msg)
|
||||||
|
|
||||||
|
// record the messages client id
|
||||||
|
return store.Write(rec)
|
||||||
|
}
|
||||||
24
chat/main.go
Normal file
24
chat/main.go
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/micro/micro/v3/service"
|
||||||
|
"github.com/micro/micro/v3/service/logger"
|
||||||
|
"github.com/micro/services/chat/handler"
|
||||||
|
pb "github.com/micro/services/chat/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Create the service
|
||||||
|
srv := service.New(
|
||||||
|
service.Name("chat"),
|
||||||
|
service.Version("latest"),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Register the handler against the server
|
||||||
|
pb.RegisterChatHandler(srv.Server(), new(handler.Chat))
|
||||||
|
|
||||||
|
// Run the service
|
||||||
|
if err := srv.Run(); err != nil {
|
||||||
|
logger.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
1
chat/micro.mu
Normal file
1
chat/micro.mu
Normal file
@@ -0,0 +1 @@
|
|||||||
|
service chat
|
||||||
1585
chat/proto/chat.pb.go
Normal file
1585
chat/proto/chat.pb.go
Normal file
File diff suppressed because it is too large
Load Diff
304
chat/proto/chat.pb.micro.go
Normal file
304
chat/proto/chat.pb.micro.go
Normal file
@@ -0,0 +1,304 @@
|
|||||||
|
// Code generated by protoc-gen-micro. DO NOT EDIT.
|
||||||
|
// source: proto/chat.proto
|
||||||
|
|
||||||
|
package chat
|
||||||
|
|
||||||
|
import (
|
||||||
|
fmt "fmt"
|
||||||
|
proto "github.com/golang/protobuf/proto"
|
||||||
|
math "math"
|
||||||
|
)
|
||||||
|
|
||||||
|
import (
|
||||||
|
context "context"
|
||||||
|
api "github.com/micro/micro/v3/service/api"
|
||||||
|
client "github.com/micro/micro/v3/service/client"
|
||||||
|
server "github.com/micro/micro/v3/service/server"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
|
var _ = proto.Marshal
|
||||||
|
var _ = fmt.Errorf
|
||||||
|
var _ = math.Inf
|
||||||
|
|
||||||
|
// This is a compile-time assertion to ensure that this generated file
|
||||||
|
// is compatible with the proto package it is being compiled against.
|
||||||
|
// A compilation error at this line likely means your copy of the
|
||||||
|
// proto package needs to be updated.
|
||||||
|
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
||||||
|
|
||||||
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
|
var _ api.Endpoint
|
||||||
|
var _ context.Context
|
||||||
|
var _ client.Option
|
||||||
|
var _ server.Option
|
||||||
|
|
||||||
|
// Api Endpoints for Chat service
|
||||||
|
|
||||||
|
func NewChatEndpoints() []*api.Endpoint {
|
||||||
|
return []*api.Endpoint{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Client API for Chat service
|
||||||
|
|
||||||
|
type ChatService interface {
|
||||||
|
New(ctx context.Context, in *NewRequest, opts ...client.CallOption) (*NewResponse, error)
|
||||||
|
History(ctx context.Context, in *HistoryRequest, opts ...client.CallOption) (*HistoryResponse, error)
|
||||||
|
Send(ctx context.Context, in *SendRequest, opts ...client.CallOption) (*SendResponse, error)
|
||||||
|
List(ctx context.Context, in *ListRequest, opts ...client.CallOption) (*ListResponse, error)
|
||||||
|
Delete(ctx context.Context, in *DeleteRequest, opts ...client.CallOption) (*DeleteResponse, error)
|
||||||
|
Join(ctx context.Context, in *JoinRequest, opts ...client.CallOption) (Chat_JoinService, error)
|
||||||
|
Invite(ctx context.Context, in *InviteRequest, opts ...client.CallOption) (*InviteResponse, error)
|
||||||
|
Leave(ctx context.Context, in *LeaveRequest, opts ...client.CallOption) (*LeaveResponse, error)
|
||||||
|
Kick(ctx context.Context, in *KickRequest, opts ...client.CallOption) (*KickResponse, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type chatService struct {
|
||||||
|
c client.Client
|
||||||
|
name string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewChatService(name string, c client.Client) ChatService {
|
||||||
|
return &chatService{
|
||||||
|
c: c,
|
||||||
|
name: name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *chatService) New(ctx context.Context, in *NewRequest, opts ...client.CallOption) (*NewResponse, error) {
|
||||||
|
req := c.c.NewRequest(c.name, "Chat.New", in)
|
||||||
|
out := new(NewResponse)
|
||||||
|
err := c.c.Call(ctx, req, out, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *chatService) History(ctx context.Context, in *HistoryRequest, opts ...client.CallOption) (*HistoryResponse, error) {
|
||||||
|
req := c.c.NewRequest(c.name, "Chat.History", in)
|
||||||
|
out := new(HistoryResponse)
|
||||||
|
err := c.c.Call(ctx, req, out, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *chatService) Send(ctx context.Context, in *SendRequest, opts ...client.CallOption) (*SendResponse, error) {
|
||||||
|
req := c.c.NewRequest(c.name, "Chat.Send", in)
|
||||||
|
out := new(SendResponse)
|
||||||
|
err := c.c.Call(ctx, req, out, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *chatService) List(ctx context.Context, in *ListRequest, opts ...client.CallOption) (*ListResponse, error) {
|
||||||
|
req := c.c.NewRequest(c.name, "Chat.List", in)
|
||||||
|
out := new(ListResponse)
|
||||||
|
err := c.c.Call(ctx, req, out, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *chatService) Delete(ctx context.Context, in *DeleteRequest, opts ...client.CallOption) (*DeleteResponse, error) {
|
||||||
|
req := c.c.NewRequest(c.name, "Chat.Delete", in)
|
||||||
|
out := new(DeleteResponse)
|
||||||
|
err := c.c.Call(ctx, req, out, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *chatService) Join(ctx context.Context, in *JoinRequest, opts ...client.CallOption) (Chat_JoinService, error) {
|
||||||
|
req := c.c.NewRequest(c.name, "Chat.Join", &JoinRequest{})
|
||||||
|
stream, err := c.c.Stream(ctx, req, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := stream.Send(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &chatServiceJoin{stream}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Chat_JoinService interface {
|
||||||
|
Context() context.Context
|
||||||
|
SendMsg(interface{}) error
|
||||||
|
RecvMsg(interface{}) error
|
||||||
|
Close() error
|
||||||
|
Recv() (*Message, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type chatServiceJoin struct {
|
||||||
|
stream client.Stream
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *chatServiceJoin) Close() error {
|
||||||
|
return x.stream.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *chatServiceJoin) Context() context.Context {
|
||||||
|
return x.stream.Context()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *chatServiceJoin) SendMsg(m interface{}) error {
|
||||||
|
return x.stream.Send(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *chatServiceJoin) RecvMsg(m interface{}) error {
|
||||||
|
return x.stream.Recv(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *chatServiceJoin) Recv() (*Message, error) {
|
||||||
|
m := new(Message)
|
||||||
|
err := x.stream.Recv(m)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *chatService) Invite(ctx context.Context, in *InviteRequest, opts ...client.CallOption) (*InviteResponse, error) {
|
||||||
|
req := c.c.NewRequest(c.name, "Chat.Invite", in)
|
||||||
|
out := new(InviteResponse)
|
||||||
|
err := c.c.Call(ctx, req, out, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *chatService) Leave(ctx context.Context, in *LeaveRequest, opts ...client.CallOption) (*LeaveResponse, error) {
|
||||||
|
req := c.c.NewRequest(c.name, "Chat.Leave", in)
|
||||||
|
out := new(LeaveResponse)
|
||||||
|
err := c.c.Call(ctx, req, out, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *chatService) Kick(ctx context.Context, in *KickRequest, opts ...client.CallOption) (*KickResponse, error) {
|
||||||
|
req := c.c.NewRequest(c.name, "Chat.Kick", in)
|
||||||
|
out := new(KickResponse)
|
||||||
|
err := c.c.Call(ctx, req, out, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Server API for Chat service
|
||||||
|
|
||||||
|
type ChatHandler interface {
|
||||||
|
New(context.Context, *NewRequest, *NewResponse) error
|
||||||
|
History(context.Context, *HistoryRequest, *HistoryResponse) error
|
||||||
|
Send(context.Context, *SendRequest, *SendResponse) error
|
||||||
|
List(context.Context, *ListRequest, *ListResponse) error
|
||||||
|
Delete(context.Context, *DeleteRequest, *DeleteResponse) error
|
||||||
|
Join(context.Context, *JoinRequest, Chat_JoinStream) error
|
||||||
|
Invite(context.Context, *InviteRequest, *InviteResponse) error
|
||||||
|
Leave(context.Context, *LeaveRequest, *LeaveResponse) error
|
||||||
|
Kick(context.Context, *KickRequest, *KickResponse) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func RegisterChatHandler(s server.Server, hdlr ChatHandler, opts ...server.HandlerOption) error {
|
||||||
|
type chat interface {
|
||||||
|
New(ctx context.Context, in *NewRequest, out *NewResponse) error
|
||||||
|
History(ctx context.Context, in *HistoryRequest, out *HistoryResponse) error
|
||||||
|
Send(ctx context.Context, in *SendRequest, out *SendResponse) error
|
||||||
|
List(ctx context.Context, in *ListRequest, out *ListResponse) error
|
||||||
|
Delete(ctx context.Context, in *DeleteRequest, out *DeleteResponse) error
|
||||||
|
Join(ctx context.Context, stream server.Stream) error
|
||||||
|
Invite(ctx context.Context, in *InviteRequest, out *InviteResponse) error
|
||||||
|
Leave(ctx context.Context, in *LeaveRequest, out *LeaveResponse) error
|
||||||
|
Kick(ctx context.Context, in *KickRequest, out *KickResponse) error
|
||||||
|
}
|
||||||
|
type Chat struct {
|
||||||
|
chat
|
||||||
|
}
|
||||||
|
h := &chatHandler{hdlr}
|
||||||
|
return s.Handle(s.NewHandler(&Chat{h}, opts...))
|
||||||
|
}
|
||||||
|
|
||||||
|
type chatHandler struct {
|
||||||
|
ChatHandler
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *chatHandler) New(ctx context.Context, in *NewRequest, out *NewResponse) error {
|
||||||
|
return h.ChatHandler.New(ctx, in, out)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *chatHandler) History(ctx context.Context, in *HistoryRequest, out *HistoryResponse) error {
|
||||||
|
return h.ChatHandler.History(ctx, in, out)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *chatHandler) Send(ctx context.Context, in *SendRequest, out *SendResponse) error {
|
||||||
|
return h.ChatHandler.Send(ctx, in, out)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *chatHandler) List(ctx context.Context, in *ListRequest, out *ListResponse) error {
|
||||||
|
return h.ChatHandler.List(ctx, in, out)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *chatHandler) Delete(ctx context.Context, in *DeleteRequest, out *DeleteResponse) error {
|
||||||
|
return h.ChatHandler.Delete(ctx, in, out)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *chatHandler) Join(ctx context.Context, stream server.Stream) error {
|
||||||
|
m := new(JoinRequest)
|
||||||
|
if err := stream.Recv(m); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return h.ChatHandler.Join(ctx, m, &chatJoinStream{stream})
|
||||||
|
}
|
||||||
|
|
||||||
|
type Chat_JoinStream interface {
|
||||||
|
Context() context.Context
|
||||||
|
SendMsg(interface{}) error
|
||||||
|
RecvMsg(interface{}) error
|
||||||
|
Close() error
|
||||||
|
Send(*Message) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type chatJoinStream struct {
|
||||||
|
stream server.Stream
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *chatJoinStream) Close() error {
|
||||||
|
return x.stream.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *chatJoinStream) Context() context.Context {
|
||||||
|
return x.stream.Context()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *chatJoinStream) SendMsg(m interface{}) error {
|
||||||
|
return x.stream.Send(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *chatJoinStream) RecvMsg(m interface{}) error {
|
||||||
|
return x.stream.Recv(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *chatJoinStream) Send(m *Message) error {
|
||||||
|
return x.stream.Send(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *chatHandler) Invite(ctx context.Context, in *InviteRequest, out *InviteResponse) error {
|
||||||
|
return h.ChatHandler.Invite(ctx, in, out)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *chatHandler) Leave(ctx context.Context, in *LeaveRequest, out *LeaveResponse) error {
|
||||||
|
return h.ChatHandler.Leave(ctx, in, out)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *chatHandler) Kick(ctx context.Context, in *KickRequest, out *KickResponse) error {
|
||||||
|
return h.ChatHandler.Kick(ctx, in, out)
|
||||||
|
}
|
||||||
163
chat/proto/chat.proto
Normal file
163
chat/proto/chat.proto
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package chat;
|
||||||
|
option go_package = "./proto;chat";
|
||||||
|
|
||||||
|
service Chat {
|
||||||
|
rpc New(NewRequest) returns (NewResponse);
|
||||||
|
rpc History(HistoryRequest) returns (HistoryResponse);
|
||||||
|
rpc Send(SendRequest) returns (SendResponse);
|
||||||
|
rpc List(ListRequest) returns (ListResponse);
|
||||||
|
rpc Delete(DeleteRequest) returns (DeleteResponse);
|
||||||
|
rpc Join(JoinRequest) returns (stream Message);
|
||||||
|
rpc Invite(InviteRequest) returns (InviteResponse);
|
||||||
|
rpc Leave(LeaveRequest) returns (LeaveResponse);
|
||||||
|
rpc Kick(KickRequest) returns (KickResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new chat room
|
||||||
|
message NewRequest {
|
||||||
|
// name of the room
|
||||||
|
string name = 1;
|
||||||
|
// chat description
|
||||||
|
string description = 2;
|
||||||
|
// optional list of user ids
|
||||||
|
repeated string user_ids = 3;
|
||||||
|
// whether its a private room
|
||||||
|
bool private = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message NewResponse {
|
||||||
|
// the unique chat room
|
||||||
|
Room room = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// List the messages in a chat
|
||||||
|
message HistoryRequest {
|
||||||
|
// the chat room id to get
|
||||||
|
string room_id = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// HistoryResponse contains the historical messages in a chat
|
||||||
|
message HistoryResponse {
|
||||||
|
// messages in the chat room
|
||||||
|
repeated Message messages = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// List available chats
|
||||||
|
message ListRequest {
|
||||||
|
// optional user id to filter by
|
||||||
|
string user_id = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ListResponse {
|
||||||
|
repeated Room rooms = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete a chat room
|
||||||
|
message DeleteRequest {
|
||||||
|
// the chat room id to delete
|
||||||
|
string room_id = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message DeleteResponse {
|
||||||
|
Room room = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Connect to a chat to receive a stream of messages
|
||||||
|
// Send a message to a chat
|
||||||
|
message SendRequest {
|
||||||
|
// a client side id, should be validated by the server to make the request retry safe
|
||||||
|
string client = 1;
|
||||||
|
// id of the chat room the message is being sent to / from
|
||||||
|
string room_id = 2;
|
||||||
|
// id of the user who sent the message
|
||||||
|
string user_id = 3;
|
||||||
|
// subject of the message
|
||||||
|
string subject = 4;
|
||||||
|
// text of the message
|
||||||
|
string text = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SendResponse {
|
||||||
|
// the message which was created
|
||||||
|
Message message = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Join a chat room
|
||||||
|
message JoinRequest {
|
||||||
|
// chat room to join
|
||||||
|
string room_id = 1;
|
||||||
|
// user id joining
|
||||||
|
string user_id = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Room {
|
||||||
|
// unique room id
|
||||||
|
string id = 1;
|
||||||
|
// name of the chat
|
||||||
|
string name = 2;
|
||||||
|
// description of the that
|
||||||
|
string description = 3;
|
||||||
|
// time of creation
|
||||||
|
string created_at = 4;
|
||||||
|
// list of users
|
||||||
|
repeated string user_ids = 5;
|
||||||
|
// whether its a private room
|
||||||
|
bool private = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Message sent to a chat
|
||||||
|
message Message {
|
||||||
|
// id of the message, allocated by the server
|
||||||
|
string id = 1;
|
||||||
|
// a client side id, should be validated by the server to make the request retry safe
|
||||||
|
string client = 2;
|
||||||
|
// id of the chat the message is being sent to / from
|
||||||
|
string room_id = 3;
|
||||||
|
// id of the user who sent the message
|
||||||
|
string user_id = 4;
|
||||||
|
// time the message was sent in RFC3339 format
|
||||||
|
string sent_at = 5;
|
||||||
|
// subject of the message
|
||||||
|
string subject = 6;
|
||||||
|
// text of the message
|
||||||
|
string text = 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Leave a chat room
|
||||||
|
message LeaveRequest {
|
||||||
|
// the chat room id
|
||||||
|
string room_id = 1;
|
||||||
|
// the user id
|
||||||
|
string user_id = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message LeaveResponse {
|
||||||
|
Room room = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invite a user to a chat room
|
||||||
|
message InviteRequest {
|
||||||
|
// the room id
|
||||||
|
string room_id = 1;
|
||||||
|
// the user id
|
||||||
|
string user_id = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message InviteResponse {
|
||||||
|
Room room = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kick a user from a chat room
|
||||||
|
message KickRequest {
|
||||||
|
// the chat room id
|
||||||
|
string room_id = 1;
|
||||||
|
// the user id
|
||||||
|
string user_id = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message KickResponse {
|
||||||
|
Room room = 1;
|
||||||
|
}
|
||||||
6
chat/publicapi.json
Normal file
6
chat/publicapi.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"name": "chat",
|
||||||
|
"icon": "💬",
|
||||||
|
"category": "messaging",
|
||||||
|
"display_name": "Chat"
|
||||||
|
}
|
||||||
3
go.mod
3
go.mod
@@ -26,7 +26,6 @@ require (
|
|||||||
github.com/hashicorp/golang-lru v0.5.3
|
github.com/hashicorp/golang-lru v0.5.3
|
||||||
github.com/iverly/go-mcping v1.0.1-0.20200818104507-3d8fc23750ae
|
github.com/iverly/go-mcping v1.0.1-0.20200818104507-3d8fc23750ae
|
||||||
github.com/jackc/pgx/v4 v4.10.1
|
github.com/jackc/pgx/v4 v4.10.1
|
||||||
github.com/jmoiron/jsonq v0.0.0-20150511023944-e874b168d07e // indirect
|
|
||||||
github.com/kevinburke/go-types v0.0.0-20201208005256-aee49f568a20 // indirect
|
github.com/kevinburke/go-types v0.0.0-20201208005256-aee49f568a20 // indirect
|
||||||
github.com/kevinburke/go.uuid v1.2.0 // indirect
|
github.com/kevinburke/go.uuid v1.2.0 // indirect
|
||||||
github.com/kevinburke/rest v0.0.0-20210506044642-5611499aa33c // indirect
|
github.com/kevinburke/rest v0.0.0-20210506044642-5611499aa33c // indirect
|
||||||
@@ -35,7 +34,7 @@ require (
|
|||||||
github.com/m3o/goduckgo v0.0.0-20210630141545-c760fe67b945
|
github.com/m3o/goduckgo v0.0.0-20210630141545-c760fe67b945
|
||||||
github.com/mattheath/base62 v0.0.0-20150408093626-b80cdc656a7a // indirect
|
github.com/mattheath/base62 v0.0.0-20150408093626-b80cdc656a7a // indirect
|
||||||
github.com/mattheath/kala v0.0.0-20171219141654-d6276794bf0e
|
github.com/mattheath/kala v0.0.0-20171219141654-d6276794bf0e
|
||||||
github.com/micro/micro/v3 v3.9.1-0.20220203152611-b23544122058
|
github.com/micro/micro/v3 v3.9.1-0.20220216155053-3f60fc42a827
|
||||||
github.com/miekg/dns v1.1.31 // indirect
|
github.com/miekg/dns v1.1.31 // indirect
|
||||||
github.com/minio/minio-go/v7 v7.0.16
|
github.com/minio/minio-go/v7 v7.0.16
|
||||||
github.com/o1egl/govatar v0.3.0
|
github.com/o1egl/govatar v0.3.0
|
||||||
|
|||||||
23
go.sum
23
go.sum
@@ -103,7 +103,6 @@ github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4Yn
|
|||||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
|
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
|
||||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI=
|
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI=
|
||||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
||||||
github.com/caddyserver/certmagic v0.10.6 h1:sCya6FmfaN74oZE46kqfaFOVoROD/mF36rTQfjN7TZc=
|
|
||||||
github.com/caddyserver/certmagic v0.10.6/go.mod h1:Y8jcUBctgk/IhpAzlHKfimZNyXCkfGgRTC0orl8gROQ=
|
github.com/caddyserver/certmagic v0.10.6/go.mod h1:Y8jcUBctgk/IhpAzlHKfimZNyXCkfGgRTC0orl8gROQ=
|
||||||
github.com/cdipaolo/goml v0.0.0-20190412180403-e1f51f713598 h1:j2XRGH5Y5uWtBYXGwmrjKeM/kfu/jh7ZcnrGvyN5Ttk=
|
github.com/cdipaolo/goml v0.0.0-20190412180403-e1f51f713598 h1:j2XRGH5Y5uWtBYXGwmrjKeM/kfu/jh7ZcnrGvyN5Ttk=
|
||||||
github.com/cdipaolo/goml v0.0.0-20190412180403-e1f51f713598/go.mod h1:sduMkaHcXDIWurl/Bd/z0rNEUHw5tr6LUA9IO8E9o0o=
|
github.com/cdipaolo/goml v0.0.0-20190412180403-e1f51f713598/go.mod h1:sduMkaHcXDIWurl/Bd/z0rNEUHw5tr6LUA9IO8E9o0o=
|
||||||
@@ -111,18 +110,15 @@ github.com/cdipaolo/sentiment v0.0.0-20200617002423-c697f64e7f10 h1:6dGQY3apkf7l
|
|||||||
github.com/cdipaolo/sentiment v0.0.0-20200617002423-c697f64e7f10/go.mod h1:JWoVf4GJxCxM3iCiZSVoXNMV+JFG49L+ou70KK3HTvQ=
|
github.com/cdipaolo/sentiment v0.0.0-20200617002423-c697f64e7f10/go.mod h1:JWoVf4GJxCxM3iCiZSVoXNMV+JFG49L+ou70KK3HTvQ=
|
||||||
github.com/cenkalti/backoff v2.1.1+incompatible h1:tKJnvO2kl0zmb/jA5UKAt4VoEVw1qxKWjE/Bpp46npY=
|
github.com/cenkalti/backoff v2.1.1+incompatible h1:tKJnvO2kl0zmb/jA5UKAt4VoEVw1qxKWjE/Bpp46npY=
|
||||||
github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
||||||
github.com/cenkalti/backoff/v4 v4.0.0 h1:6VeaLF9aI+MAUQ95106HwWzYZgJJpZ4stumjj6RFYAU=
|
|
||||||
github.com/cenkalti/backoff/v4 v4.0.0/go.mod h1:eEew/i+1Q6OrCDZh3WiXYv3+nJwBASZ8Bog/87DQnVg=
|
github.com/cenkalti/backoff/v4 v4.0.0/go.mod h1:eEew/i+1Q6OrCDZh3WiXYv3+nJwBASZ8Bog/87DQnVg=
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8=
|
|
||||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
github.com/cloudflare/cloudflare-go v0.10.2/go.mod h1:qhVI5MKwBGhdNU89ZRz2plgYutcJ5PCekLxXn56w6SY=
|
github.com/cloudflare/cloudflare-go v0.10.2/go.mod h1:qhVI5MKwBGhdNU89ZRz2plgYutcJ5PCekLxXn56w6SY=
|
||||||
github.com/cloudflare/cloudflare-go v0.10.9 h1:d8KOgLpYiC+Xq3T4tuO+/goM+RZvuO+T4pojuv8giL8=
|
|
||||||
github.com/cloudflare/cloudflare-go v0.10.9/go.mod h1:5TrsWH+3f4NV6WjtS5QFp+DifH81rph40gU374Sh0dQ=
|
github.com/cloudflare/cloudflare-go v0.10.9/go.mod h1:5TrsWH+3f4NV6WjtS5QFp+DifH81rph40gU374Sh0dQ=
|
||||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||||
@@ -157,7 +153,6 @@ github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1
|
|||||||
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
|
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
|
||||||
github.com/dnaeon/go-vcr v0.0.0-20180814043457-aafff18a5cc2/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
|
github.com/dnaeon/go-vcr v0.0.0-20180814043457-aafff18a5cc2/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
|
||||||
github.com/dnsimple/dnsimple-go v0.30.0/go.mod h1:O5TJ0/U6r7AfT8niYNlmohpLbCSG+c71tQlGr9SeGrg=
|
github.com/dnsimple/dnsimple-go v0.30.0/go.mod h1:O5TJ0/U6r7AfT8niYNlmohpLbCSG+c71tQlGr9SeGrg=
|
||||||
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
|
|
||||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||||
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
|
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
|
||||||
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
|
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
|
||||||
@@ -175,12 +170,10 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
|
|||||||
github.com/evanphx/json-patch/v5 v5.0.0 h1:dKTrUeykyQwKb/kx7Z+4ukDs6l+4L41HqG1XHnhX7WE=
|
github.com/evanphx/json-patch/v5 v5.0.0 h1:dKTrUeykyQwKb/kx7Z+4ukDs6l+4L41HqG1XHnhX7WE=
|
||||||
github.com/evanphx/json-patch/v5 v5.0.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4=
|
github.com/evanphx/json-patch/v5 v5.0.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4=
|
||||||
github.com/exoscale/egoscale v0.18.1/go.mod h1:Z7OOdzzTOz1Q1PjQXumlz9Wn/CddH0zSYdCF3rnBKXE=
|
github.com/exoscale/egoscale v0.18.1/go.mod h1:Z7OOdzzTOz1Q1PjQXumlz9Wn/CddH0zSYdCF3rnBKXE=
|
||||||
github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8=
|
|
||||||
github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=
|
github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=
|
||||||
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
|
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
|
||||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||||
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
||||||
github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ=
|
|
||||||
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||||
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
|
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
@@ -316,7 +309,6 @@ github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLe
|
|||||||
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
|
|
||||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
|
github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
|
||||||
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
@@ -329,10 +321,8 @@ github.com/gophercloud/gophercloud v0.3.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEo
|
|||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||||
github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
|
github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
|
||||||
github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
|
|
||||||
github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
|
github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
|
||||||
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||||
github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw=
|
|
||||||
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
@@ -366,8 +356,6 @@ github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df/go.mod h1:QMZY7/J/KSQEhK
|
|||||||
github.com/improbable-eng/grpc-web v0.13.0 h1:7XqtaBWaOCH0cVGKHyvhtcuo6fgW32Y10yRKrDHFHOc=
|
github.com/improbable-eng/grpc-web v0.13.0 h1:7XqtaBWaOCH0cVGKHyvhtcuo6fgW32Y10yRKrDHFHOc=
|
||||||
github.com/improbable-eng/grpc-web v0.13.0/go.mod h1:6hRR09jOEG81ADP5wCQju1z71g6OL4eEvELdran/3cs=
|
github.com/improbable-eng/grpc-web v0.13.0/go.mod h1:6hRR09jOEG81ADP5wCQju1z71g6OL4eEvELdran/3cs=
|
||||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||||
github.com/iverly/go-mcping v1.0.0 h1:zr/6Ko0D2101YslDoEiUg0N2hQP6iLalGQlnfErBlbY=
|
|
||||||
github.com/iverly/go-mcping v1.0.0/go.mod h1:OTdb13yxI0zg1VKs7G53T43N2hAduaPeUcY+PbP608g=
|
|
||||||
github.com/iverly/go-mcping v1.0.1-0.20200818104507-3d8fc23750ae h1:x8jYVK94sexeVwGhw4au/tuOnzzJUa1D5CdU3ZSZfuQ=
|
github.com/iverly/go-mcping v1.0.1-0.20200818104507-3d8fc23750ae h1:x8jYVK94sexeVwGhw4au/tuOnzzJUa1D5CdU3ZSZfuQ=
|
||||||
github.com/iverly/go-mcping v1.0.1-0.20200818104507-3d8fc23750ae/go.mod h1:OTdb13yxI0zg1VKs7G53T43N2hAduaPeUcY+PbP608g=
|
github.com/iverly/go-mcping v1.0.1-0.20200818104507-3d8fc23750ae/go.mod h1:OTdb13yxI0zg1VKs7G53T43N2hAduaPeUcY+PbP608g=
|
||||||
github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0=
|
github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0=
|
||||||
@@ -460,7 +448,6 @@ github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0
|
|||||||
github.com/klauspost/compress v1.13.5 h1:9O69jUPDcsT9fEm74W92rZL9FQY7rCdaXVneq+yyzl4=
|
github.com/klauspost/compress v1.13.5 h1:9O69jUPDcsT9fEm74W92rZL9FQY7rCdaXVneq+yyzl4=
|
||||||
github.com/klauspost/compress v1.13.5/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
github.com/klauspost/compress v1.13.5/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||||
github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||||
github.com/klauspost/cpuid v1.3.1 h1:5JNjFYYQrZeKRJ0734q51WCEEn2huer72Dc7K+R/b6s=
|
|
||||||
github.com/klauspost/cpuid v1.3.1/go.mod h1:bYW4mA6ZgKPob1/Dlai2LviZJO7KGI3uoWLd42rAQw4=
|
github.com/klauspost/cpuid v1.3.1/go.mod h1:bYW4mA6ZgKPob1/Dlai2LviZJO7KGI3uoWLd42rAQw4=
|
||||||
github.com/kolo/xmlrpc v0.0.0-20190717152603-07c4ee3fd181/go.mod h1:o03bZfuBwAXHetKXuInt4S7omeXUu62/A845kiycsSQ=
|
github.com/kolo/xmlrpc v0.0.0-20190717152603-07c4ee3fd181/go.mod h1:o03bZfuBwAXHetKXuInt4S7omeXUu62/A845kiycsSQ=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
@@ -508,14 +495,13 @@ github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Ky
|
|||||||
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||||
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||||
github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||||
github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54=
|
|
||||||
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||||
github.com/mattn/go-sqlite3 v1.14.5 h1:1IdxlwTNazvbKJQSxoJ5/9ECbEeaTTyeU7sEAZ5KKTQ=
|
github.com/mattn/go-sqlite3 v1.14.5 h1:1IdxlwTNazvbKJQSxoJ5/9ECbEeaTTyeU7sEAZ5KKTQ=
|
||||||
github.com/mattn/go-sqlite3 v1.14.5/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI=
|
github.com/mattn/go-sqlite3 v1.14.5/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI=
|
||||||
github.com/mattn/go-tty v0.0.0-20180219170247-931426f7535a/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE=
|
github.com/mattn/go-tty v0.0.0-20180219170247-931426f7535a/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE=
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||||
github.com/micro/micro/v3 v3.9.1-0.20220203152611-b23544122058 h1:xsjuL35jhBpJk0PZGrya/gYm/uQzNWvqzFJdUpWD6lM=
|
github.com/micro/micro/v3 v3.9.1-0.20220216155053-3f60fc42a827 h1:ri67oBlQBVidHb99aNQFZBsBS5qUNGdUGo1+c0/g9ss=
|
||||||
github.com/micro/micro/v3 v3.9.1-0.20220203152611-b23544122058/go.mod h1:fNx55Nv50LZMgd5Prytro1ZNwXphvVID78R6KJ+xLbQ=
|
github.com/micro/micro/v3 v3.9.1-0.20220216155053-3f60fc42a827/go.mod h1:fNx55Nv50LZMgd5Prytro1ZNwXphvVID78R6KJ+xLbQ=
|
||||||
github.com/miekg/dns v1.1.15/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
github.com/miekg/dns v1.1.15/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||||
github.com/miekg/dns v1.1.27/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
github.com/miekg/dns v1.1.27/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
||||||
github.com/miekg/dns v1.1.31 h1:sJFOl9BgwbYAWOGEwr61FU28pqsBNdpRBnhGXtO06Oo=
|
github.com/miekg/dns v1.1.31 h1:sJFOl9BgwbYAWOGEwr61FU28pqsBNdpRBnhGXtO06Oo=
|
||||||
@@ -550,7 +536,6 @@ github.com/o1egl/govatar v0.3.0 h1:hGDsiJJs6qgQ6Ea4JiaukRsUKTY2Ai4dgMEdsYvlUa0=
|
|||||||
github.com/o1egl/govatar v0.3.0/go.mod h1:YeDGDII+2Ji1RcBKvb1KqaPhk4PmuZyBq+rPYc6b+cQ=
|
github.com/o1egl/govatar v0.3.0/go.mod h1:YeDGDII+2Ji1RcBKvb1KqaPhk4PmuZyBq+rPYc6b+cQ=
|
||||||
github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
||||||
github.com/olekukonko/tablewriter v0.0.3/go.mod h1:YZeBtGzYYEsCHp2LST/u/0NDwGkRoBtmn1cIWCJiS6M=
|
github.com/olekukonko/tablewriter v0.0.3/go.mod h1:YZeBtGzYYEsCHp2LST/u/0NDwGkRoBtmn1cIWCJiS6M=
|
||||||
github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8=
|
|
||||||
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
|
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
|
||||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
@@ -630,7 +615,6 @@ github.com/sendgrid/rest v2.6.4+incompatible h1:lq6gAQxLwVBf3mVyCCSHI6mgF+NfaJFJ
|
|||||||
github.com/sendgrid/rest v2.6.4+incompatible/go.mod h1:kXX7q3jZtJXK5c5qK83bSGMdV6tsOE70KbHoqJls4lE=
|
github.com/sendgrid/rest v2.6.4+incompatible/go.mod h1:kXX7q3jZtJXK5c5qK83bSGMdV6tsOE70KbHoqJls4lE=
|
||||||
github.com/sendgrid/sendgrid-go v3.10.0+incompatible h1:aSYyurHxEZSDy7kxhvZ4fH0inNkEEmRssZNbAmETR2c=
|
github.com/sendgrid/sendgrid-go v3.10.0+incompatible h1:aSYyurHxEZSDy7kxhvZ4fH0inNkEEmRssZNbAmETR2c=
|
||||||
github.com/sendgrid/sendgrid-go v3.10.0+incompatible/go.mod h1:QRQt+LX/NmgVEvmdRw0VT/QgUn499+iza2FnDca9fg8=
|
github.com/sendgrid/sendgrid-go v3.10.0+incompatible/go.mod h1:QRQt+LX/NmgVEvmdRw0VT/QgUn499+iza2FnDca9fg8=
|
||||||
github.com/serenize/snaker v0.0.0-20171204205717-a683aaf2d516 h1:ofR1ZdrNSkiWcMsRrubK9tb2/SlZVWttAfqUjJi6QYc=
|
|
||||||
github.com/serenize/snaker v0.0.0-20171204205717-a683aaf2d516/go.mod h1:Yow6lPLSAXx2ifx470yD/nUe22Dv5vBvxK/UK9UUTVs=
|
github.com/serenize/snaker v0.0.0-20171204205717-a683aaf2d516/go.mod h1:Yow6lPLSAXx2ifx470yD/nUe22Dv5vBvxK/UK9UUTVs=
|
||||||
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
|
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
|
||||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||||
@@ -707,7 +691,6 @@ github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6
|
|||||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
|
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
|
||||||
github.com/xeipuuv/gojsonschema v1.1.0/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs=
|
github.com/xeipuuv/gojsonschema v1.1.0/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs=
|
||||||
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca h1:1CFlNzQhALwjS9mBAUkycX616GzgsuYUOCHA5+HSlXI=
|
|
||||||
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
|
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
|
||||||
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA=
|
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA=
|
||||||
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
|
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
|
||||||
@@ -962,7 +945,6 @@ golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac h1:oN6lz7iLW/YC7un8pq+9bOLyXrprv2+DKfkJY+2LJJw=
|
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac h1:oN6lz7iLW/YC7un8pq+9bOLyXrprv2+DKfkJY+2LJJw=
|
||||||
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
|
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
@@ -1229,7 +1211,6 @@ gopkg.in/ns1/ns1-go.v2 v2.0.0-20190730140822-b51389932cbc/go.mod h1:VV+3haRsgDiV
|
|||||||
gopkg.in/resty.v1 v1.9.1/go.mod h1:vo52Hzryw9PnPHcJfPsBiFW62XhNx5OczbV9y+IMpgc=
|
gopkg.in/resty.v1 v1.9.1/go.mod h1:vo52Hzryw9PnPHcJfPsBiFW62XhNx5OczbV9y+IMpgc=
|
||||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||||
gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||||
gopkg.in/square/go-jose.v2 v2.4.1 h1:H0TmLt7/KmzlrDOpa1F+zr0Tk90PbJYBfsVUmRLrf9Y=
|
|
||||||
gopkg.in/square/go-jose.v2 v2.4.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
gopkg.in/square/go-jose.v2 v2.4.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||||
|
|||||||
@@ -10,6 +10,9 @@ import (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
metaOwner = "apikey_owner"
|
metaOwner = "apikey_owner"
|
||||||
|
|
||||||
|
// default tenant
|
||||||
|
defaultId = "micro"
|
||||||
)
|
)
|
||||||
|
|
||||||
// FromContext returns a tenant from the context
|
// FromContext returns a tenant from the context
|
||||||
@@ -62,3 +65,12 @@ func NewContext(id, issuer, owner string) context.Context {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the tenant and default where needed
|
||||||
|
func Id(ctx context.Context) string {
|
||||||
|
id, ok := FromContext(ctx)
|
||||||
|
if !ok {
|
||||||
|
return "micro"
|
||||||
|
}
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user