forked from lug/matterbridge
		
	Compare commits
	
		
			49 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 822605c157 | ||
|   | e49266ae43 | ||
|   | 62e9de1a3b | ||
|   | 2ddc4f7ae9 | ||
|   | 2dd402675d | ||
|   | 25b1af1e11 | ||
|   | 75fb2b8156 | ||
|   | 2a403f8b85 | ||
|   | c3d45a9f06 | ||
|   | c07b85b625 | ||
|   | 511f653e6e | ||
|   | 5636eaca6d | ||
|   | 4b839b9958 | ||
|   | 3f79da84d5 | ||
|   | d540638223 | ||
|   | 4ec9b6dd4e | ||
|   | 3bc219167a | ||
|   | 8a55c97b4e | ||
|   | 9e34162a09 | ||
|   | 860a371eeb | ||
|   | 41a46526a1 | ||
|   | 46b798ac1b | ||
|   | 359d0f2910 | ||
|   | ad3cb0386b | ||
|   | 3a183cb218 | ||
|   | 2eecaccd1c | ||
|   | 5f30a98bc1 | ||
|   | b8a2fcbaff | ||
|   | 01496cd080 | ||
|   | 6a968ab82a | ||
|   | c0c4890887 | ||
|   | 171a53592d | ||
|   | 7811c330db | ||
|   | 9bcd131e66 | ||
|   | c791423dd5 | ||
|   | 80bdf38388 | ||
|   | 9d9cb32f4e | ||
|   | 87229bab13 | ||
|   | f065e9e4d5 | ||
|   | 3812693111 | ||
|   | dd3c572256 | ||
|   | c5dfe40326 | ||
|   | ef278301e3 | ||
|   | 2888fd64b0 | ||
|   | 27c0f37e49 | ||
|   | 0774f6a5e7 | ||
|   | 4036d4459b | ||
|   | ee643de5b6 | ||
|   | 8c7549a09e | 
| @@ -28,7 +28,7 @@ Simple bridge between Mattermost, IRC, XMPP, Gitter, Slack, Discord, Telegram, R | ||||
|  | ||||
| # Requirements | ||||
| Accounts to one of the supported bridges | ||||
| * [Mattermost](https://github.com/mattermost/platform/) 3.5.x - 3.9.x | ||||
| * [Mattermost](https://github.com/mattermost/platform/) 3.5.x - 3.10.x | ||||
| * [IRC](http://www.mirc.com/servers.html) | ||||
| * [XMPP](https://jabber.org) | ||||
| * [Gitter](https://gitter.im) | ||||
| @@ -42,7 +42,7 @@ Accounts to one of the supported bridges | ||||
| # Installing | ||||
| ## Binaries | ||||
| Binaries can be found [here] (https://github.com/42wim/matterbridge/releases/) | ||||
| * Latest stable release [v0.12.0](https://github.com/42wim/matterbridge/releases/latest) | ||||
| * Latest stable release [v0.15.0](https://github.com/42wim/matterbridge/releases/latest) | ||||
|  | ||||
| ## Building | ||||
| Go 1.6+ is required. Make sure you have [Go](https://golang.org/doc/install) properly installed, including setting up your [GOPATH] (https://golang.org/doc/code.html#GOPATH) | ||||
|   | ||||
| @@ -4,6 +4,7 @@ import ( | ||||
| 	"github.com/42wim/matterbridge/bridge/config" | ||||
| 	log "github.com/Sirupsen/logrus" | ||||
| 	"github.com/labstack/echo" | ||||
| 	"github.com/labstack/echo/middleware" | ||||
| 	"github.com/zfjagann/golang-ring" | ||||
| 	"net/http" | ||||
| 	"sync" | ||||
| @@ -20,7 +21,9 @@ type Api struct { | ||||
| type ApiMessage struct { | ||||
| 	Text     string `json:"text"` | ||||
| 	Username string `json:"username"` | ||||
| 	UserID   string `json:"userid"` | ||||
| 	Avatar   string `json:"avatar"` | ||||
| 	Gateway  string `json:"gateway"` | ||||
| } | ||||
|  | ||||
| var flog *log.Entry | ||||
| @@ -38,6 +41,11 @@ func New(cfg config.Protocol, account string, c chan config.Message) *Api { | ||||
| 	b.Config = &cfg | ||||
| 	b.Account = account | ||||
| 	b.Remote = c | ||||
| 	if b.Config.Token != "" { | ||||
| 		e.Use(middleware.KeyAuth(func(key string, c echo.Context) (bool, error) { | ||||
| 			return key == b.Config.Token, nil | ||||
| 		})) | ||||
| 	} | ||||
| 	e.GET("/api/messages", b.handleMessages) | ||||
| 	e.POST("/api/message", b.handlePostMessage) | ||||
| 	go func() { | ||||
| @@ -70,12 +78,16 @@ func (b *Api) handlePostMessage(c echo.Context) error { | ||||
| 	if err := c.Bind(message); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	flog.Debugf("Sending message from %s on %s to gateway", message.Username, "api") | ||||
| 	b.Remote <- config.Message{ | ||||
| 		Text:     message.Text, | ||||
| 		Username: message.Username, | ||||
| 		UserID:   message.UserID, | ||||
| 		Channel:  "api", | ||||
| 		Avatar:   message.Avatar, | ||||
| 		Account:  b.Account, | ||||
| 		Gateway:  message.Gateway, | ||||
| 		Protocol: "api", | ||||
| 	} | ||||
| 	return c.JSON(http.StatusOK, message) | ||||
| } | ||||
| @@ -83,9 +95,7 @@ func (b *Api) handlePostMessage(c echo.Context) error { | ||||
| func (b *Api) handleMessages(c echo.Context) error { | ||||
| 	b.Lock() | ||||
| 	defer b.Unlock() | ||||
| 	for _, msg := range b.Messages.Values() { | ||||
| 		c.JSONPretty(http.StatusOK, msg, " ") | ||||
| 	} | ||||
| 	c.JSONPretty(http.StatusOK, b.Messages.Values(), " ") | ||||
| 	b.Messages = ring.Ring{} | ||||
| 	return nil | ||||
| } | ||||
|   | ||||
| @@ -100,7 +100,7 @@ func (b *Bridge) joinChannels(channels map[string]config.ChannelInfo, exists map | ||||
| 				log.Debugf("using key %s for channel %s", channel.Options.Key, channel.Name) | ||||
| 				mychannel = mychannel + " " + channel.Options.Key | ||||
| 			} | ||||
| 			err := b.JoinChannel(channel.Name) | ||||
| 			err := b.JoinChannel(mychannel) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
|   | ||||
| @@ -16,14 +16,16 @@ const ( | ||||
| ) | ||||
|  | ||||
| type Message struct { | ||||
| 	Text      string | ||||
| 	Channel   string | ||||
| 	Username  string | ||||
| 	Avatar    string | ||||
| 	Account   string | ||||
| 	Event     string | ||||
| 	Protocol  string | ||||
| 	Timestamp time.Time | ||||
| 	Text      string    `json:"text"` | ||||
| 	Channel   string    `json:"channel"` | ||||
| 	Username  string    `json:"username"` | ||||
| 	UserID    string    `json:"userid"` // userid on the bridge | ||||
| 	Avatar    string    `json:"avatar"` | ||||
| 	Account   string    `json:"account"` | ||||
| 	Event     string    `json:"event"` | ||||
| 	Protocol  string    `json:"protocol"` | ||||
| 	Gateway   string    `json:"gateway"` | ||||
| 	Timestamp time.Time `json:"timestamp"` | ||||
| } | ||||
|  | ||||
| type ChannelInfo struct { | ||||
| @@ -43,6 +45,7 @@ type Protocol struct { | ||||
| 	EditDisable            bool   // mattermost, slack, discord, telegram, gitter | ||||
| 	IconURL                string // mattermost, slack | ||||
| 	IgnoreNicks            string // all protocols | ||||
| 	IgnoreMessages         string // all protocols | ||||
| 	Jid                    string // xmpp | ||||
| 	Login                  string // mattermost, matrix | ||||
| 	Muc                    string // xmpp | ||||
| @@ -52,23 +55,26 @@ type Protocol struct { | ||||
| 	NickServNick           string // IRC | ||||
| 	NickServPassword       string // IRC | ||||
| 	NicksPerRow            int    // mattermost, slack | ||||
| 	NoHomeServerSuffix     bool   // matrix | ||||
| 	NoTLS                  bool   // mattermost | ||||
| 	Password               string // IRC,mattermost,XMPP,matrix | ||||
| 	PrefixMessagesWithNick bool   // mattemost, slack | ||||
| 	Protocol               string //all protocols | ||||
| 	MessageQueue           int    // IRC, size of message queue for flood control | ||||
| 	MessageDelay           int    // IRC, time in millisecond to wait between messages | ||||
| 	MessageLength          int    // IRC, max length of a message allowed | ||||
| 	MessageFormat          string // telegram | ||||
| 	RemoteNickFormat       string // all protocols | ||||
| 	Server                 string // IRC,mattermost,XMPP,discord | ||||
| 	ShowJoinPart           bool   // all protocols | ||||
| 	SkipTLSVerify          bool   // IRC, mattermost | ||||
| 	Team                   string // mattermost | ||||
| 	Token                  string // gitter, slack, discord | ||||
| 	Token                  string // gitter, slack, discord, api | ||||
| 	URL                    string // mattermost, slack, matrix | ||||
| 	UseAPI                 bool   // mattermost, slack | ||||
| 	UseSASL                bool   // IRC | ||||
| 	UseTLS                 bool   // IRC | ||||
| 	UseFirstName           bool   // telegram | ||||
| } | ||||
|  | ||||
| type ChannelOptions struct { | ||||
|   | ||||
| @@ -140,8 +140,10 @@ func (b *bdiscord) messageCreate(s *discordgo.Session, m *discordgo.MessageCreat | ||||
| 		m.Message.Content = b.replaceRoleMentions(m.Message.Content) | ||||
| 	} | ||||
| 	m.Message.Content = b.stripCustomoji(m.Message.Content) | ||||
| 	m.Message.Content = b.replaceChannelMentions(m.Message.Content) | ||||
| 	b.Remote <- config.Message{Username: username, Text: m.ContentWithMentionsReplaced(), Channel: channelName, | ||||
| 		Account: b.Account, Avatar: "https://cdn.discordapp.com/avatars/" + m.Author.ID + "/" + m.Author.Avatar + ".jpg"} | ||||
| 		Account: b.Account, Avatar: "https://cdn.discordapp.com/avatars/" + m.Author.ID + "/" + m.Author.Avatar + ".jpg", | ||||
| 		UserID: m.Author.ID} | ||||
| } | ||||
|  | ||||
| func (b *bdiscord) memberUpdate(s *discordgo.Session, m *discordgo.GuildMemberUpdate) { | ||||
| @@ -158,18 +160,21 @@ func (b *bdiscord) getNick(user *discordgo.User) string { | ||||
| 	b.Lock() | ||||
| 	defer b.Unlock() | ||||
| 	if _, ok := b.userMemberMap[user.ID]; ok { | ||||
| 		if b.userMemberMap[user.ID].Nick != "" { | ||||
| 			// only return if nick is set | ||||
| 			return b.userMemberMap[user.ID].Nick | ||||
| 		if b.userMemberMap[user.ID] != nil { | ||||
| 			if b.userMemberMap[user.ID].Nick != "" { | ||||
| 				// only return if nick is set | ||||
| 				return b.userMemberMap[user.ID].Nick | ||||
| 			} | ||||
| 			// otherwise return username | ||||
| 			return user.Username | ||||
| 		} | ||||
| 		// otherwise return username | ||||
| 		return user.Username | ||||
| 	} | ||||
| 	// if we didn't find nick, search for it | ||||
| 	b.userMemberMap[user.ID], err = b.c.GuildMember(b.guildID, user.ID) | ||||
| 	member, err := b.c.GuildMember(b.guildID, user.ID) | ||||
| 	if err != nil { | ||||
| 		return user.Username | ||||
| 	} | ||||
| 	b.userMemberMap[user.ID] = member | ||||
| 	// only return if nick is set | ||||
| 	if b.userMemberMap[user.ID].Nick != "" { | ||||
| 		return b.userMemberMap[user.ID].Nick | ||||
| @@ -211,6 +216,25 @@ func (b *bdiscord) replaceRoleMentions(text string) string { | ||||
| 	return text | ||||
| } | ||||
|  | ||||
| func (b *bdiscord) replaceChannelMentions(text string) string { | ||||
| 	var err error | ||||
| 	re := regexp.MustCompile("<#[0-9]+>") | ||||
| 	text = re.ReplaceAllStringFunc(text, func(m string) string { | ||||
| 		channel := b.getChannelName(m[2 : len(m)-1]) | ||||
| 		// if at first don't succeed, try again | ||||
| 		if channel == "" { | ||||
| 			b.Channels, err = b.c.GuildChannels(b.guildID) | ||||
| 			if err != nil { | ||||
| 				return "#unknownchannel" | ||||
| 			} | ||||
| 			channel = b.getChannelName(m[2 : len(m)-1]) | ||||
| 			return "#" + channel | ||||
| 		} | ||||
| 		return "#" + channel | ||||
| 	}) | ||||
| 	return text | ||||
| } | ||||
|  | ||||
| func (b *bdiscord) stripCustomoji(text string) string { | ||||
| 	// <:doge:302803592035958784> | ||||
| 	re := regexp.MustCompile("<(:.*?:)[0-9]+>") | ||||
|   | ||||
| @@ -82,7 +82,7 @@ func (b *Bgitter) JoinChannel(channel string) error { | ||||
| 				if !strings.HasSuffix(ev.Message.Text, "") { | ||||
| 					flog.Debugf("Sending message from %s on %s to gateway", ev.Message.From.Username, b.Account) | ||||
| 					b.Remote <- config.Message{Username: ev.Message.From.Username, Text: ev.Message.Text, Channel: room, | ||||
| 						Account: b.Account, Avatar: b.getAvatar(ev.Message.From.Username)} | ||||
| 						Account: b.Account, Avatar: b.getAvatar(ev.Message.From.Username), UserID: ev.Message.From.ID} | ||||
| 				} | ||||
| 			case *gitter.GitterConnectionClosed: | ||||
| 				flog.Errorf("connection with gitter closed for room %s", room) | ||||
|   | ||||
| @@ -15,14 +15,15 @@ import ( | ||||
| ) | ||||
|  | ||||
| type Birc struct { | ||||
| 	i         *irc.Connection | ||||
| 	Nick      string | ||||
| 	names     map[string][]string | ||||
| 	Config    *config.Protocol | ||||
| 	Remote    chan config.Message | ||||
| 	connected chan struct{} | ||||
| 	Local     chan config.Message // local queue for flood control | ||||
| 	Account   string | ||||
| 	i               *irc.Connection | ||||
| 	Nick            string | ||||
| 	names           map[string][]string | ||||
| 	Config          *config.Protocol | ||||
| 	Remote          chan config.Message | ||||
| 	connected       chan struct{} | ||||
| 	Local           chan config.Message // local queue for flood control | ||||
| 	Account         string | ||||
| 	FirstConnection bool | ||||
| } | ||||
|  | ||||
| var flog *log.Entry | ||||
| @@ -46,6 +47,10 @@ func New(cfg config.Protocol, account string, c chan config.Message) *Birc { | ||||
| 	if b.Config.MessageQueue == 0 { | ||||
| 		b.Config.MessageQueue = 30 | ||||
| 	} | ||||
| 	if b.Config.MessageLength == 0 { | ||||
| 		b.Config.MessageLength = 400 | ||||
| 	} | ||||
| 	b.FirstConnection = true | ||||
| 	return b | ||||
| } | ||||
|  | ||||
| @@ -71,6 +76,8 @@ func (b *Birc) Connect() error { | ||||
| 	i.SASLLogin = b.Config.NickServNick | ||||
| 	i.SASLPassword = b.Config.NickServPassword | ||||
| 	i.TLSConfig = &tls.Config{InsecureSkipVerify: b.Config.SkipTLSVerify} | ||||
| 	i.KeepAlive = time.Minute | ||||
| 	i.PingFreq = time.Minute | ||||
| 	if b.Config.Password != "" { | ||||
| 		i.Password = b.Config.Password | ||||
| 	} | ||||
| @@ -87,6 +94,14 @@ func (b *Birc) Connect() error { | ||||
| 		return fmt.Errorf("connection timed out") | ||||
| 	} | ||||
| 	i.Debug = false | ||||
| 	// clear on reconnects | ||||
| 	i.ClearCallback(ircm.RPL_WELCOME) | ||||
| 	i.AddCallback(ircm.RPL_WELCOME, func(event *irc.Event) { | ||||
| 		b.Remote <- config.Message{Username: "system", Text: "rejoin", Channel: "", Account: b.Account, Event: config.EVENT_REJOIN_CHANNELS} | ||||
| 		// set our correct nick on reconnect if necessary | ||||
| 		b.Nick = event.Nick | ||||
| 	}) | ||||
| 	go i.Loop() | ||||
| 	go b.doSend() | ||||
| 	return nil | ||||
| } | ||||
| @@ -111,6 +126,9 @@ func (b *Birc) Send(msg config.Message) error { | ||||
| 		b.Command(&msg) | ||||
| 	} | ||||
| 	for _, text := range strings.Split(msg.Text, "\n") { | ||||
| 		if len(text) > b.Config.MessageLength { | ||||
| 			text = text[:b.Config.MessageLength] + " <message clipped>" | ||||
| 		} | ||||
| 		if len(b.Local) < b.Config.MessageQueue { | ||||
| 			if len(b.Local) == b.Config.MessageQueue-1 { | ||||
| 				text = text + " <message clipped>" | ||||
| @@ -186,8 +204,11 @@ func (b *Birc) handleJoinPart(event *irc.Event) { | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
| 	flog.Debugf("Sending JOIN_LEAVE event from %s to gateway", b.Account) | ||||
| 	b.Remote <- config.Message{Username: "system", Text: event.Nick + " " + strings.ToLower(event.Code) + "s", Channel: channel, Account: b.Account, Event: config.EVENT_JOIN_LEAVE} | ||||
| 	if event.Nick != b.Nick { | ||||
| 		flog.Debugf("Sending JOIN_LEAVE event from %s to gateway", b.Account) | ||||
| 		b.Remote <- config.Message{Username: "system", Text: event.Nick + " " + strings.ToLower(event.Code) + "s", Channel: channel, Account: b.Account, Event: config.EVENT_JOIN_LEAVE} | ||||
| 		return | ||||
| 	} | ||||
| 	flog.Debugf("handle %#v", event) | ||||
| } | ||||
|  | ||||
| @@ -208,6 +229,11 @@ func (b *Birc) handleOther(event *irc.Event) { | ||||
| } | ||||
|  | ||||
| func (b *Birc) handlePrivMsg(event *irc.Event) { | ||||
| 	b.Nick = b.i.GetNick() | ||||
| 	// freenode doesn't send 001 as first reply | ||||
| 	if event.Code == "NOTICE" { | ||||
| 		return | ||||
| 	} | ||||
| 	// don't forward queries to the bot | ||||
| 	if event.Arguments[0] == b.Nick { | ||||
| 		return | ||||
| @@ -226,7 +252,7 @@ func (b *Birc) handlePrivMsg(event *irc.Event) { | ||||
| 	re := regexp.MustCompile(`[[:cntrl:]](\d+,|)\d+`) | ||||
| 	msg = re.ReplaceAllString(msg, "") | ||||
| 	flog.Debugf("Sending message from %s on %s to gateway", event.Arguments[0], b.Account) | ||||
| 	b.Remote <- config.Message{Username: event.Nick, Text: msg, Channel: event.Arguments[0], Account: b.Account} | ||||
| 	b.Remote <- config.Message{Username: event.Nick, Text: msg, Channel: event.Arguments[0], Account: b.Account, UserID: event.User + "@" + event.Host} | ||||
| } | ||||
|  | ||||
| func (b *Birc) handleTopicWhoTime(event *irc.Event) { | ||||
|   | ||||
| @@ -1,10 +1,12 @@ | ||||
| package bmatrix | ||||
|  | ||||
| import ( | ||||
| 	"regexp" | ||||
| 	"sync" | ||||
|  | ||||
| 	"github.com/42wim/matterbridge/bridge/config" | ||||
| 	log "github.com/Sirupsen/logrus" | ||||
| 	matrix "github.com/matrix-org/gomatrix" | ||||
| 	"sync" | ||||
| ) | ||||
|  | ||||
| type Bmatrix struct { | ||||
| @@ -101,8 +103,13 @@ func (b *Bmatrix) handlematrix() error { | ||||
| 				flog.Debugf("Unknown room %s", ev.RoomID) | ||||
| 				return | ||||
| 			} | ||||
| 			username := ev.Sender[1:] | ||||
| 			if b.Config.NoHomeServerSuffix { | ||||
| 				re := regexp.MustCompile("(.*?):.*") | ||||
| 				username = re.ReplaceAllString(username, `$1`) | ||||
| 			} | ||||
| 			flog.Debugf("Sending message from %s on %s to gateway", ev.Sender, b.Account) | ||||
| 			b.Remote <- config.Message{Username: ev.Sender, Text: ev.Content["body"].(string), Channel: channel, Account: b.Account} | ||||
| 			b.Remote <- config.Message{Username: username, Text: ev.Content["body"].(string), Channel: channel, Account: b.Account, UserID: ev.Sender} | ||||
| 		} | ||||
| 		flog.Debugf("Received: %#v", ev) | ||||
| 	}) | ||||
|   | ||||
| @@ -21,6 +21,7 @@ type MMMessage struct { | ||||
| 	Text     string | ||||
| 	Channel  string | ||||
| 	Username string | ||||
| 	UserID   string | ||||
| } | ||||
|  | ||||
| type Bmattermost struct { | ||||
| @@ -127,7 +128,7 @@ func (b *Bmattermost) handleMatter() { | ||||
| 	} | ||||
| 	for message := range mchan { | ||||
| 		flog.Debugf("Sending message from %s on %s to gateway", message.Username, b.Account) | ||||
| 		b.Remote <- config.Message{Text: message.Text, Username: message.Username, Channel: message.Channel, Account: b.Account} | ||||
| 		b.Remote <- config.Message{Text: message.Text, Username: message.Username, Channel: message.Channel, Account: b.Account, UserID: message.UserID} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -141,12 +142,16 @@ func (b *Bmattermost) handleMatterClient(mchan chan *MMMessage) { | ||||
| 			b.Remote <- config.Message{Username: "system", Text: message.Text, Channel: message.Channel, Account: b.Account, Event: config.EVENT_JOIN_LEAVE} | ||||
| 			continue | ||||
| 		} | ||||
| 		if (message.Raw.Event == "post_edited") && b.Config.EditDisable { | ||||
| 			continue | ||||
| 		} | ||||
| 		// do not post our own messages back to irc | ||||
| 		// only listen to message from our team | ||||
| 		if (message.Raw.Event == "posted" || message.Raw.Event == "post_edited") && | ||||
| 			b.mc.User.Username != message.Username && message.Raw.Data["team_id"].(string) == b.TeamId { | ||||
| 			flog.Debugf("Receiving from matterclient %#v", message) | ||||
| 			m := &MMMessage{} | ||||
| 			m.UserID = message.UserID | ||||
| 			m.Username = message.Username | ||||
| 			m.Channel = message.Channel | ||||
| 			m.Text = message.Text | ||||
| @@ -168,6 +173,7 @@ func (b *Bmattermost) handleMatterHook(mchan chan *MMMessage) { | ||||
| 		message := b.mh.Receive() | ||||
| 		flog.Debugf("Receiving from matterhook %#v", message) | ||||
| 		m := &MMMessage{} | ||||
| 		m.UserID = message.UserID | ||||
| 		m.Username = message.UserName | ||||
| 		m.Text = message.Text | ||||
| 		m.Channel = message.ChannelName | ||||
|   | ||||
| @@ -82,6 +82,6 @@ func (b *Brocketchat) handleRocketHook() { | ||||
| 			continue | ||||
| 		} | ||||
| 		flog.Debugf("Sending message from %s on %s to gateway", message.UserName, b.Account) | ||||
| 		b.Remote <- config.Message{Text: message.Text, Username: message.UserName, Channel: message.ChannelName, Account: b.Account} | ||||
| 		b.Remote <- config.Message{Text: message.Text, Username: message.UserName, Channel: message.ChannelName, Account: b.Account, UserID: message.UserID} | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -15,6 +15,7 @@ type MMMessage struct { | ||||
| 	Text     string | ||||
| 	Channel  string | ||||
| 	Username string | ||||
| 	UserID   string | ||||
| 	Raw      *slack.MessageEvent | ||||
| } | ||||
|  | ||||
| @@ -89,9 +90,6 @@ func (b *Bslack) JoinChannel(channel string) error { | ||||
|  | ||||
| func (b *Bslack) Send(msg config.Message) error { | ||||
| 	flog.Debugf("Receiving %#v", msg) | ||||
| 	if msg.Account == b.Account { | ||||
| 		return nil | ||||
| 	} | ||||
| 	nick := msg.Username | ||||
| 	message := msg.Text | ||||
| 	channel := msg.Channel | ||||
| @@ -188,7 +186,7 @@ func (b *Bslack) handleSlack() { | ||||
| 		texts := strings.Split(message.Text, "\n") | ||||
| 		for _, text := range texts { | ||||
| 			flog.Debugf("Sending message from %s on %s to gateway", message.Username, b.Account) | ||||
| 			b.Remote <- config.Message{Text: text, Username: message.Username, Channel: message.Channel, Account: b.Account, Avatar: b.getAvatar(message.Username)} | ||||
| 			b.Remote <- config.Message{Text: text, Username: message.Username, Channel: message.Channel, Account: b.Account, Avatar: b.getAvatar(message.Username), UserID: message.UserID} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -216,6 +214,7 @@ func (b *Bslack) handleSlackClient(mchan chan *MMMessage) { | ||||
| 					continue | ||||
| 				} | ||||
| 				m := &MMMessage{} | ||||
| 				m.UserID = user.ID | ||||
| 				m.Username = user.Name | ||||
| 				m.Channel = channel.Name | ||||
| 				m.Text = ev.Text | ||||
|   | ||||
| @@ -76,11 +76,11 @@ func (b *Btelegram) Send(msg config.Message) error { | ||||
| } | ||||
|  | ||||
| func (b *Btelegram) handleRecv(updates <-chan tgbotapi.Update) { | ||||
| 	username := "" | ||||
| 	text := "" | ||||
| 	channel := "" | ||||
| 	for update := range updates { | ||||
| 		var message *tgbotapi.Message | ||||
| 		username := "" | ||||
| 		channel := "" | ||||
| 		text := "" | ||||
| 		// handle channels | ||||
| 		if update.ChannelPost != nil { | ||||
| 			message = update.ChannelPost | ||||
| @@ -98,9 +98,14 @@ func (b *Btelegram) handleRecv(updates <-chan tgbotapi.Update) { | ||||
| 			message.Text = message.Text + b.Config.EditSuffix | ||||
| 		} | ||||
| 		if message.From != nil { | ||||
| 			username = message.From.FirstName | ||||
| 			if b.Config.UseFirstName { | ||||
| 				username = message.From.FirstName | ||||
| 			} | ||||
| 			if username == "" { | ||||
| 				username = message.From.UserName | ||||
| 				if username == "" { | ||||
| 					username = message.From.FirstName | ||||
| 				} | ||||
| 			} | ||||
| 			text = message.Text | ||||
| 			channel = strconv.FormatInt(message.Chat.ID, 10) | ||||
| @@ -109,9 +114,31 @@ func (b *Btelegram) handleRecv(updates <-chan tgbotapi.Update) { | ||||
| 		if username == "" { | ||||
| 			username = "unknown" | ||||
| 		} | ||||
| 		if message.Sticker != nil { | ||||
| 			text = text + " " + b.getFileDirectURL(message.Sticker.FileID) | ||||
| 		} | ||||
| 		if message.Video != nil { | ||||
| 			text = text + " " + b.getFileDirectURL(message.Video.FileID) | ||||
| 		} | ||||
| 		if message.Photo != nil { | ||||
| 			photos := *message.Photo | ||||
| 			// last photo is the biggest | ||||
| 			text = text + " " + b.getFileDirectURL(photos[len(photos)-1].FileID) | ||||
| 		} | ||||
| 		if message.Document != nil { | ||||
| 			text = text + " " + message.Document.FileName + " : " + b.getFileDirectURL(message.Document.FileID) | ||||
| 		} | ||||
| 		if text != "" { | ||||
| 			flog.Debugf("Sending message from %s on %s to gateway", username, b.Account) | ||||
| 			b.Remote <- config.Message{Username: username, Text: text, Channel: channel, Account: b.Account} | ||||
| 			b.Remote <- config.Message{Username: username, Text: text, Channel: channel, Account: b.Account, UserID: strconv.Itoa(message.From.ID)} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (b *Btelegram) getFileDirectURL(id string) string { | ||||
| 	res, err := b.c.GetFileDirectURL(id) | ||||
| 	if err != nil { | ||||
| 		return "" | ||||
| 	} | ||||
| 	return res | ||||
| } | ||||
|   | ||||
| @@ -119,7 +119,7 @@ func (b *Bxmpp) handleXmpp() error { | ||||
| 			var channel, nick string | ||||
| 			if v.Type == "groupchat" { | ||||
| 				s := strings.Split(v.Remote, "@") | ||||
| 				if len(s) == 2 { | ||||
| 				if len(s) >= 2 { | ||||
| 					channel = s[0] | ||||
| 				} | ||||
| 				s = strings.Split(s[1], "/") | ||||
| @@ -128,7 +128,7 @@ func (b *Bxmpp) handleXmpp() error { | ||||
| 				} | ||||
| 				if nick != b.Config.Nick && v.Stamp == nodelay && v.Text != "" { | ||||
| 					flog.Debugf("Sending message from %s on %s to gateway", nick, b.Account) | ||||
| 					b.Remote <- config.Message{Username: nick, Text: v.Text, Channel: channel, Account: b.Account} | ||||
| 					b.Remote <- config.Message{Username: nick, Text: v.Text, Channel: channel, Account: b.Account, UserID: v.Remote} | ||||
| 				} | ||||
| 			} | ||||
| 		case xmpp.Presence: | ||||
|   | ||||
							
								
								
									
										69
									
								
								changelog.md
									
									
									
									
									
								
							
							
						
						
									
										69
									
								
								changelog.md
									
									
									
									
									
								
							| @@ -1,3 +1,62 @@ | ||||
| # v0.15.0 | ||||
| ## New features | ||||
| * general: add option IgnoreMessages for all protocols (see mattebridge.toml.sample) | ||||
|   Messages matching these regexp will be ignored and not sent to other bridges | ||||
|   e.g. IgnoreMessages="^~~ badword" | ||||
| * telegram: add support for sticker/video/photo/document #184 | ||||
|  | ||||
| ## Changes | ||||
| * api: add userid to each message #200 | ||||
|  | ||||
| ## Bugfix | ||||
| * discord: fix crash in memberupdate #198 | ||||
| * mattermost: Fix incorrect behaviour of EditDisable (mattermost). Fixes #197  | ||||
| * irc: Do not relay join/part of ourselves (irc). Closes #190  | ||||
| * irc: make reconnections more robust. #153 | ||||
| * gitter: update library, fixes possible crash | ||||
|  | ||||
| # v0.14.0 | ||||
| ## New features | ||||
| * api: add token authentication | ||||
| * mattermost: add support for mattermost 3.10.0 | ||||
|  | ||||
| ## Changes | ||||
| * api: gateway name is added in JSON messages | ||||
| * api: lowercase JSON keys | ||||
| * api: channel name isn't needed in config #195 | ||||
|  | ||||
| ## Bugfix | ||||
| * discord: Add hashtag to channelname (when translating from id) (discord) | ||||
| * mattermost: Fix a panic. #186 | ||||
| * mattermost: use teamid cache if possible. Fixes a panic | ||||
| * api: post valid json. #185 | ||||
| * api: allow reuse of api in different gateways. #189 | ||||
| * general: Fix utf-8 issues for {NOPINGNICK}. #193 | ||||
|  | ||||
| # v0.13.0 | ||||
| ## New features | ||||
| * irc: Limit message length. ```MessageLength=400``` | ||||
|   Maximum length of message sent to irc server. If it exceeds <message clipped> will be add to the message. | ||||
| * irc: Add NOPINGNICK option.  | ||||
|   The string "{NOPINGNICK}" (case sensitive) will be replaced by the actual nick / username, but with a ZWSP inside the nick, so the irc user with the same nick won't get pinged.    | ||||
|   See https://github.com/42wim/matterbridge/issues/175 for more information | ||||
|  | ||||
| ## Bugfix | ||||
| * slack: Fix sending to different channels on same account (slack). Closes #177 | ||||
| * telegram: Fix incorrect usernames being sent. Closes #181 | ||||
|  | ||||
|  | ||||
| # v0.12.1 | ||||
| ## New features | ||||
| * telegram: Add UseFirstName option (telegram). Closes #144 | ||||
| * matrix: Add NoHomeServerSuffix. Option to disable homeserver on username (matrix). Closes #160. | ||||
|  | ||||
| ## Bugfix | ||||
| * xmpp: Add Compatibility for Cisco Jabber (xmpp) (#166) | ||||
| * irc: Fix JoinChannel argument to use IRC channel key (#172) | ||||
| * discord: Fix possible crash on nil (discord) | ||||
| * discord: Replace long ids in channel metions (discord). Fixes #174 | ||||
|  | ||||
| # v0.12.0 | ||||
| ## Changes | ||||
| * general: edited messages are now being sent by default on discord/mattermost/telegram/slack. See "New Features" | ||||
| @@ -6,10 +65,15 @@ | ||||
| * general: add support for edited messages.  | ||||
|   Add new keyword EditDisable (false/true), default false. Which means by default edited messages will be sent to other bridges. | ||||
|   Add new keyword EditSuffix , default "". You can change this eg to "(edited)", this will be appended to every edit message. | ||||
|  | ||||
| ## Enhancements | ||||
| * mattermost: support mattermost v3.9.x | ||||
| * general: Add support for HTTP{S}_PROXY env variables (#162) | ||||
| * discord: Strip custom emoji metadata (discord). Closes #148 | ||||
|  | ||||
| ## Bugfix | ||||
| * slack: Ignore error on private channel join (slack) Fixes #150  | ||||
| * mattermost: fix crash on reconnects when server is down. Closes #163 | ||||
| * irc: Relay messages starting with ! (irc). Closes #164 | ||||
|  | ||||
| # v0.11.0 | ||||
| ## New features | ||||
| * general: reusing the same account on multiple gateways now also reuses the connection. | ||||
| @@ -18,6 +82,7 @@ | ||||
| * telegram:  Support edited messages (telegram). See #141 | ||||
| * mattermost: Add support for showing/hiding join/leave messages from mattermost. Closes #147 | ||||
| * mattermost: Reconnect on session removal/timeout (mattermost) | ||||
| * mattermost: Support mattermost v3.8.x | ||||
| * irc:  Rejoin channel when kicked (irc). | ||||
|  | ||||
| ## Bugfix | ||||
|   | ||||
| @@ -5,6 +5,7 @@ import ( | ||||
| 	"github.com/42wim/matterbridge/bridge" | ||||
| 	"github.com/42wim/matterbridge/bridge/config" | ||||
| 	log "github.com/Sirupsen/logrus" | ||||
| 	"regexp" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| ) | ||||
| @@ -139,6 +140,9 @@ RECONNECT: | ||||
|  | ||||
| func (gw *Gateway) mapChannels() error { | ||||
| 	for _, br := range append(gw.MyConfig.Out, gw.MyConfig.InOut...) { | ||||
| 		if isApi(br.Account) { | ||||
| 			br.Channel = "api" | ||||
| 		} | ||||
| 		ID := br.Channel + br.Account | ||||
| 		_, ok := gw.Channels[ID] | ||||
| 		if !ok { | ||||
| @@ -153,6 +157,9 @@ func (gw *Gateway) mapChannels() error { | ||||
| 	} | ||||
|  | ||||
| 	for _, br := range append(gw.MyConfig.In, gw.MyConfig.InOut...) { | ||||
| 		if isApi(br.Account) { | ||||
| 			br.Channel = "api" | ||||
| 		} | ||||
| 		ID := br.Channel + br.Account | ||||
| 		_, ok := gw.Channels[ID] | ||||
| 		if !ok { | ||||
| @@ -174,7 +181,7 @@ func (gw *Gateway) getDestChannel(msg *config.Message, dest bridge.Bridge) []con | ||||
| 		if _, ok := gw.Channels[getChannelID(*msg)]; !ok { | ||||
| 			continue | ||||
| 		} | ||||
| 		if channel.Direction == "out" && channel.Account == dest.Account && gw.validGatewayDest(*msg, channel) { | ||||
| 		if channel.Direction == "out" && channel.Account == dest.Account && gw.validGatewayDest(msg, channel) { | ||||
| 			channels = append(channels, *channel) | ||||
| 		} | ||||
| 	} | ||||
| @@ -192,9 +199,10 @@ func (gw *Gateway) handleMessage(msg config.Message, dest *bridge.Bridge) { | ||||
| 		return | ||||
| 	} | ||||
| 	originchannel := msg.Channel | ||||
| 	origmsg := msg | ||||
| 	for _, channel := range gw.DestChannelFunc(&msg, *dest) { | ||||
| 		// do not send to ourself | ||||
| 		if channel.ID == getChannelID(msg) { | ||||
| 		if channel.ID == getChannelID(origmsg) { | ||||
| 			continue | ||||
| 		} | ||||
| 		log.Debugf("Sending %#v from %s (%s) to %s (%s)", msg, msg.Account, originchannel, dest.Account, channel.Name) | ||||
| @@ -223,6 +231,20 @@ func (gw *Gateway) ignoreMessage(msg *config.Message) bool { | ||||
| 			return true | ||||
| 		} | ||||
| 	} | ||||
| 	// TODO do not compile regexps everytime | ||||
| 	for _, entry := range strings.Fields(gw.Bridges[msg.Account].Config.IgnoreMessages) { | ||||
| 		if entry != "" { | ||||
| 			re, err := regexp.Compile(entry) | ||||
| 			if err != nil { | ||||
| 				log.Errorf("incorrect regexp %s for %s", entry, msg.Account) | ||||
| 				continue | ||||
| 			} | ||||
| 			if re.MatchString(msg.Text) { | ||||
| 				log.Debugf("matching %s. ignoring %s from %s", entry, msg.Text, msg.Account) | ||||
| 				return true | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| @@ -233,6 +255,18 @@ func (gw *Gateway) modifyUsername(msg *config.Message, dest *bridge.Bridge) { | ||||
| 	if nick == "" { | ||||
| 		nick = dest.Config.RemoteNickFormat | ||||
| 	} | ||||
| 	if len(msg.Username) > 0 { | ||||
| 		// fix utf-8 issue #193 | ||||
| 		i := 0 | ||||
| 		for index := range msg.Username { | ||||
| 			if i == 1 { | ||||
| 				i = index | ||||
| 				break | ||||
| 			} | ||||
| 			i++ | ||||
| 		} | ||||
| 		nick = strings.Replace(nick, "{NOPINGNICK}", msg.Username[:i]+""+msg.Username[i:], -1) | ||||
| 	} | ||||
| 	nick = strings.Replace(nick, "{NICK}", msg.Username, -1) | ||||
| 	nick = strings.Replace(nick, "{BRIDGE}", br.Name, -1) | ||||
| 	nick = strings.Replace(nick, "{PROTOCOL}", br.Protocol, -1) | ||||
| @@ -254,13 +288,21 @@ func getChannelID(msg config.Message) string { | ||||
| 	return msg.Channel + msg.Account | ||||
| } | ||||
|  | ||||
| func (gw *Gateway) validGatewayDest(msg config.Message, channel *config.ChannelInfo) bool { | ||||
| 	GIDmap := gw.Channels[getChannelID(msg)].GID | ||||
| func (gw *Gateway) validGatewayDest(msg *config.Message, channel *config.ChannelInfo) bool { | ||||
| 	GIDmap := gw.Channels[getChannelID(*msg)].GID | ||||
|  | ||||
| 	// gateway is specified in message (probably from api) | ||||
| 	if msg.Gateway != "" { | ||||
| 		return channel.GID[msg.Gateway] | ||||
| 	} | ||||
|  | ||||
| 	// check if we are running a samechannelgateway. | ||||
| 	// if it is and the channel name matches it's ok, otherwise we shouldn't use this channel. | ||||
| 	for k, _ := range GIDmap { | ||||
| 		if channel.SameChannel[k] == true { | ||||
| 			if msg.Channel == channel.Name { | ||||
| 				// add the gateway to our message | ||||
| 				msg.Gateway = k | ||||
| 				return true | ||||
| 			} else { | ||||
| 				return false | ||||
| @@ -270,8 +312,17 @@ func (gw *Gateway) validGatewayDest(msg config.Message, channel *config.ChannelI | ||||
| 	// check if we are in the correct gateway | ||||
| 	for k, _ := range GIDmap { | ||||
| 		if channel.GID[k] == true { | ||||
| 			// add the gateway to our message | ||||
| 			msg.Gateway = k | ||||
| 			return true | ||||
| 		} | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| func isApi(account string) bool { | ||||
| 	if strings.HasPrefix(account, "api.") { | ||||
| 		return true | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
|   | ||||
| @@ -12,7 +12,7 @@ import ( | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	version = "0.12.0" | ||||
| 	version = "0.15.0" | ||||
| 	githash string | ||||
| ) | ||||
|  | ||||
| @@ -34,7 +34,6 @@ func main() { | ||||
| 		fmt.Printf("version: %s %s\n", version, githash) | ||||
| 		return | ||||
| 	} | ||||
| 	flag.Parse() | ||||
| 	if *flagDebug { | ||||
| 		log.Info("Enabling debug") | ||||
| 		log.SetLevel(log.DebugLevel) | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| #This is configuration for matterbridge. | ||||
| #WARNING: as this file contains credentials, be sure to set correct file permissions | ||||
| ################################################################### | ||||
| #IRC section | ||||
| ################################################################### | ||||
| @@ -48,19 +49,31 @@ MessageDelay=1300 | ||||
|  | ||||
| #Maximum amount of messages to hold in queue. If queue is full  | ||||
| #messages will be dropped.  | ||||
| #<clipped> will be add to the message that fills the queue. | ||||
| #<message clipped> will be add to the message that fills the queue. | ||||
| #OPTIONAL (default 30) | ||||
| MessageQueue=30 | ||||
|  | ||||
| #Maximum length of message sent to irc server. If it exceeds | ||||
| #<message clipped> will be add to the message. | ||||
| #OPTIONAL (default 400) | ||||
| MessageLength=400 | ||||
|  | ||||
| #Nicks you want to ignore.  | ||||
| #Messages from those users will not be sent to other bridges. | ||||
| #OPTIONAL | ||||
| IgnoreNicks="ircspammer1 ircspammer2" | ||||
|  | ||||
| #Messages you want to ignore.  | ||||
| #Messages matching these regexp will be ignored and not sent to other bridges | ||||
| #See https://regex-golang.appspot.com/assets/html/index.html for more regex info | ||||
| #OPTIONAL (example below ignores messages starting with ~~ or messages containing badword | ||||
| IgnoreMessages="^~~ badword" | ||||
|  | ||||
| #RemoteNickFormat defines how remote users appear on this bridge  | ||||
| #The string "{NICK}" (case sensitive) will be replaced by the actual nick / username. | ||||
| #The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge | ||||
| #The string "{PROTOCOL}" (case sensitive) will be replaced by the protocol used by the bridge | ||||
| #The string "{NOPINGNICK}" (case sensitive) will be replaced by the actual nick / username, but with a ZWSP inside the nick, so the irc user with the same nick won't get pinged. See https://github.com/42wim/matterbridge/issues/175 for more information | ||||
| #OPTIONAL (default empty) | ||||
| RemoteNickFormat="[{PROTOCOL}] <{NICK}> " | ||||
|  | ||||
| @@ -108,6 +121,12 @@ SkipTLSVerify=true | ||||
| #OPTIONAL | ||||
| IgnoreNicks="ircspammer1 ircspammer2" | ||||
|  | ||||
| #Messages you want to ignore.  | ||||
| #Messages matching these regexp will be ignored and not sent to other bridges | ||||
| #See https://regex-golang.appspot.com/assets/html/index.html for more regex info | ||||
| #OPTIONAL (example below ignores messages starting with ~~ or messages containing badword | ||||
| IgnoreMessages="^~~ badword" | ||||
|  | ||||
| #RemoteNickFormat defines how remote users appear on this bridge  | ||||
| #The string "{NICK}" (case sensitive) will be replaced by the actual nick / username. | ||||
| #The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge | ||||
| @@ -152,6 +171,12 @@ Nick="yourlogin" | ||||
| #OPTIONAL | ||||
| IgnoreNicks="spammer1 spammer2" | ||||
|  | ||||
| #Messages you want to ignore.  | ||||
| #Messages matching these regexp will be ignored and not sent to other bridges | ||||
| #See https://regex-golang.appspot.com/assets/html/index.html for more regex info | ||||
| #OPTIONAL (example below ignores messages starting with ~~ or messages containing badword | ||||
| IgnoreMessages="^~~ badword" | ||||
|  | ||||
| #RemoteNickFormat defines how remote users appear on this bridge  | ||||
| #The string "{NICK}" (case sensitive) will be replaced by the actual nick / username. | ||||
| #The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge | ||||
| @@ -254,6 +279,12 @@ EditSuffix=" (edited)" | ||||
| #OPTIONAL | ||||
| IgnoreNicks="ircspammer1 ircspammer2" | ||||
|  | ||||
| #Messages you want to ignore.  | ||||
| #Messages matching these regexp will be ignored and not sent to other bridges | ||||
| #See https://regex-golang.appspot.com/assets/html/index.html for more regex info | ||||
| #OPTIONAL (example below ignores messages starting with ~~ or messages containing badword | ||||
| IgnoreMessages="^~~ badword" | ||||
|  | ||||
| #RemoteNickFormat defines how remote users appear on this bridge  | ||||
| #The string "{NICK}" (case sensitive) will be replaced by the actual nick / username. | ||||
| #The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge | ||||
| @@ -287,6 +318,12 @@ Token="Yourtokenhere" | ||||
| #OPTIONAL | ||||
| IgnoreNicks="ircspammer1 ircspammer2" | ||||
|  | ||||
| #Messages you want to ignore.  | ||||
| #Messages matching these regexp will be ignored and not sent to other bridges | ||||
| #See https://regex-golang.appspot.com/assets/html/index.html for more regex info | ||||
| #OPTIONAL (example below ignores messages starting with ~~ or messages containing badword | ||||
| IgnoreMessages="^~~ badword" | ||||
|  | ||||
| #RemoteNickFormat defines how remote users appear on this bridge  | ||||
| #The string "{NICK}" (case sensitive) will be replaced by the actual nick / username. | ||||
| #The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge | ||||
| @@ -376,6 +413,12 @@ PrefixMessagesWithNick=false | ||||
| #OPTIONAL | ||||
| IgnoreNicks="ircspammer1 ircspammer2" | ||||
|  | ||||
| #Messages you want to ignore.  | ||||
| #Messages matching these regexp will be ignored and not sent to other bridges | ||||
| #See https://regex-golang.appspot.com/assets/html/index.html for more regex info | ||||
| #OPTIONAL (example below ignores messages starting with ~~ or messages containing badword | ||||
| IgnoreMessages="^~~ badword" | ||||
|  | ||||
| #RemoteNickFormat defines how remote users appear on this bridge  | ||||
| #The string "{NICK}" (case sensitive) will be replaced by the actual nick / username. | ||||
| #The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge | ||||
| @@ -420,6 +463,12 @@ EditSuffix=" (edited)" | ||||
| #OPTIONAL | ||||
| IgnoreNicks="ircspammer1 ircspammer2" | ||||
|  | ||||
| #Messages you want to ignore.  | ||||
| #Messages matching these regexp will be ignored and not sent to other bridges | ||||
| #See https://regex-golang.appspot.com/assets/html/index.html for more regex info | ||||
| #OPTIONAL (example below ignores messages starting with ~~ or messages containing badword | ||||
| IgnoreMessages="^~~ badword" | ||||
|  | ||||
| #RemoteNickFormat defines how remote users appear on this bridge  | ||||
| #The string "{NICK}" (case sensitive) will be replaced by the actual nick / username. | ||||
| #The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge | ||||
| @@ -451,6 +500,12 @@ Token="Yourtokenhere" | ||||
| #See https://core.telegram.org/bots/api#html-style | ||||
| MessageFormat="" | ||||
|  | ||||
| #If enabled use the "First Name" as username. If this is empty use the Username | ||||
| #If disabled use the "Username" as username. If this is empty use the First Name  | ||||
| #If all names are empty, username will be "unknown" | ||||
| #OPTIONAL (default false) | ||||
| UseFirstName=false | ||||
|  | ||||
| #Disable sending of edits to other bridges | ||||
| #OPTIONAL (default false) | ||||
| EditDisable=false | ||||
| @@ -464,6 +519,12 @@ EditSuffix=" (edited)" | ||||
| #OPTIONAL | ||||
| IgnoreNicks="spammer1 spammer2" | ||||
|  | ||||
| #Messages you want to ignore.  | ||||
| #Messages matching these regexp will be ignored and not sent to other bridges | ||||
| #See https://regex-golang.appspot.com/assets/html/index.html for more regex info | ||||
| #OPTIONAL (example below ignores messages starting with ~~ or messages containing badword | ||||
| IgnoreMessages="^~~ badword" | ||||
|  | ||||
| #RemoteNickFormat defines how remote users appear on this bridge  | ||||
| #The string "{NICK}" (case sensitive) will be replaced by the actual nick / username. | ||||
| #The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge | ||||
| @@ -522,6 +583,12 @@ PrefixMessagesWithNick=false | ||||
| #OPTIONAL | ||||
| IgnoreNicks="ircspammer1 ircspammer2" | ||||
|  | ||||
| #Messages you want to ignore.  | ||||
| #Messages matching these regexp will be ignored and not sent to other bridges | ||||
| #See https://regex-golang.appspot.com/assets/html/index.html for more regex info | ||||
| #OPTIONAL (example below ignores messages starting with ~~ or messages containing badword | ||||
| IgnoreMessages="^~~ badword" | ||||
|  | ||||
| #RemoteNickFormat defines how remote users appear on this bridge  | ||||
| #The string "{NICK}" (case sensitive) will be replaced by the actual nick / username. | ||||
| #The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge | ||||
| @@ -554,6 +621,11 @@ Server="https://matrix.org" | ||||
| Login="yourlogin" | ||||
| Password="yourpass" | ||||
|  | ||||
| #Whether to send the homeserver suffix. eg ":matrix.org" in @username:matrix.org | ||||
| #to other bridges, or only send "username".(true only sends username) | ||||
| #OPTIONAL (default false) | ||||
| NoHomeServerSuffix=false | ||||
|  | ||||
| #Whether to prefix messages from other bridges to matrix with the sender's nick.  | ||||
| #Useful if username overrides for incoming webhooks isn't enabled on the  | ||||
| #matrix server. If you set PrefixMessagesWithNick to true, each message  | ||||
| @@ -566,6 +638,12 @@ PrefixMessagesWithNick=false | ||||
| #OPTIONAL | ||||
| IgnoreNicks="spammer1 spammer2" | ||||
|  | ||||
| #Messages you want to ignore.  | ||||
| #Messages matching these regexp will be ignored and not sent to other bridges | ||||
| #See https://regex-golang.appspot.com/assets/html/index.html for more regex info | ||||
| #OPTIONAL (example below ignores messages starting with ~~ or messages containing badword | ||||
| IgnoreMessages="^~~ badword" | ||||
|  | ||||
| #RemoteNickFormat defines how remote users appear on this bridge  | ||||
| #The string "{NICK}" (case sensitive) will be replaced by the actual nick / username. | ||||
| #The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge | ||||
| @@ -595,6 +673,11 @@ BindAddress="127.0.0.1:4242" | ||||
| #Amount of messages to keep in memory | ||||
| Buffer=1000 | ||||
|  | ||||
| #Bearer token used for authentication | ||||
| #curl -H "Authorization: Bearer token" http://localhost:4242/api/messages | ||||
| #OPTIONAL (no authorization if token is empty) | ||||
| Token="mytoken" | ||||
|  | ||||
| #RemoteNickFormat defines how remote users appear on this bridge  | ||||
| #The string "{NICK}" (case sensitive) will be replaced by the actual nick / username. | ||||
| #The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge | ||||
| @@ -602,6 +685,8 @@ Buffer=1000 | ||||
| #OPTIONAL (default empty) | ||||
| RemoteNickFormat="{NICK}" | ||||
|  | ||||
|  | ||||
|  | ||||
| ################################################################### | ||||
| #General configuration | ||||
| ################################################################### | ||||
| @@ -645,7 +730,7 @@ enable=true | ||||
|     #channel to connect on that account | ||||
|     #How to specify them for the different bridges: | ||||
|     # | ||||
|     #irc        - #channel (# is required) | ||||
|     #irc        - #channel (# is required) (this needs to be lowercase!) | ||||
|     #mattermost - channel (the channel name as seen in the URL, not the displayname) | ||||
|     #gitter     - username/room  | ||||
|     #xmpp       - channel | ||||
| @@ -656,8 +741,9 @@ enable=true | ||||
|     #telegram   - chatid (a large negative number, eg -123456789) | ||||
|     #             see (https://www.linkedin.com/pulse/telegram-bots-beginners-marco-frau) | ||||
|     #hipchat    - id_channel (see https://www.hipchat.com/account/xmpp for the correct channel) | ||||
|     #rocketchat - #channel (# is required) | ||||
|     #matrix     - #channel:server (eg #yourchannel:matrix.org) | ||||
|     #rocketchat - #channel (# is required (also needed for private channels!) | ||||
|     #matrix     - #channel:server (eg #yourchannel:matrix.org)  | ||||
|     #           - encrypted rooms are not supported in matrix | ||||
|     #REQUIRED | ||||
|     channel="#testing" | ||||
|  | ||||
| @@ -693,7 +779,9 @@ enable=true | ||||
|     #account="api.local" | ||||
|     #channel="api" | ||||
|     #To send data to the api: | ||||
|     #curl -XPOST -H 'Content-Type: application/json'  -d '{"text":"test","username":"randomuser"}' http://localhost:4242/api/message | ||||
|     #curl -XPOST -H 'Content-Type: application/json'  -d '{"text":"test","username":"randomuser","gateway":"gateway1"}' http://localhost:4242/api/message | ||||
|     #To read from the api: | ||||
|     #curl http://localhost:4242/api/messages | ||||
|  | ||||
| #If you want to do a 1:1 mapping between protocols where the channelnames are the same | ||||
| #e.g. slack and mattermost you can use the samechannelgateway configuration | ||||
|   | ||||
| @@ -1,3 +1,4 @@ | ||||
| #WARNING: as this file contains credentials, be sure to set correct file permissions | ||||
| [irc] | ||||
|     [irc.freenode] | ||||
|     Server="irc.freenode.net:6667" | ||||
| @@ -16,22 +17,14 @@ | ||||
| [[gateway]] | ||||
| name="gateway1" | ||||
| enable=true | ||||
|     [[gateway.in]] | ||||
|     [[gateway.inout]] | ||||
|     account="irc.freenode" | ||||
|     channel="#testing" | ||||
|  | ||||
|     [[gateway.out]] | ||||
|     account="irc.freenode" | ||||
|     channel="#testing" | ||||
|  | ||||
|     [[gateway.in]] | ||||
|     [[gateway.inout]] | ||||
|     account="mattermost.work" | ||||
|     channel="off-topic" | ||||
|      | ||||
|     [[gateway.out]] | ||||
|     account="mattermost.work" | ||||
|     channel="off-topic" | ||||
|  | ||||
| #simpler config possible since v0.10.2 | ||||
| #[[gateway]] | ||||
| #name="gateway2" | ||||
|   | ||||
| @@ -37,6 +37,7 @@ type Message struct { | ||||
| 	Username string | ||||
| 	Text     string | ||||
| 	Type     string | ||||
| 	UserID   string | ||||
| } | ||||
|  | ||||
| type Team struct { | ||||
| @@ -288,15 +289,15 @@ func (m *MMClient) parseActionPost(rmsg *Message) { | ||||
| 	if m.GetUser(data.UserId) == nil { | ||||
| 		m.UpdateUsers() | ||||
| 	} | ||||
| 	rmsg.Username = m.GetUser(data.UserId).Username | ||||
| 	rmsg.Username = m.GetUserName(data.UserId) | ||||
| 	rmsg.Channel = m.GetChannelName(data.ChannelId) | ||||
| 	rmsg.UserID = data.UserId | ||||
| 	rmsg.Type = data.Type | ||||
| 	teamid, _ := rmsg.Raw.Data["team_id"].(string) | ||||
| 	// edit messsages have no team_id for some reason | ||||
| 	if teamid == "" { | ||||
| 		// we can find the team_id from the channelid | ||||
| 		result, _ := m.Client.GetChannel(data.ChannelId, "") | ||||
| 		teamid = result.Data.(*model.ChannelData).Channel.TeamId | ||||
| 		teamid = m.GetChannelTeamId(data.ChannelId) | ||||
| 		rmsg.Raw.Data["team_id"] = teamid | ||||
| 	} | ||||
| 	if teamid != "" { | ||||
| @@ -328,7 +329,7 @@ func (m *MMClient) UpdateChannels() error { | ||||
| 		return errors.New(err.DetailedError) | ||||
| 	} | ||||
| 	var mmchannels2 *model.Result | ||||
| 	if m.mmVersion() >= 3.8 { | ||||
| 	if m.mmVersion() >= 3.08 { | ||||
| 		mmchannels2, err = m.Client.GetMoreChannelsPage(0, 5000) | ||||
| 	} else { | ||||
| 		mmchannels2, err = m.Client.GetMoreChannels("") | ||||
| @@ -374,6 +375,19 @@ func (m *MMClient) GetChannelId(name string, teamId string) string { | ||||
| 	return "" | ||||
| } | ||||
|  | ||||
| func (m *MMClient) GetChannelTeamId(id string) string { | ||||
| 	m.RLock() | ||||
| 	defer m.RUnlock() | ||||
| 	for _, t := range append(m.OtherTeams, m.Team) { | ||||
| 		for _, channel := range append(*t.Channels, *t.MoreChannels...) { | ||||
| 			if channel.Id == id { | ||||
| 				return channel.TeamId | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return "" | ||||
| } | ||||
|  | ||||
| func (m *MMClient) GetChannelHeader(channelId string) string { | ||||
| 	m.RLock() | ||||
| 	defer m.RUnlock() | ||||
| @@ -467,7 +481,7 @@ func (m *MMClient) UpdateChannelHeader(channelId string, header string) { | ||||
|  | ||||
| func (m *MMClient) UpdateLastViewed(channelId string) { | ||||
| 	m.log.Debugf("posting lastview %#v", channelId) | ||||
| 	if m.mmVersion() >= 3.8 { | ||||
| 	if m.mmVersion() >= 3.08 { | ||||
| 		view := model.ChannelView{ChannelId: channelId} | ||||
| 		res, _ := m.Client.ViewChannel(view) | ||||
| 		if res == false { | ||||
| @@ -621,6 +635,14 @@ func (m *MMClient) GetUser(userId string) *model.User { | ||||
| 	return m.Users[userId] | ||||
| } | ||||
|  | ||||
| func (m *MMClient) GetUserName(userId string) string { | ||||
| 	user := m.GetUser(userId) | ||||
| 	if user != nil { | ||||
| 		return user.Username | ||||
| 	} | ||||
| 	return "" | ||||
| } | ||||
|  | ||||
| func (m *MMClient) GetStatus(userId string) string { | ||||
| 	res, err := m.Client.GetStatuses() | ||||
| 	if err != nil { | ||||
| @@ -708,7 +730,7 @@ func (m *MMClient) initUser() error { | ||||
| 			return errors.New(err.DetailedError) | ||||
| 		} | ||||
| 		t.Channels = mmchannels.Data.(*model.ChannelList) | ||||
| 		if m.mmVersion() >= 3.8 { | ||||
| 		if m.mmVersion() >= 3.08 { | ||||
| 			mmchannels, err = m.Client.GetMoreChannelsPage(0, 5000) | ||||
| 		} else { | ||||
| 			mmchannels, err = m.Client.GetMoreChannels("") | ||||
| @@ -742,7 +764,10 @@ func (m *MMClient) sendWSRequest(action string, data map[string]interface{}) err | ||||
| } | ||||
|  | ||||
| func (m *MMClient) mmVersion() float64 { | ||||
| 	v, _ := strconv.ParseFloat(m.ServerVersion[0:3], 64) | ||||
| 	v, _ := strconv.ParseFloat(string(m.ServerVersion[0:2])+"0"+string(m.ServerVersion[2]), 64) | ||||
| 	if string(m.ServerVersion[4]) == "." { | ||||
| 		v, _ = strconv.ParseFloat(m.ServerVersion[0:4], 64) | ||||
| 	} | ||||
| 	return v | ||||
| } | ||||
|  | ||||
| @@ -751,7 +776,8 @@ func supportedVersion(version string) bool { | ||||
| 		strings.HasPrefix(version, "3.6.0") || | ||||
| 		strings.HasPrefix(version, "3.7.0") || | ||||
| 		strings.HasPrefix(version, "3.8.0") || | ||||
| 		strings.HasPrefix(version, "3.9.0") { | ||||
| 		strings.HasPrefix(version, "3.9.0") || | ||||
| 		strings.HasPrefix(version, "3.10.0") { | ||||
| 		return true | ||||
| 	} | ||||
| 	return false | ||||
|   | ||||
							
								
								
									
										18
									
								
								vendor/github.com/labstack/echo/bind.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										18
									
								
								vendor/github.com/labstack/echo/bind.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -30,16 +30,16 @@ type ( | ||||
| // Bind implements the `Binder#Bind` function. | ||||
| func (b *DefaultBinder) Bind(i interface{}, c Context) (err error) { | ||||
| 	req := c.Request() | ||||
| 	if req.Method == GET { | ||||
| 		if err = b.bindData(i, c.QueryParams(), "query"); err != nil { | ||||
| 			return NewHTTPError(http.StatusBadRequest, err.Error()) | ||||
| 		} | ||||
| 		return | ||||
| 	} | ||||
| 	ctype := req.Header.Get(HeaderContentType) | ||||
| 	if req.ContentLength == 0 { | ||||
| 		if req.Method == GET || req.Method == DELETE { | ||||
| 			if err = b.bindData(i, c.QueryParams(), "query"); err != nil { | ||||
| 				return NewHTTPError(http.StatusBadRequest, err.Error()) | ||||
| 			} | ||||
| 			return | ||||
| 		} | ||||
| 		return NewHTTPError(http.StatusBadRequest, "Request body can't be empty") | ||||
| 	} | ||||
| 	ctype := req.Header.Get(HeaderContentType) | ||||
| 	switch { | ||||
| 	case strings.HasPrefix(ctype, MIMEApplicationJSON): | ||||
| 		if err = json.NewDecoder(req.Body).Decode(i); err != nil { | ||||
| @@ -51,7 +51,7 @@ func (b *DefaultBinder) Bind(i interface{}, c Context) (err error) { | ||||
| 				return NewHTTPError(http.StatusBadRequest, err.Error()) | ||||
| 			} | ||||
| 		} | ||||
| 	case strings.HasPrefix(ctype, MIMEApplicationXML): | ||||
| 	case strings.HasPrefix(ctype, MIMEApplicationXML), strings.HasPrefix(ctype, MIMETextXML): | ||||
| 		if err = xml.NewDecoder(req.Body).Decode(i); err != nil { | ||||
| 			if ute, ok := err.(*xml.UnsupportedTypeError); ok { | ||||
| 				return NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Unsupported type error: type=%v, error=%v", ute.Type, ute.Error())) | ||||
| @@ -142,6 +142,8 @@ func setWithProperType(valueKind reflect.Kind, val string, structField reflect.V | ||||
| 	} | ||||
|  | ||||
| 	switch valueKind { | ||||
| 	case reflect.Ptr: | ||||
| 		return setWithProperType(structField.Elem().Kind(), val, structField.Elem()) | ||||
| 	case reflect.Int: | ||||
| 		return setIntField(val, 0, structField) | ||||
| 	case reflect.Int8: | ||||
|   | ||||
							
								
								
									
										47
									
								
								vendor/github.com/labstack/echo/context.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										47
									
								
								vendor/github.com/labstack/echo/context.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -31,6 +31,9 @@ type ( | ||||
| 		// IsTLS returns true if HTTP connection is TLS otherwise false. | ||||
| 		IsTLS() bool | ||||
|  | ||||
| 		// IsWebSocket returns true if HTTP connection is WebSocket otherwise false. | ||||
| 		IsWebSocket() bool | ||||
|  | ||||
| 		// Scheme returns the HTTP protocol scheme, `http` or `https`. | ||||
| 		Scheme() string | ||||
|  | ||||
| @@ -219,19 +222,36 @@ func (c *context) IsTLS() bool { | ||||
| 	return c.request.TLS != nil | ||||
| } | ||||
|  | ||||
| func (c *context) IsWebSocket() bool { | ||||
| 	upgrade := c.request.Header.Get(HeaderUpgrade) | ||||
| 	return upgrade == "websocket" || upgrade == "Websocket" | ||||
| } | ||||
|  | ||||
| func (c *context) Scheme() string { | ||||
| 	// Can't use `r.Request.URL.Scheme` | ||||
| 	// See: https://groups.google.com/forum/#!topic/golang-nuts/pMUkBlQBDF0 | ||||
| 	if c.IsTLS() { | ||||
| 		return "https" | ||||
| 	} | ||||
| 	if scheme := c.request.Header.Get(HeaderXForwardedProto); scheme != "" { | ||||
| 		return scheme | ||||
| 	} | ||||
| 	if scheme := c.request.Header.Get(HeaderXForwardedProtocol); scheme != "" { | ||||
| 		return scheme | ||||
| 	} | ||||
| 	if ssl := c.request.Header.Get(HeaderXForwardedSsl); ssl == "on" { | ||||
| 		return "https" | ||||
| 	} | ||||
| 	if scheme := c.request.Header.Get(HeaderXUrlScheme); scheme != "" { | ||||
| 		return scheme | ||||
| 	} | ||||
| 	return "http" | ||||
| } | ||||
|  | ||||
| func (c *context) RealIP() string { | ||||
| 	ra := c.request.RemoteAddr | ||||
| 	if ip := c.request.Header.Get(HeaderXForwardedFor); ip != "" { | ||||
| 		ra = ip | ||||
| 		ra = strings.Split(ip, ", ")[0] | ||||
| 	} else if ip := c.request.Header.Get(HeaderXRealIP); ip != "" { | ||||
| 		ra = ip | ||||
| 	} else { | ||||
| @@ -275,7 +295,7 @@ func (c *context) SetParamNames(names ...string) { | ||||
| } | ||||
|  | ||||
| func (c *context) ParamValues() []string { | ||||
| 	return c.pvalues | ||||
| 	return c.pvalues[:len(c.pnames)] | ||||
| } | ||||
|  | ||||
| func (c *context) SetParamValues(values ...string) { | ||||
| @@ -385,7 +405,8 @@ func (c *context) String(code int, s string) (err error) { | ||||
| } | ||||
|  | ||||
| func (c *context) JSON(code int, i interface{}) (err error) { | ||||
| 	if c.echo.Debug { | ||||
| 	_, pretty := c.QueryParams()["pretty"] | ||||
| 	if c.echo.Debug || pretty { | ||||
| 		return c.JSONPretty(code, i, "  ") | ||||
| 	} | ||||
| 	b, err := json.Marshal(i) | ||||
| @@ -429,7 +450,8 @@ func (c *context) JSONPBlob(code int, callback string, b []byte) (err error) { | ||||
| } | ||||
|  | ||||
| func (c *context) XML(code int, i interface{}) (err error) { | ||||
| 	if c.echo.Debug { | ||||
| 	_, pretty := c.QueryParams()["pretty"] | ||||
| 	if c.echo.Debug || pretty { | ||||
| 		return c.XMLPretty(code, i, "  ") | ||||
| 	} | ||||
| 	b, err := xml.Marshal(i) | ||||
| @@ -471,7 +493,12 @@ func (c *context) Stream(code int, contentType string, r io.Reader) (err error) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| func (c *context) File(file string) error { | ||||
| func (c *context) File(file string) (err error) { | ||||
| 	file, err = url.QueryUnescape(file) // Issue #839 | ||||
| 	if err != nil { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	f, err := os.Open(file) | ||||
| 	if err != nil { | ||||
| 		return ErrNotFound | ||||
| @@ -487,11 +514,11 @@ func (c *context) File(file string) error { | ||||
| 		} | ||||
| 		defer f.Close() | ||||
| 		if fi, err = f.Stat(); err != nil { | ||||
| 			return err | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
| 	http.ServeContent(c.Response(), c.Request(), fi.Name(), fi.ModTime(), f) | ||||
| 	return nil | ||||
| 	return | ||||
| } | ||||
|  | ||||
| func (c *context) Attachment(file, name string) (err error) { | ||||
| @@ -514,7 +541,7 @@ func (c *context) NoContent(code int) error { | ||||
| } | ||||
|  | ||||
| func (c *context) Redirect(code int, url string) error { | ||||
| 	if code < http.StatusMultipleChoices || code > http.StatusTemporaryRedirect { | ||||
| 	if code < 300 || code > 308 { | ||||
| 		return ErrInvalidRedirectCode | ||||
| 	} | ||||
| 	c.response.Header().Set(HeaderLocation, url) | ||||
| @@ -548,4 +575,8 @@ func (c *context) Reset(r *http.Request, w http.ResponseWriter) { | ||||
| 	c.query = nil | ||||
| 	c.handler = NotFoundHandler | ||||
| 	c.store = nil | ||||
| 	c.path = "" | ||||
| 	c.pnames = nil | ||||
| 	// NOTE: Don't reset because it has to have length c.echo.maxParam at all times | ||||
| 	// c.pvalues = nil | ||||
| } | ||||
|   | ||||
							
								
								
									
										26
									
								
								vendor/github.com/labstack/echo/cookbook/auto-tls/server.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										26
									
								
								vendor/github.com/labstack/echo/cookbook/auto-tls/server.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,26 +0,0 @@ | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"net/http" | ||||
|  | ||||
| 	"golang.org/x/crypto/acme/autocert" | ||||
|  | ||||
| 	"github.com/labstack/echo" | ||||
| 	"github.com/labstack/echo/middleware" | ||||
| ) | ||||
|  | ||||
| func main() { | ||||
| 	e := echo.New() | ||||
| 	// e.AutoTLSManager.HostPolicy = autocert.HostWhitelist("<DOMAIN>") | ||||
| 	// Cache certificates | ||||
| 	e.AutoTLSManager.Cache = autocert.DirCache("/var/www/.cache") | ||||
| 	e.Use(middleware.Recover()) | ||||
| 	e.Use(middleware.Logger()) | ||||
| 	e.GET("/", func(c echo.Context) error { | ||||
| 		return c.HTML(http.StatusOK, ` | ||||
| 			<h1>Welcome to Echo!</h1> | ||||
| 			<h3>TLS certificates automatically installed from Let's Encrypt :)</h3> | ||||
| 		`) | ||||
| 	}) | ||||
| 	e.Logger.Fatal(e.StartAutoTLS(":443")) | ||||
| } | ||||
							
								
								
									
										38
									
								
								vendor/github.com/labstack/echo/cookbook/cors/server.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										38
									
								
								vendor/github.com/labstack/echo/cookbook/cors/server.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,38 +0,0 @@ | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"net/http" | ||||
|  | ||||
| 	"github.com/labstack/echo" | ||||
| 	"github.com/labstack/echo/middleware" | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	users = []string{"Joe", "Veer", "Zion"} | ||||
| ) | ||||
|  | ||||
| func getUsers(c echo.Context) error { | ||||
| 	return c.JSON(http.StatusOK, users) | ||||
| } | ||||
|  | ||||
| func main() { | ||||
| 	e := echo.New() | ||||
| 	e.Use(middleware.Logger()) | ||||
| 	e.Use(middleware.Recover()) | ||||
|  | ||||
| 	// CORS default | ||||
| 	// Allows requests from any origin wth GET, HEAD, PUT, POST or DELETE method. | ||||
| 	// e.Use(middleware.CORS()) | ||||
|  | ||||
| 	// CORS restricted | ||||
| 	// Allows requests from any `https://labstack.com` or `https://labstack.net` origin | ||||
| 	// wth GET, PUT, POST or DELETE method. | ||||
| 	e.Use(middleware.CORSWithConfig(middleware.CORSConfig{ | ||||
| 		AllowOrigins: []string{"https://labstack.com", "https://labstack.net"}, | ||||
| 		AllowMethods: []string{echo.GET, echo.PUT, echo.POST, echo.DELETE}, | ||||
| 	})) | ||||
|  | ||||
| 	e.GET("/api/users", getUsers) | ||||
|  | ||||
| 	e.Logger.Fatal(e.Start(":1323")) | ||||
| } | ||||
							
								
								
									
										75
									
								
								vendor/github.com/labstack/echo/cookbook/crud/server.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										75
									
								
								vendor/github.com/labstack/echo/cookbook/crud/server.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,75 +0,0 @@ | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"net/http" | ||||
| 	"strconv" | ||||
|  | ||||
| 	"github.com/labstack/echo" | ||||
| 	"github.com/labstack/echo/middleware" | ||||
| ) | ||||
|  | ||||
| type ( | ||||
| 	user struct { | ||||
| 		ID   int    `json:"id"` | ||||
| 		Name string `json:"name"` | ||||
| 	} | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	users = map[int]*user{} | ||||
| 	seq   = 1 | ||||
| ) | ||||
|  | ||||
| //---------- | ||||
| // Handlers | ||||
| //---------- | ||||
|  | ||||
| func createUser(c echo.Context) error { | ||||
| 	u := &user{ | ||||
| 		ID: seq, | ||||
| 	} | ||||
| 	if err := c.Bind(u); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	users[u.ID] = u | ||||
| 	seq++ | ||||
| 	return c.JSON(http.StatusCreated, u) | ||||
| } | ||||
|  | ||||
| func getUser(c echo.Context) error { | ||||
| 	id, _ := strconv.Atoi(c.Param("id")) | ||||
| 	return c.JSON(http.StatusOK, users[id]) | ||||
| } | ||||
|  | ||||
| func updateUser(c echo.Context) error { | ||||
| 	u := new(user) | ||||
| 	if err := c.Bind(u); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	id, _ := strconv.Atoi(c.Param("id")) | ||||
| 	users[id].Name = u.Name | ||||
| 	return c.JSON(http.StatusOK, users[id]) | ||||
| } | ||||
|  | ||||
| func deleteUser(c echo.Context) error { | ||||
| 	id, _ := strconv.Atoi(c.Param("id")) | ||||
| 	delete(users, id) | ||||
| 	return c.NoContent(http.StatusNoContent) | ||||
| } | ||||
|  | ||||
| func main() { | ||||
| 	e := echo.New() | ||||
|  | ||||
| 	// Middleware | ||||
| 	e.Use(middleware.Logger()) | ||||
| 	e.Use(middleware.Recover()) | ||||
|  | ||||
| 	// Routes | ||||
| 	e.POST("/users", createUser) | ||||
| 	e.GET("/users/:id", getUser) | ||||
| 	e.PUT("/users/:id", updateUser) | ||||
| 	e.DELETE("/users/:id", deleteUser) | ||||
|  | ||||
| 	// Start server | ||||
| 	e.Logger.Fatal(e.Start(":1323")) | ||||
| } | ||||
							
								
								
									
										21
									
								
								vendor/github.com/labstack/echo/cookbook/embed-resources/server.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										21
									
								
								vendor/github.com/labstack/echo/cookbook/embed-resources/server.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,21 +0,0 @@ | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"net/http" | ||||
|  | ||||
| 	rice "github.com/GeertJohan/go.rice" | ||||
| 	"github.com/labstack/echo" | ||||
| ) | ||||
|  | ||||
| func main() { | ||||
| 	e := echo.New() | ||||
| 	// the file server for rice. "app" is the folder where the files come from. | ||||
| 	assetHandler := http.FileServer(rice.MustFindBox("app").HTTPBox()) | ||||
| 	// serves the index.html from rice | ||||
| 	e.GET("/", echo.WrapHandler(assetHandler)) | ||||
|  | ||||
| 	// servers other static files | ||||
| 	e.GET("/static/*", echo.WrapHandler(http.StripPrefix("/static/", assetHandler))) | ||||
|  | ||||
| 	e.Logger.Fatal(e.Start(":1323")) | ||||
| } | ||||
							
								
								
									
										65
									
								
								vendor/github.com/labstack/echo/cookbook/file-upload/multiple/server.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										65
									
								
								vendor/github.com/labstack/echo/cookbook/file-upload/multiple/server.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,65 +0,0 @@ | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"os" | ||||
|  | ||||
| 	"net/http" | ||||
|  | ||||
| 	"github.com/labstack/echo" | ||||
| 	"github.com/labstack/echo/middleware" | ||||
| ) | ||||
|  | ||||
| func upload(c echo.Context) error { | ||||
| 	// Read form fields | ||||
| 	name := c.FormValue("name") | ||||
| 	email := c.FormValue("email") | ||||
|  | ||||
| 	//------------ | ||||
| 	// Read files | ||||
| 	//------------ | ||||
|  | ||||
| 	// Multipart form | ||||
| 	form, err := c.MultipartForm() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	files := form.File["files"] | ||||
|  | ||||
| 	for _, file := range files { | ||||
| 		// Source | ||||
| 		src, err := file.Open() | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		defer src.Close() | ||||
|  | ||||
| 		// Destination | ||||
| 		dst, err := os.Create(file.Filename) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		defer dst.Close() | ||||
|  | ||||
| 		// Copy | ||||
| 		if _, err = io.Copy(dst, src); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	return c.HTML(http.StatusOK, fmt.Sprintf("<p>Uploaded successfully %d files with fields name=%s and email=%s.</p>", len(files), name, email)) | ||||
| } | ||||
|  | ||||
| func main() { | ||||
| 	e := echo.New() | ||||
|  | ||||
| 	e.Use(middleware.Logger()) | ||||
| 	e.Use(middleware.Recover()) | ||||
|  | ||||
| 	e.Static("/", "public") | ||||
| 	e.POST("/upload", upload) | ||||
|  | ||||
| 	e.Logger.Fatal(e.Start(":1323")) | ||||
| } | ||||
							
								
								
									
										59
									
								
								vendor/github.com/labstack/echo/cookbook/file-upload/single/server.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										59
									
								
								vendor/github.com/labstack/echo/cookbook/file-upload/single/server.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,59 +0,0 @@ | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"os" | ||||
|  | ||||
| 	"net/http" | ||||
|  | ||||
| 	"github.com/labstack/echo" | ||||
| 	"github.com/labstack/echo/middleware" | ||||
| ) | ||||
|  | ||||
| func upload(c echo.Context) error { | ||||
| 	// Read form fields | ||||
| 	name := c.FormValue("name") | ||||
| 	email := c.FormValue("email") | ||||
|  | ||||
| 	//----------- | ||||
| 	// Read file | ||||
| 	//----------- | ||||
|  | ||||
| 	// Source | ||||
| 	file, err := c.FormFile("file") | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	src, err := file.Open() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	defer src.Close() | ||||
|  | ||||
| 	// Destination | ||||
| 	dst, err := os.Create(file.Filename) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	defer dst.Close() | ||||
|  | ||||
| 	// Copy | ||||
| 	if _, err = io.Copy(dst, src); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	return c.HTML(http.StatusOK, fmt.Sprintf("<p>File %s uploaded successfully with fields name=%s and email=%s.</p>", file.Filename, name, email)) | ||||
| } | ||||
|  | ||||
| func main() { | ||||
| 	e := echo.New() | ||||
|  | ||||
| 	e.Use(middleware.Logger()) | ||||
| 	e.Use(middleware.Recover()) | ||||
|  | ||||
| 	e.Static("/", "public") | ||||
| 	e.POST("/upload", upload) | ||||
|  | ||||
| 	e.Logger.Fatal(e.Start(":1323")) | ||||
| } | ||||
							
								
								
									
										17
									
								
								vendor/github.com/labstack/echo/cookbook/google-app-engine/app-engine.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										17
									
								
								vendor/github.com/labstack/echo/cookbook/google-app-engine/app-engine.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,17 +0,0 @@ | ||||
| // +build appengine | ||||
|  | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"net/http" | ||||
|  | ||||
| 	"github.com/labstack/echo" | ||||
| ) | ||||
|  | ||||
| func createMux() *echo.Echo { | ||||
| 	e := echo.New() | ||||
| 	// note: we don't need to provide the middleware or static handlers, that's taken care of by the platform | ||||
| 	// app engine has it's own "main" wrapper - we just need to hook echo into the default handler | ||||
| 	http.Handle("/", e) | ||||
| 	return e | ||||
| } | ||||
							
								
								
									
										25
									
								
								vendor/github.com/labstack/echo/cookbook/google-app-engine/app-managed.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										25
									
								
								vendor/github.com/labstack/echo/cookbook/google-app-engine/app-managed.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,25 +0,0 @@ | ||||
| // +build appenginevm | ||||
|  | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"net/http" | ||||
|  | ||||
| 	"github.com/labstack/echo" | ||||
| 	"google.golang.org/appengine" | ||||
| ) | ||||
|  | ||||
| func createMux() *echo.Echo { | ||||
| 	e := echo.New() | ||||
| 	// note: we don't need to provide the middleware or static handlers | ||||
| 	// for the appengine vm version - that's taken care of by the platform | ||||
| 	return e | ||||
| } | ||||
|  | ||||
| func main() { | ||||
| 	// the appengine package provides a convenient method to handle the health-check requests | ||||
| 	// and also run the app on the correct port. We just need to add Echo to the default handler | ||||
| 	e := echo.New(":8080") | ||||
| 	http.Handle("/", e) | ||||
| 	appengine.Main() | ||||
| } | ||||
							
								
								
									
										24
									
								
								vendor/github.com/labstack/echo/cookbook/google-app-engine/app-standalone.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										24
									
								
								vendor/github.com/labstack/echo/cookbook/google-app-engine/app-standalone.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,24 +0,0 @@ | ||||
| // +build !appengine,!appenginevm | ||||
|  | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"github.com/labstack/echo" | ||||
| 	"github.com/labstack/echo/middleware" | ||||
| ) | ||||
|  | ||||
| func createMux() *echo.Echo { | ||||
| 	e := echo.New() | ||||
|  | ||||
| 	e.Use(middleware.Recover()) | ||||
| 	e.Use(middleware.Logger()) | ||||
| 	e.Use(middleware.Gzip()) | ||||
|  | ||||
| 	e.Static("/", "public") | ||||
|  | ||||
| 	return e | ||||
| } | ||||
|  | ||||
| func main() { | ||||
| 	e.Logger.Fatal(e.Start(":8080")) | ||||
| } | ||||
							
								
								
									
										4
									
								
								vendor/github.com/labstack/echo/cookbook/google-app-engine/app.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								vendor/github.com/labstack/echo/cookbook/google-app-engine/app.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,4 +0,0 @@ | ||||
| package main | ||||
|  | ||||
| // reference our echo instance and create it early | ||||
| var e = createMux() | ||||
							
								
								
									
										54
									
								
								vendor/github.com/labstack/echo/cookbook/google-app-engine/users.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										54
									
								
								vendor/github.com/labstack/echo/cookbook/google-app-engine/users.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,54 +0,0 @@ | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"net/http" | ||||
|  | ||||
| 	"github.com/labstack/echo" | ||||
| 	"github.com/labstack/echo/middleware" | ||||
| ) | ||||
|  | ||||
| type ( | ||||
| 	user struct { | ||||
| 		ID   string `json:"id"` | ||||
| 		Name string `json:"name"` | ||||
| 	} | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	users map[string]user | ||||
| ) | ||||
|  | ||||
| func init() { | ||||
| 	users = map[string]user{ | ||||
| 		"1": user{ | ||||
| 			ID:   "1", | ||||
| 			Name: "Wreck-It Ralph", | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	// hook into the echo instance to create an endpoint group | ||||
| 	// and add specific middleware to it plus handlers | ||||
| 	g := e.Group("/users") | ||||
| 	g.Use(middleware.CORS()) | ||||
|  | ||||
| 	g.POST("", createUser) | ||||
| 	g.GET("", getUsers) | ||||
| 	g.GET("/:id", getUser) | ||||
| } | ||||
|  | ||||
| func createUser(c echo.Context) error { | ||||
| 	u := new(user) | ||||
| 	if err := c.Bind(u); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	users[u.ID] = *u | ||||
| 	return c.JSON(http.StatusCreated, u) | ||||
| } | ||||
|  | ||||
| func getUsers(c echo.Context) error { | ||||
| 	return c.JSON(http.StatusOK, users) | ||||
| } | ||||
|  | ||||
| func getUser(c echo.Context) error { | ||||
| 	return c.JSON(http.StatusOK, users[c.Param("id")]) | ||||
| } | ||||
							
								
								
									
										31
									
								
								vendor/github.com/labstack/echo/cookbook/google-app-engine/welcome.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										31
									
								
								vendor/github.com/labstack/echo/cookbook/google-app-engine/welcome.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,31 +0,0 @@ | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"html/template" | ||||
| 	"io" | ||||
| 	"net/http" | ||||
|  | ||||
| 	"github.com/labstack/echo" | ||||
| ) | ||||
|  | ||||
| type ( | ||||
| 	Template struct { | ||||
| 		templates *template.Template | ||||
| 	} | ||||
| ) | ||||
|  | ||||
| func init() { | ||||
| 	t := &Template{ | ||||
| 		templates: template.Must(template.ParseFiles("templates/welcome.html")), | ||||
| 	} | ||||
| 	e.Renderer = t | ||||
| 	e.GET("/welcome", welcome) | ||||
| } | ||||
|  | ||||
| func (t *Template) Render(w io.Writer, name string, data interface{}, c echo.Context) error { | ||||
| 	return t.templates.ExecuteTemplate(w, name, data) | ||||
| } | ||||
|  | ||||
| func welcome(c echo.Context) error { | ||||
| 	return c.Render(http.StatusOK, "welcome", "Joe") | ||||
| } | ||||
							
								
								
									
										20
									
								
								vendor/github.com/labstack/echo/cookbook/graceful-shutdown/grace/server.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										20
									
								
								vendor/github.com/labstack/echo/cookbook/graceful-shutdown/grace/server.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,20 +0,0 @@ | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"net/http" | ||||
|  | ||||
| 	"github.com/facebookgo/grace/gracehttp" | ||||
| 	"github.com/labstack/echo" | ||||
| ) | ||||
|  | ||||
| func main() { | ||||
| 	// Setup | ||||
| 	e := echo.New() | ||||
| 	e.GET("/", func(c echo.Context) error { | ||||
| 		return c.String(http.StatusOK, "Six sick bricks tick") | ||||
| 	}) | ||||
| 	e.Server.Addr = ":1323" | ||||
|  | ||||
| 	// Serve it like a boss | ||||
| 	e.Logger.Fatal(gracehttp.Serve(e.Server)) | ||||
| } | ||||
							
								
								
									
										21
									
								
								vendor/github.com/labstack/echo/cookbook/graceful-shutdown/graceful/server.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										21
									
								
								vendor/github.com/labstack/echo/cookbook/graceful-shutdown/graceful/server.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,21 +0,0 @@ | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"net/http" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/labstack/echo" | ||||
| 	"github.com/tylerb/graceful" | ||||
| ) | ||||
|  | ||||
| func main() { | ||||
| 	// Setup | ||||
| 	e := echo.New() | ||||
| 	e.GET("/", func(c echo.Context) error { | ||||
| 		return c.String(http.StatusOK, "Sue sews rose on slow joe crows nose") | ||||
| 	}) | ||||
| 	e.Server.Addr = ":1323" | ||||
|  | ||||
| 	// Serve it like a boss | ||||
| 	graceful.ListenAndServe(e.Server, 5*time.Second) | ||||
| } | ||||
							
								
								
									
										25
									
								
								vendor/github.com/labstack/echo/cookbook/hello-world/server.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										25
									
								
								vendor/github.com/labstack/echo/cookbook/hello-world/server.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,25 +0,0 @@ | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"net/http" | ||||
|  | ||||
| 	"github.com/labstack/echo" | ||||
| 	"github.com/labstack/echo/middleware" | ||||
| ) | ||||
|  | ||||
| func main() { | ||||
| 	// Echo instance | ||||
| 	e := echo.New() | ||||
|  | ||||
| 	// Middleware | ||||
| 	e.Use(middleware.Logger()) | ||||
| 	e.Use(middleware.Recover()) | ||||
|  | ||||
| 	// Route => handler | ||||
| 	e.GET("/", func(c echo.Context) error { | ||||
| 		return c.String(http.StatusOK, "Hello, World!\n") | ||||
| 	}) | ||||
|  | ||||
| 	// Start server | ||||
| 	e.Logger.Fatal(e.Start(":1323")) | ||||
| } | ||||
							
								
								
									
										42
									
								
								vendor/github.com/labstack/echo/cookbook/http2/server.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										42
									
								
								vendor/github.com/labstack/echo/cookbook/http2/server.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,42 +0,0 @@ | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"net/http" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/labstack/echo" | ||||
| ) | ||||
|  | ||||
| func request(c echo.Context) error { | ||||
| 	req := c.Request() | ||||
| 	format := "<pre><strong>Request Information</strong>\n\n<code>Protocol: %s\nHost: %s\nRemote Address: %s\nMethod: %s\nPath: %s\n</code></pre>" | ||||
| 	return c.HTML(http.StatusOK, fmt.Sprintf(format, req.Proto, req.Host, req.RemoteAddr, req.Method, req.URL.Path)) | ||||
| } | ||||
|  | ||||
| func stream(c echo.Context) error { | ||||
| 	res := c.Response() | ||||
| 	gone := res.CloseNotify() | ||||
| 	res.Header().Set(echo.HeaderContentType, echo.MIMETextHTMLCharsetUTF8) | ||||
| 	res.WriteHeader(http.StatusOK) | ||||
| 	ticker := time.NewTicker(1 * time.Second) | ||||
| 	defer ticker.Stop() | ||||
|  | ||||
| 	fmt.Fprint(res, "<pre><strong>Clock Stream</strong>\n\n<code>") | ||||
| 	for { | ||||
| 		fmt.Fprintf(res, "%v\n", time.Now()) | ||||
| 		res.Flush() | ||||
| 		select { | ||||
| 		case <-ticker.C: | ||||
| 		case <-gone: | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func main() { | ||||
| 	e := echo.New() | ||||
| 	e.GET("/request", request) | ||||
| 	e.GET("/stream", stream) | ||||
| 	e.Logger.Fatal(e.StartTLS(":1323", "cert.pem", "key.pem")) | ||||
| } | ||||
							
								
								
									
										35
									
								
								vendor/github.com/labstack/echo/cookbook/jsonp/server.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										35
									
								
								vendor/github.com/labstack/echo/cookbook/jsonp/server.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,35 +0,0 @@ | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"math/rand" | ||||
| 	"net/http" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/labstack/echo" | ||||
| 	"github.com/labstack/echo/middleware" | ||||
| ) | ||||
|  | ||||
| func main() { | ||||
| 	e := echo.New() | ||||
| 	e.Use(middleware.Logger()) | ||||
| 	e.Use(middleware.Recover()) | ||||
|  | ||||
| 	e.Static("/", "public") | ||||
|  | ||||
| 	// JSONP | ||||
| 	e.GET("/jsonp", func(c echo.Context) error { | ||||
| 		callback := c.QueryParam("callback") | ||||
| 		var content struct { | ||||
| 			Response  string    `json:"response"` | ||||
| 			Timestamp time.Time `json:"timestamp"` | ||||
| 			Random    int       `json:"random"` | ||||
| 		} | ||||
| 		content.Response = "Sent via JSONP" | ||||
| 		content.Timestamp = time.Now().UTC() | ||||
| 		content.Random = rand.Intn(1000) | ||||
| 		return c.JSONP(http.StatusOK, callback, &content) | ||||
| 	}) | ||||
|  | ||||
| 	// Start server | ||||
| 	e.Logger.Fatal(e.Start(":1323")) | ||||
| } | ||||
							
								
								
									
										86
									
								
								vendor/github.com/labstack/echo/cookbook/jwt/custom-claims/server.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										86
									
								
								vendor/github.com/labstack/echo/cookbook/jwt/custom-claims/server.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,86 +0,0 @@ | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"net/http" | ||||
| 	"time" | ||||
|  | ||||
| 	jwt "github.com/dgrijalva/jwt-go" | ||||
| 	"github.com/labstack/echo" | ||||
| 	"github.com/labstack/echo/middleware" | ||||
| ) | ||||
|  | ||||
| // jwtCustomClaims are custom claims extending default ones. | ||||
| type jwtCustomClaims struct { | ||||
| 	Name  string `json:"name"` | ||||
| 	Admin bool   `json:"admin"` | ||||
| 	jwt.StandardClaims | ||||
| } | ||||
|  | ||||
| func login(c echo.Context) error { | ||||
| 	username := c.FormValue("username") | ||||
| 	password := c.FormValue("password") | ||||
|  | ||||
| 	if username == "jon" && password == "shhh!" { | ||||
|  | ||||
| 		// Set custom claims | ||||
| 		claims := &jwtCustomClaims{ | ||||
| 			"Jon Snow", | ||||
| 			true, | ||||
| 			jwt.StandardClaims{ | ||||
| 				ExpiresAt: time.Now().Add(time.Hour * 72).Unix(), | ||||
| 			}, | ||||
| 		} | ||||
|  | ||||
| 		// Create token with claims | ||||
| 		token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) | ||||
|  | ||||
| 		// Generate encoded token and send it as response. | ||||
| 		t, err := token.SignedString([]byte("secret")) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		return c.JSON(http.StatusOK, echo.Map{ | ||||
| 			"token": t, | ||||
| 		}) | ||||
| 	} | ||||
|  | ||||
| 	return echo.ErrUnauthorized | ||||
| } | ||||
|  | ||||
| func accessible(c echo.Context) error { | ||||
| 	return c.String(http.StatusOK, "Accessible") | ||||
| } | ||||
|  | ||||
| func restricted(c echo.Context) error { | ||||
| 	user := c.Get("user").(*jwt.Token) | ||||
| 	claims := user.Claims.(*jwtCustomClaims) | ||||
| 	name := claims.Name | ||||
| 	return c.String(http.StatusOK, "Welcome "+name+"!") | ||||
| } | ||||
|  | ||||
| func main() { | ||||
| 	e := echo.New() | ||||
|  | ||||
| 	// Middleware | ||||
| 	e.Use(middleware.Logger()) | ||||
| 	e.Use(middleware.Recover()) | ||||
|  | ||||
| 	// Login route | ||||
| 	e.POST("/login", login) | ||||
|  | ||||
| 	// Unauthenticated route | ||||
| 	e.GET("/", accessible) | ||||
|  | ||||
| 	// Restricted group | ||||
| 	r := e.Group("/restricted") | ||||
|  | ||||
| 	// Configure middleware with the custom claims type | ||||
| 	config := middleware.JWTConfig{ | ||||
| 		Claims:     &jwtCustomClaims{}, | ||||
| 		SigningKey: []byte("secret"), | ||||
| 	} | ||||
| 	r.Use(middleware.JWTWithConfig(config)) | ||||
| 	r.GET("", restricted) | ||||
|  | ||||
| 	e.Logger.Fatal(e.Start(":1323")) | ||||
| } | ||||
							
								
								
									
										69
									
								
								vendor/github.com/labstack/echo/cookbook/jwt/map-claims/server.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										69
									
								
								vendor/github.com/labstack/echo/cookbook/jwt/map-claims/server.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,69 +0,0 @@ | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"net/http" | ||||
| 	"time" | ||||
|  | ||||
| 	jwt "github.com/dgrijalva/jwt-go" | ||||
| 	"github.com/labstack/echo" | ||||
| 	"github.com/labstack/echo/middleware" | ||||
| ) | ||||
|  | ||||
| func login(c echo.Context) error { | ||||
| 	username := c.FormValue("username") | ||||
| 	password := c.FormValue("password") | ||||
|  | ||||
| 	if username == "jon" && password == "shhh!" { | ||||
| 		// Create token | ||||
| 		token := jwt.New(jwt.SigningMethodHS256) | ||||
|  | ||||
| 		// Set claims | ||||
| 		claims := token.Claims.(jwt.MapClaims) | ||||
| 		claims["name"] = "Jon Snow" | ||||
| 		claims["admin"] = true | ||||
| 		claims["exp"] = time.Now().Add(time.Hour * 72).Unix() | ||||
|  | ||||
| 		// Generate encoded token and send it as response. | ||||
| 		t, err := token.SignedString([]byte("secret")) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		return c.JSON(http.StatusOK, map[string]string{ | ||||
| 			"token": t, | ||||
| 		}) | ||||
| 	} | ||||
|  | ||||
| 	return echo.ErrUnauthorized | ||||
| } | ||||
|  | ||||
| func accessible(c echo.Context) error { | ||||
| 	return c.String(http.StatusOK, "Accessible") | ||||
| } | ||||
|  | ||||
| func restricted(c echo.Context) error { | ||||
| 	user := c.Get("user").(*jwt.Token) | ||||
| 	claims := user.Claims.(jwt.MapClaims) | ||||
| 	name := claims["name"].(string) | ||||
| 	return c.String(http.StatusOK, "Welcome "+name+"!") | ||||
| } | ||||
|  | ||||
| func main() { | ||||
| 	e := echo.New() | ||||
|  | ||||
| 	// Middleware | ||||
| 	e.Use(middleware.Logger()) | ||||
| 	e.Use(middleware.Recover()) | ||||
|  | ||||
| 	// Login route | ||||
| 	e.POST("/login", login) | ||||
|  | ||||
| 	// Unauthenticated route | ||||
| 	e.GET("/", accessible) | ||||
|  | ||||
| 	// Restricted group | ||||
| 	r := e.Group("/restricted") | ||||
| 	r.Use(middleware.JWT([]byte("secret"))) | ||||
| 	r.GET("", restricted) | ||||
|  | ||||
| 	e.Logger.Fatal(e.Start(":1323")) | ||||
| } | ||||
							
								
								
									
										82
									
								
								vendor/github.com/labstack/echo/cookbook/middleware/server.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										82
									
								
								vendor/github.com/labstack/echo/cookbook/middleware/server.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,82 +0,0 @@ | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"net/http" | ||||
| 	"strconv" | ||||
| 	"sync" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/labstack/echo" | ||||
| ) | ||||
|  | ||||
| type ( | ||||
| 	Stats struct { | ||||
| 		Uptime       time.Time      `json:"uptime"` | ||||
| 		RequestCount uint64         `json:"requestCount"` | ||||
| 		Statuses     map[string]int `json:"statuses"` | ||||
| 		mutex        sync.RWMutex | ||||
| 	} | ||||
| ) | ||||
|  | ||||
| func NewStats() *Stats { | ||||
| 	return &Stats{ | ||||
| 		Uptime:   time.Now(), | ||||
| 		Statuses: make(map[string]int), | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Process is the middleware function. | ||||
| func (s *Stats) Process(next echo.HandlerFunc) echo.HandlerFunc { | ||||
| 	return func(c echo.Context) error { | ||||
| 		if err := next(c); err != nil { | ||||
| 			c.Error(err) | ||||
| 		} | ||||
| 		s.mutex.Lock() | ||||
| 		defer s.mutex.Unlock() | ||||
| 		s.RequestCount++ | ||||
| 		status := strconv.Itoa(c.Response().Status) | ||||
| 		s.Statuses[status]++ | ||||
| 		return nil | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Handle is the endpoint to get stats. | ||||
| func (s *Stats) Handle(c echo.Context) error { | ||||
| 	s.mutex.RLock() | ||||
| 	defer s.mutex.RUnlock() | ||||
| 	return c.JSON(http.StatusOK, s) | ||||
| } | ||||
|  | ||||
| // ServerHeader middleware adds a `Server` header to the response. | ||||
| func ServerHeader(next echo.HandlerFunc) echo.HandlerFunc { | ||||
| 	return func(c echo.Context) error { | ||||
| 		c.Response().Header().Set(echo.HeaderServer, "Echo/3.0") | ||||
| 		return next(c) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func main() { | ||||
| 	e := echo.New() | ||||
|  | ||||
| 	// Debug mode | ||||
| 	e.Debug = true | ||||
|  | ||||
| 	//------------------- | ||||
| 	// Custom middleware | ||||
| 	//------------------- | ||||
| 	// Stats | ||||
| 	s := NewStats() | ||||
| 	e.Use(s.Process) | ||||
| 	e.GET("/stats", s.Handle) // Endpoint to get stats | ||||
|  | ||||
| 	// Server header | ||||
| 	e.Use(ServerHeader) | ||||
|  | ||||
| 	// Handler | ||||
| 	e.GET("/", func(c echo.Context) error { | ||||
| 		return c.String(http.StatusOK, "Hello, World!") | ||||
| 	}) | ||||
|  | ||||
| 	// Start server | ||||
| 	e.Logger.Fatal(e.Start(":1323")) | ||||
| } | ||||
							
								
								
									
										45
									
								
								vendor/github.com/labstack/echo/cookbook/streaming-response/server.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										45
									
								
								vendor/github.com/labstack/echo/cookbook/streaming-response/server.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,45 +0,0 @@ | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"net/http" | ||||
| 	"time" | ||||
|  | ||||
| 	"encoding/json" | ||||
|  | ||||
| 	"github.com/labstack/echo" | ||||
| ) | ||||
|  | ||||
| type ( | ||||
| 	Geolocation struct { | ||||
| 		Altitude  float64 | ||||
| 		Latitude  float64 | ||||
| 		Longitude float64 | ||||
| 	} | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	locations = []Geolocation{ | ||||
| 		{-97, 37.819929, -122.478255}, | ||||
| 		{1899, 39.096849, -120.032351}, | ||||
| 		{2619, 37.865101, -119.538329}, | ||||
| 		{42, 33.812092, -117.918974}, | ||||
| 		{15, 37.77493, -122.419416}, | ||||
| 	} | ||||
| ) | ||||
|  | ||||
| func main() { | ||||
| 	e := echo.New() | ||||
| 	e.GET("/", func(c echo.Context) error { | ||||
| 		c.Response().Header().Set(echo.HeaderContentType, echo.MIMEApplicationJSON) | ||||
| 		c.Response().WriteHeader(http.StatusOK) | ||||
| 		for _, l := range locations { | ||||
| 			if err := json.NewEncoder(c.Response()).Encode(l); err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			c.Response().Flush() | ||||
| 			time.Sleep(1 * time.Second) | ||||
| 		} | ||||
| 		return nil | ||||
| 	}) | ||||
| 	e.Logger.Fatal(e.Start(":1323")) | ||||
| } | ||||
							
								
								
									
										78
									
								
								vendor/github.com/labstack/echo/cookbook/subdomains/server.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										78
									
								
								vendor/github.com/labstack/echo/cookbook/subdomains/server.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,78 +0,0 @@ | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"net/http" | ||||
|  | ||||
| 	"github.com/labstack/echo" | ||||
| 	"github.com/labstack/echo/middleware" | ||||
| ) | ||||
|  | ||||
| type ( | ||||
| 	Host struct { | ||||
| 		Echo *echo.Echo | ||||
| 	} | ||||
| ) | ||||
|  | ||||
| func main() { | ||||
| 	// Hosts | ||||
| 	hosts := make(map[string]*Host) | ||||
|  | ||||
| 	//----- | ||||
| 	// API | ||||
| 	//----- | ||||
|  | ||||
| 	api := echo.New() | ||||
| 	api.Use(middleware.Logger()) | ||||
| 	api.Use(middleware.Recover()) | ||||
|  | ||||
| 	hosts["api.localhost:1323"] = &Host{api} | ||||
|  | ||||
| 	api.GET("/", func(c echo.Context) error { | ||||
| 		return c.String(http.StatusOK, "API") | ||||
| 	}) | ||||
|  | ||||
| 	//------ | ||||
| 	// Blog | ||||
| 	//------ | ||||
|  | ||||
| 	blog := echo.New() | ||||
| 	blog.Use(middleware.Logger()) | ||||
| 	blog.Use(middleware.Recover()) | ||||
|  | ||||
| 	hosts["blog.localhost:1323"] = &Host{blog} | ||||
|  | ||||
| 	blog.GET("/", func(c echo.Context) error { | ||||
| 		return c.String(http.StatusOK, "Blog") | ||||
| 	}) | ||||
|  | ||||
| 	//--------- | ||||
| 	// Website | ||||
| 	//--------- | ||||
|  | ||||
| 	site := echo.New() | ||||
| 	site.Use(middleware.Logger()) | ||||
| 	site.Use(middleware.Recover()) | ||||
|  | ||||
| 	hosts["localhost:1323"] = &Host{site} | ||||
|  | ||||
| 	site.GET("/", func(c echo.Context) error { | ||||
| 		return c.String(http.StatusOK, "Website") | ||||
| 	}) | ||||
|  | ||||
| 	// Server | ||||
| 	e := echo.New() | ||||
| 	e.Any("/*", func(c echo.Context) (err error) { | ||||
| 		req := c.Request() | ||||
| 		res := c.Response() | ||||
| 		host := hosts[req.Host] | ||||
|  | ||||
| 		if host == nil { | ||||
| 			err = echo.ErrNotFound | ||||
| 		} else { | ||||
| 			host.Echo.ServeHTTP(res, req) | ||||
| 		} | ||||
|  | ||||
| 		return | ||||
| 	}) | ||||
| 	e.Logger.Fatal(e.Start(":1323")) | ||||
| } | ||||
							
								
								
									
										14
									
								
								vendor/github.com/labstack/echo/cookbook/twitter/handler/handler.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										14
									
								
								vendor/github.com/labstack/echo/cookbook/twitter/handler/handler.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,14 +0,0 @@ | ||||
| package handler | ||||
|  | ||||
| import mgo "gopkg.in/mgo.v2" | ||||
|  | ||||
| type ( | ||||
| 	Handler struct { | ||||
| 		DB *mgo.Session | ||||
| 	} | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	// Key (Should come from somewhere else). | ||||
| 	Key = "secret" | ||||
| ) | ||||
							
								
								
									
										73
									
								
								vendor/github.com/labstack/echo/cookbook/twitter/handler/post.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										73
									
								
								vendor/github.com/labstack/echo/cookbook/twitter/handler/post.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,73 +0,0 @@ | ||||
| package handler | ||||
|  | ||||
| import ( | ||||
| 	"net/http" | ||||
| 	"strconv" | ||||
|  | ||||
| 	"github.com/labstack/echo" | ||||
| 	"github.com/labstack/echo/cookbook/twitter/model" | ||||
| 	mgo "gopkg.in/mgo.v2" | ||||
| 	"gopkg.in/mgo.v2/bson" | ||||
| ) | ||||
|  | ||||
| func (h *Handler) CreatePost(c echo.Context) (err error) { | ||||
| 	u := &model.User{ | ||||
| 		ID: bson.ObjectIdHex(userIDFromToken(c)), | ||||
| 	} | ||||
| 	p := &model.Post{ | ||||
| 		ID:   bson.NewObjectId(), | ||||
| 		From: u.ID.Hex(), | ||||
| 	} | ||||
| 	if err = c.Bind(p); err != nil { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	// Validation | ||||
| 	if p.To == "" || p.Message == "" { | ||||
| 		return &echo.HTTPError{Code: http.StatusBadRequest, Message: "invalid to or message fields"} | ||||
| 	} | ||||
|  | ||||
| 	// Find user from database | ||||
| 	db := h.DB.Clone() | ||||
| 	defer db.Close() | ||||
| 	if err = db.DB("twitter").C("users").FindId(u.ID).One(u); err != nil { | ||||
| 		if err == mgo.ErrNotFound { | ||||
| 			return echo.ErrNotFound | ||||
| 		} | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	// Save post in database | ||||
| 	if err = db.DB("twitter").C("posts").Insert(p); err != nil { | ||||
| 		return | ||||
| 	} | ||||
| 	return c.JSON(http.StatusCreated, p) | ||||
| } | ||||
|  | ||||
| func (h *Handler) FetchPost(c echo.Context) (err error) { | ||||
| 	userID := userIDFromToken(c) | ||||
| 	page, _ := strconv.Atoi(c.QueryParam("page")) | ||||
| 	limit, _ := strconv.Atoi(c.QueryParam("limit")) | ||||
|  | ||||
| 	// Defaults | ||||
| 	if page == 0 { | ||||
| 		page = 1 | ||||
| 	} | ||||
| 	if limit == 0 { | ||||
| 		limit = 100 | ||||
| 	} | ||||
|  | ||||
| 	// Retrieve posts from database | ||||
| 	posts := []*model.Post{} | ||||
| 	db := h.DB.Clone() | ||||
| 	if err = db.DB("twitter").C("posts"). | ||||
| 		Find(bson.M{"to": userID}). | ||||
| 		Skip((page - 1) * limit). | ||||
| 		Limit(limit). | ||||
| 		All(&posts); err != nil { | ||||
| 		return | ||||
| 	} | ||||
| 	defer db.Close() | ||||
|  | ||||
| 	return c.JSON(http.StatusOK, posts) | ||||
| } | ||||
							
								
								
									
										97
									
								
								vendor/github.com/labstack/echo/cookbook/twitter/handler/user.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										97
									
								
								vendor/github.com/labstack/echo/cookbook/twitter/handler/user.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,97 +0,0 @@ | ||||
| package handler | ||||
|  | ||||
| import ( | ||||
| 	"net/http" | ||||
| 	"time" | ||||
|  | ||||
| 	jwt "github.com/dgrijalva/jwt-go" | ||||
| 	"github.com/labstack/echo" | ||||
| 	"github.com/labstack/echo/cookbook/twitter/model" | ||||
| 	mgo "gopkg.in/mgo.v2" | ||||
| 	"gopkg.in/mgo.v2/bson" | ||||
| ) | ||||
|  | ||||
| func (h *Handler) Signup(c echo.Context) (err error) { | ||||
| 	// Bind | ||||
| 	u := &model.User{ID: bson.NewObjectId()} | ||||
| 	if err = c.Bind(u); err != nil { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	// Validate | ||||
| 	if u.Email == "" || u.Password == "" { | ||||
| 		return &echo.HTTPError{Code: http.StatusBadRequest, Message: "invalid email or password"} | ||||
| 	} | ||||
|  | ||||
| 	// Save user | ||||
| 	db := h.DB.Clone() | ||||
| 	defer db.Close() | ||||
| 	if err = db.DB("twitter").C("users").Insert(u); err != nil { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	return c.JSON(http.StatusCreated, u) | ||||
| } | ||||
|  | ||||
| func (h *Handler) Login(c echo.Context) (err error) { | ||||
| 	// Bind | ||||
| 	u := new(model.User) | ||||
| 	if err = c.Bind(u); err != nil { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	// Find user | ||||
| 	db := h.DB.Clone() | ||||
| 	defer db.Close() | ||||
| 	if err = db.DB("twitter").C("users"). | ||||
| 		Find(bson.M{"email": u.Email, "password": u.Password}).One(u); err != nil { | ||||
| 		if err == mgo.ErrNotFound { | ||||
| 			return &echo.HTTPError{Code: http.StatusUnauthorized, Message: "invalid email or password"} | ||||
| 		} | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	//----- | ||||
| 	// JWT | ||||
| 	//----- | ||||
|  | ||||
| 	// Create token | ||||
| 	token := jwt.New(jwt.SigningMethodHS256) | ||||
|  | ||||
| 	// Set claims | ||||
| 	claims := token.Claims.(jwt.MapClaims) | ||||
| 	claims["id"] = u.ID | ||||
| 	claims["exp"] = time.Now().Add(time.Hour * 72).Unix() | ||||
|  | ||||
| 	// Generate encoded token and send it as response | ||||
| 	u.Token, err = token.SignedString([]byte(Key)) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	u.Password = "" // Don't send password | ||||
| 	return c.JSON(http.StatusOK, u) | ||||
| } | ||||
|  | ||||
| func (h *Handler) Follow(c echo.Context) (err error) { | ||||
| 	userID := userIDFromToken(c) | ||||
| 	id := c.Param("id") | ||||
|  | ||||
| 	// Add a follower to user | ||||
| 	db := h.DB.Clone() | ||||
| 	defer db.Close() | ||||
| 	if err = db.DB("twitter").C("users"). | ||||
| 		UpdateId(bson.ObjectIdHex(id), bson.M{"$addToSet": bson.M{"followers": userID}}); err != nil { | ||||
| 		if err == mgo.ErrNotFound { | ||||
| 			return echo.ErrNotFound | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return | ||||
| } | ||||
|  | ||||
| func userIDFromToken(c echo.Context) string { | ||||
| 	user := c.Get("user").(*jwt.Token) | ||||
| 	claims := user.Claims.(jwt.MapClaims) | ||||
| 	return claims["id"].(string) | ||||
| } | ||||
							
								
								
									
										12
									
								
								vendor/github.com/labstack/echo/cookbook/twitter/model/post.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								vendor/github.com/labstack/echo/cookbook/twitter/model/post.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,12 +0,0 @@ | ||||
| package model | ||||
|  | ||||
| import "gopkg.in/mgo.v2/bson" | ||||
|  | ||||
| type ( | ||||
| 	Post struct { | ||||
| 		ID      bson.ObjectId `json:"id" bson:"_id,omitempty"` | ||||
| 		To      string        `json:"to" bson:"to"` | ||||
| 		From    string        `json:"from" bson:"from"` | ||||
| 		Message string        `json:"message" bson:"message"` | ||||
| 	} | ||||
| ) | ||||
							
								
								
									
										13
									
								
								vendor/github.com/labstack/echo/cookbook/twitter/model/user.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										13
									
								
								vendor/github.com/labstack/echo/cookbook/twitter/model/user.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,13 +0,0 @@ | ||||
| package model | ||||
|  | ||||
| import "gopkg.in/mgo.v2/bson" | ||||
|  | ||||
| type ( | ||||
| 	User struct { | ||||
| 		ID        bson.ObjectId `json:"id" bson:"_id,omitempty"` | ||||
| 		Email     string        `json:"email" bson:"email"` | ||||
| 		Password  string        `json:"password,omitempty" bson:"password"` | ||||
| 		Token     string        `json:"token,omitempty" bson:"-"` | ||||
| 		Followers []string      `json:"followers,omitempty" bson:"followers,omitempty"` | ||||
| 	} | ||||
| ) | ||||
							
								
								
									
										52
									
								
								vendor/github.com/labstack/echo/cookbook/twitter/server.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										52
									
								
								vendor/github.com/labstack/echo/cookbook/twitter/server.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,52 +0,0 @@ | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"github.com/labstack/echo" | ||||
| 	"github.com/labstack/echo/cookbook/twitter/handler" | ||||
| 	"github.com/labstack/echo/middleware" | ||||
| 	"github.com/labstack/gommon/log" | ||||
| 	mgo "gopkg.in/mgo.v2" | ||||
| ) | ||||
|  | ||||
| func main() { | ||||
| 	e := echo.New() | ||||
| 	e.Logger.SetLevel(log.ERROR) | ||||
| 	e.Use(middleware.Logger()) | ||||
| 	e.Use(middleware.JWTWithConfig(middleware.JWTConfig{ | ||||
| 		SigningKey: []byte(handler.Key), | ||||
| 		Skipper: func(c echo.Context) bool { | ||||
| 			// Skip authentication for and signup login requests | ||||
| 			if c.Path() == "/login" || c.Path() == "/signup" { | ||||
| 				return true | ||||
| 			} | ||||
| 			return false | ||||
| 		}, | ||||
| 	})) | ||||
|  | ||||
| 	// Database connection | ||||
| 	db, err := mgo.Dial("localhost") | ||||
| 	if err != nil { | ||||
| 		e.Logger.Fatal(err) | ||||
| 	} | ||||
|  | ||||
| 	// Create indices | ||||
| 	if err = db.Copy().DB("twitter").C("users").EnsureIndex(mgo.Index{ | ||||
| 		Key:    []string{"email"}, | ||||
| 		Unique: true, | ||||
| 	}); err != nil { | ||||
| 		log.Fatal(err) | ||||
| 	} | ||||
|  | ||||
| 	// Initialize handler | ||||
| 	h := &handler.Handler{DB: db} | ||||
|  | ||||
| 	// Routes | ||||
| 	e.POST("/signup", h.Signup) | ||||
| 	e.POST("/login", h.Login) | ||||
| 	e.POST("/follow/:id", h.Follow) | ||||
| 	e.POST("/posts", h.CreatePost) | ||||
| 	e.GET("/feed", h.FetchPost) | ||||
|  | ||||
| 	// Start server | ||||
| 	e.Logger.Fatal(e.Start(":1323")) | ||||
| } | ||||
							
								
								
									
										47
									
								
								vendor/github.com/labstack/echo/cookbook/websocket/gorilla/server.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										47
									
								
								vendor/github.com/labstack/echo/cookbook/websocket/gorilla/server.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,47 +0,0 @@ | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"log" | ||||
|  | ||||
| 	"github.com/labstack/echo" | ||||
|  | ||||
| 	"github.com/gorilla/websocket" | ||||
| 	"github.com/labstack/echo/middleware" | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	upgrader = websocket.Upgrader{} | ||||
| ) | ||||
|  | ||||
| func hello(c echo.Context) error { | ||||
| 	ws, err := upgrader.Upgrade(c.Response(), c.Request(), nil) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	defer ws.Close() | ||||
|  | ||||
| 	for { | ||||
| 		// Write | ||||
| 		err := ws.WriteMessage(websocket.TextMessage, []byte("Hello, Client!")) | ||||
| 		if err != nil { | ||||
| 			log.Fatal(err) | ||||
| 		} | ||||
|  | ||||
| 		// Read | ||||
| 		_, msg, err := ws.ReadMessage() | ||||
| 		if err != nil { | ||||
| 			log.Fatal(err) | ||||
| 		} | ||||
| 		fmt.Printf("%s\n", msg) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func main() { | ||||
| 	e := echo.New() | ||||
| 	e.Use(middleware.Logger()) | ||||
| 	e.Use(middleware.Recover()) | ||||
| 	e.Static("/", "../public") | ||||
| 	e.GET("/ws", hello) | ||||
| 	e.Logger.Fatal(e.Start(":1323")) | ||||
| } | ||||
							
								
								
									
										41
									
								
								vendor/github.com/labstack/echo/cookbook/websocket/net/server.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										41
									
								
								vendor/github.com/labstack/echo/cookbook/websocket/net/server.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,41 +0,0 @@ | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"log" | ||||
|  | ||||
| 	"github.com/labstack/echo" | ||||
| 	"github.com/labstack/echo/middleware" | ||||
| 	"golang.org/x/net/websocket" | ||||
| ) | ||||
|  | ||||
| func hello(c echo.Context) error { | ||||
| 	websocket.Handler(func(ws *websocket.Conn) { | ||||
| 		defer ws.Close() | ||||
| 		for { | ||||
| 			// Write | ||||
| 			err := websocket.Message.Send(ws, "Hello, Client!") | ||||
| 			if err != nil { | ||||
| 				log.Fatal(err) | ||||
| 			} | ||||
|  | ||||
| 			// Read | ||||
| 			msg := "" | ||||
| 			err = websocket.Message.Receive(ws, &msg) | ||||
| 			if err != nil { | ||||
| 				log.Fatal(err) | ||||
| 			} | ||||
| 			fmt.Printf("%s\n", msg) | ||||
| 		} | ||||
| 	}).ServeHTTP(c.Response(), c.Request()) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func main() { | ||||
| 	e := echo.New() | ||||
| 	e.Use(middleware.Logger()) | ||||
| 	e.Use(middleware.Recover()) | ||||
| 	e.Static("/", "../public") | ||||
| 	e.GET("/ws", hello) | ||||
| 	e.Logger.Fatal(e.Start(":1323")) | ||||
| } | ||||
							
								
								
									
										124
									
								
								vendor/github.com/labstack/echo/echo.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										124
									
								
								vendor/github.com/labstack/echo/echo.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -42,10 +42,11 @@ import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	slog "log" | ||||
| 	stdLog "log" | ||||
| 	"net" | ||||
| 	"net/http" | ||||
| 	"path" | ||||
| 	"path/filepath" | ||||
| 	"reflect" | ||||
| 	"runtime" | ||||
| 	"sync" | ||||
| @@ -59,7 +60,7 @@ import ( | ||||
| type ( | ||||
| 	// Echo is the top-level framework instance. | ||||
| 	Echo struct { | ||||
| 		stdLogger        *slog.Logger | ||||
| 		stdLogger        *stdLog.Logger | ||||
| 		colorer          *color.Color | ||||
| 		premiddleware    []MiddlewareFunc | ||||
| 		middleware       []MiddlewareFunc | ||||
| @@ -73,20 +74,21 @@ type ( | ||||
| 		TLSListener      net.Listener | ||||
| 		DisableHTTP2     bool | ||||
| 		Debug            bool | ||||
| 		HideBanner       bool | ||||
| 		HTTPErrorHandler HTTPErrorHandler | ||||
| 		Binder           Binder | ||||
| 		Validator        Validator | ||||
| 		Renderer         Renderer | ||||
| 		AutoTLSManager   autocert.Manager | ||||
| 		Mutex            sync.RWMutex | ||||
| 		Logger           Logger | ||||
| 		// Mutex            sync.RWMutex | ||||
| 		Logger Logger | ||||
| 	} | ||||
|  | ||||
| 	// Route contains a handler and information for matching against requests. | ||||
| 	Route struct { | ||||
| 		Method  string | ||||
| 		Path    string | ||||
| 		Handler string | ||||
| 		Method  string `json:"method"` | ||||
| 		Path    string `json:"path"` | ||||
| 		Handler string `json:"handler"` | ||||
| 	} | ||||
|  | ||||
| 	// HTTPError represents an error that occurred while handling a request. | ||||
| @@ -144,6 +146,8 @@ const ( | ||||
| 	MIMEApplicationJavaScriptCharsetUTF8 = MIMEApplicationJavaScript + "; " + charsetUTF8 | ||||
| 	MIMEApplicationXML                   = "application/xml" | ||||
| 	MIMEApplicationXMLCharsetUTF8        = MIMEApplicationXML + "; " + charsetUTF8 | ||||
| 	MIMETextXML                          = "text/xml" | ||||
| 	MIMETextXMLCharsetUTF8               = MIMETextXML + "; " + charsetUTF8 | ||||
| 	MIMEApplicationForm                  = "application/x-www-form-urlencoded" | ||||
| 	MIMEApplicationProtobuf              = "application/protobuf" | ||||
| 	MIMEApplicationMsgpack               = "application/msgpack" | ||||
| @@ -161,27 +165,34 @@ const ( | ||||
|  | ||||
| // Headers | ||||
| const ( | ||||
| 	HeaderAcceptEncoding                = "Accept-Encoding" | ||||
| 	HeaderAllow                         = "Allow" | ||||
| 	HeaderAuthorization                 = "Authorization" | ||||
| 	HeaderContentDisposition            = "Content-Disposition" | ||||
| 	HeaderContentEncoding               = "Content-Encoding" | ||||
| 	HeaderContentLength                 = "Content-Length" | ||||
| 	HeaderContentType                   = "Content-Type" | ||||
| 	HeaderCookie                        = "Cookie" | ||||
| 	HeaderSetCookie                     = "Set-Cookie" | ||||
| 	HeaderIfModifiedSince               = "If-Modified-Since" | ||||
| 	HeaderLastModified                  = "Last-Modified" | ||||
| 	HeaderLocation                      = "Location" | ||||
| 	HeaderUpgrade                       = "Upgrade" | ||||
| 	HeaderVary                          = "Vary" | ||||
| 	HeaderWWWAuthenticate               = "WWW-Authenticate" | ||||
| 	HeaderXForwardedProto               = "X-Forwarded-Proto" | ||||
| 	HeaderXHTTPMethodOverride           = "X-HTTP-Method-Override" | ||||
| 	HeaderXForwardedFor                 = "X-Forwarded-For" | ||||
| 	HeaderXRealIP                       = "X-Real-IP" | ||||
| 	HeaderServer                        = "Server" | ||||
| 	HeaderOrigin                        = "Origin" | ||||
| 	HeaderAccept              = "Accept" | ||||
| 	HeaderAcceptEncoding      = "Accept-Encoding" | ||||
| 	HeaderAllow               = "Allow" | ||||
| 	HeaderAuthorization       = "Authorization" | ||||
| 	HeaderContentDisposition  = "Content-Disposition" | ||||
| 	HeaderContentEncoding     = "Content-Encoding" | ||||
| 	HeaderContentLength       = "Content-Length" | ||||
| 	HeaderContentType         = "Content-Type" | ||||
| 	HeaderCookie              = "Cookie" | ||||
| 	HeaderSetCookie           = "Set-Cookie" | ||||
| 	HeaderIfModifiedSince     = "If-Modified-Since" | ||||
| 	HeaderLastModified        = "Last-Modified" | ||||
| 	HeaderLocation            = "Location" | ||||
| 	HeaderUpgrade             = "Upgrade" | ||||
| 	HeaderVary                = "Vary" | ||||
| 	HeaderWWWAuthenticate     = "WWW-Authenticate" | ||||
| 	HeaderXForwardedFor       = "X-Forwarded-For" | ||||
| 	HeaderXForwardedProto     = "X-Forwarded-Proto" | ||||
| 	HeaderXForwardedProtocol  = "X-Forwarded-Protocol" | ||||
| 	HeaderXForwardedSsl       = "X-Forwarded-Ssl" | ||||
| 	HeaderXUrlScheme          = "X-Url-Scheme" | ||||
| 	HeaderXHTTPMethodOverride = "X-HTTP-Method-Override" | ||||
| 	HeaderXRealIP             = "X-Real-IP" | ||||
| 	HeaderXRequestID          = "X-Request-ID" | ||||
| 	HeaderServer              = "Server" | ||||
| 	HeaderOrigin              = "Origin" | ||||
|  | ||||
| 	// Access control | ||||
| 	HeaderAccessControlRequestMethod    = "Access-Control-Request-Method" | ||||
| 	HeaderAccessControlRequestHeaders   = "Access-Control-Request-Headers" | ||||
| 	HeaderAccessControlAllowOrigin      = "Access-Control-Allow-Origin" | ||||
| @@ -200,6 +211,22 @@ const ( | ||||
| 	HeaderXCSRFToken              = "X-CSRF-Token" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	version = "3.1.0" | ||||
| 	website = "https://echo.labstack.com" | ||||
| 	// http://patorjk.com/software/taag/#p=display&f=Small%20Slant&t=Echo | ||||
| 	banner = ` | ||||
|    ____    __ | ||||
|   / __/___/ /  ___ | ||||
|  / _// __/ _ \/ _ \ | ||||
| /___/\__/_//_/\___/ %s | ||||
| High performance, minimalist Go web framework | ||||
| %s | ||||
| ____________________________________O/_______ | ||||
|                                     O\ | ||||
| ` | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	methods = [...]string{ | ||||
| 		CONNECT, | ||||
| @@ -219,6 +246,7 @@ var ( | ||||
| 	ErrUnsupportedMediaType        = NewHTTPError(http.StatusUnsupportedMediaType) | ||||
| 	ErrNotFound                    = NewHTTPError(http.StatusNotFound) | ||||
| 	ErrUnauthorized                = NewHTTPError(http.StatusUnauthorized) | ||||
| 	ErrForbidden                   = NewHTTPError(http.StatusForbidden) | ||||
| 	ErrMethodNotAllowed            = NewHTTPError(http.StatusMethodNotAllowed) | ||||
| 	ErrStatusRequestEntityTooLarge = NewHTTPError(http.StatusRequestEntityTooLarge) | ||||
| 	ErrValidatorNotRegistered      = errors.New("Validator not registered") | ||||
| @@ -255,7 +283,7 @@ func New() (e *Echo) { | ||||
| 	e.HTTPErrorHandler = e.DefaultHTTPErrorHandler | ||||
| 	e.Binder = &DefaultBinder{} | ||||
| 	e.Logger.SetLevel(log.OFF) | ||||
| 	e.stdLogger = slog.New(e.Logger.Output(), e.Logger.Prefix()+": ", 0) | ||||
| 	e.stdLogger = stdLog.New(e.Logger.Output(), e.Logger.Prefix()+": ", 0) | ||||
| 	e.pool.New = func() interface{} { | ||||
| 		return e.NewContext(nil, nil) | ||||
| 	} | ||||
| @@ -398,12 +426,16 @@ func (e *Echo) Match(methods []string, path string, handler HandlerFunc, middlew | ||||
| // Static registers a new route with path prefix to serve static files from the | ||||
| // provided root directory. | ||||
| func (e *Echo) Static(prefix, root string) { | ||||
| 	if root == "" { | ||||
| 		root = "." // For security we want to restrict to CWD. | ||||
| 	} | ||||
| 	static(e, prefix, root) | ||||
| } | ||||
|  | ||||
| func static(i i, prefix, root string) { | ||||
| 	h := func(c Context) error { | ||||
| 		return c.File(path.Join(root, c.Param("*"))) | ||||
| 		name := filepath.Join(root, path.Clean("/"+c.Param("*"))) // "/"+ for security | ||||
| 		return c.File(name) | ||||
| 	} | ||||
| 	i.GET(prefix, h) | ||||
| 	if prefix == "/" { | ||||
| @@ -430,7 +462,7 @@ func (e *Echo) add(method, path string, handler HandlerFunc, middleware ...Middl | ||||
| 		} | ||||
| 		return h(c) | ||||
| 	}) | ||||
| 	r := Route{ | ||||
| 	r := &Route{ | ||||
| 		Method:  method, | ||||
| 		Path:    path, | ||||
| 		Handler: name, | ||||
| @@ -476,8 +508,8 @@ func (e *Echo) URL(h HandlerFunc, params ...interface{}) string { | ||||
| } | ||||
|  | ||||
| // Routes returns the registered routes. | ||||
| func (e *Echo) Routes() []Route { | ||||
| 	routes := []Route{} | ||||
| func (e *Echo) Routes() []*Route { | ||||
| 	routes := []*Route{} | ||||
| 	for _, v := range e.router.routes { | ||||
| 		routes = append(routes, v) | ||||
| 	} | ||||
| @@ -499,8 +531,8 @@ func (e *Echo) ReleaseContext(c Context) { | ||||
| // ServeHTTP implements `http.Handler` interface, which serves HTTP requests. | ||||
| func (e *Echo) ServeHTTP(w http.ResponseWriter, r *http.Request) { | ||||
| 	// Acquire lock | ||||
| 	e.Mutex.RLock() | ||||
| 	defer e.Mutex.RUnlock() | ||||
| 	// e.Mutex.RLock() | ||||
| 	// defer e.Mutex.RUnlock() | ||||
|  | ||||
| 	// Acquire context | ||||
| 	c := e.pool.Get().(*context) | ||||
| @@ -510,7 +542,10 @@ func (e *Echo) ServeHTTP(w http.ResponseWriter, r *http.Request) { | ||||
| 	// Middleware | ||||
| 	h := func(c Context) error { | ||||
| 		method := r.Method | ||||
| 		path := r.URL.EscapedPath() | ||||
| 		path := r.URL.RawPath | ||||
| 		if path == "" { | ||||
| 			path = r.URL.Path | ||||
| 		} | ||||
| 		e.router.Find(method, path, c) | ||||
| 		h := c.Handler() | ||||
| 		for i := len(e.middleware) - 1; i >= 0; i-- { | ||||
| @@ -572,8 +607,15 @@ func (e *Echo) startTLS(address string) error { | ||||
| func (e *Echo) StartServer(s *http.Server) (err error) { | ||||
| 	// Setup | ||||
| 	e.colorer.SetOutput(e.Logger.Output()) | ||||
| 	s.Handler = e | ||||
| 	s.ErrorLog = e.stdLogger | ||||
| 	s.Handler = e | ||||
| 	if e.Debug { | ||||
| 		e.Logger.SetLevel(log.DEBUG) | ||||
| 	} | ||||
|  | ||||
| 	if !e.HideBanner { | ||||
| 		e.colorer.Printf(banner, e.colorer.Red("v"+version), e.colorer.Blue(website)) | ||||
| 	} | ||||
|  | ||||
| 	if s.TLSConfig == nil { | ||||
| 		if e.Listener == nil { | ||||
| @@ -582,7 +624,9 @@ func (e *Echo) StartServer(s *http.Server) (err error) { | ||||
| 				return err | ||||
| 			} | ||||
| 		} | ||||
| 		e.colorer.Printf("⇛ http server started on %s\n", e.colorer.Green(e.Listener.Addr())) | ||||
| 		if !e.HideBanner { | ||||
| 			e.colorer.Printf("⇨ http server started on %s\n", e.colorer.Green(e.Listener.Addr())) | ||||
| 		} | ||||
| 		return s.Serve(e.Listener) | ||||
| 	} | ||||
| 	if e.TLSListener == nil { | ||||
| @@ -592,7 +636,9 @@ func (e *Echo) StartServer(s *http.Server) (err error) { | ||||
| 		} | ||||
| 		e.TLSListener = tls.NewListener(l, s.TLSConfig) | ||||
| 	} | ||||
| 	e.colorer.Printf("⇛ https server started on %s\n", e.colorer.Green(e.TLSListener.Addr())) | ||||
| 	if !e.HideBanner { | ||||
| 		e.colorer.Printf("⇨ https server started on %s\n", e.colorer.Green(e.TLSListener.Addr())) | ||||
| 	} | ||||
| 	return s.Serve(e.TLSListener) | ||||
| } | ||||
|  | ||||
|   | ||||
							
								
								
									
										25
									
								
								vendor/github.com/labstack/echo/echo_go1.8.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								vendor/github.com/labstack/echo/echo_go1.8.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| // +build go1.8 | ||||
|  | ||||
| package echo | ||||
|  | ||||
| import ( | ||||
| 	stdContext "context" | ||||
| ) | ||||
|  | ||||
| // Close immediately stops the server. | ||||
| // It internally calls `http.Server#Close()`. | ||||
| func (e *Echo) Close() error { | ||||
| 	if err := e.TLSServer.Close(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return e.Server.Close() | ||||
| } | ||||
|  | ||||
| // Shutdown stops server the gracefully. | ||||
| // It internally calls `http.Server#Shutdown()`. | ||||
| func (e *Echo) Shutdown(ctx stdContext.Context) error { | ||||
| 	if err := e.TLSServer.Shutdown(ctx); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return e.Server.Shutdown(ctx) | ||||
| } | ||||
							
								
								
									
										11
									
								
								vendor/github.com/labstack/echo/group.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										11
									
								
								vendor/github.com/labstack/echo/group.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,8 +1,12 @@ | ||||
| package echo | ||||
|  | ||||
| import ( | ||||
| 	"path" | ||||
| ) | ||||
|  | ||||
| type ( | ||||
| 	// Group is a set of sub-routes for a specified route. It can be used for inner | ||||
| 	// routes that share a common middlware or functionality that should be separate | ||||
| 	// routes that share a common middleware or functionality that should be separate | ||||
| 	// from the parent echo instance while still inheriting from it. | ||||
| 	Group struct { | ||||
| 		prefix     string | ||||
| @@ -14,6 +18,11 @@ type ( | ||||
| // Use implements `Echo#Use()` for sub-routes within the Group. | ||||
| func (g *Group) Use(middleware ...MiddlewareFunc) { | ||||
| 	g.middleware = append(g.middleware, middleware...) | ||||
| 	// Allow all requests to reach the group as they might get dropped if router | ||||
| 	// doesn't find a match, making none of the group middleware process. | ||||
| 	g.echo.Any(path.Clean(g.prefix+"/*"), func(c Context) error { | ||||
| 		return ErrNotFound | ||||
| 	}, g.middleware...) | ||||
| } | ||||
|  | ||||
| // CONNECT implements `Echo#CONNECT()` for sub-routes within the Group. | ||||
|   | ||||
							
								
								
									
										28
									
								
								vendor/github.com/labstack/echo/middleware/basic_auth.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										28
									
								
								vendor/github.com/labstack/echo/middleware/basic_auth.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -2,6 +2,7 @@ package middleware | ||||
|  | ||||
| import ( | ||||
| 	"encoding/base64" | ||||
| 	"strconv" | ||||
|  | ||||
| 	"github.com/labstack/echo" | ||||
| ) | ||||
| @@ -15,20 +16,26 @@ type ( | ||||
| 		// Validator is a function to validate BasicAuth credentials. | ||||
| 		// Required. | ||||
| 		Validator BasicAuthValidator | ||||
|  | ||||
| 		// Realm is a string to define realm attribute of BasicAuth. | ||||
| 		// Default value "Restricted". | ||||
| 		Realm string | ||||
| 	} | ||||
|  | ||||
| 	// BasicAuthValidator defines a function to validate BasicAuth credentials. | ||||
| 	BasicAuthValidator func(string, string, echo.Context) bool | ||||
| 	BasicAuthValidator func(string, string, echo.Context) (bool, error) | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	basic = "Basic" | ||||
| 	basic        = "Basic" | ||||
| 	defaultRealm = "Restricted" | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	// DefaultBasicAuthConfig is the default BasicAuth middleware config. | ||||
| 	DefaultBasicAuthConfig = BasicAuthConfig{ | ||||
| 		Skipper: DefaultSkipper, | ||||
| 		Realm:   defaultRealm, | ||||
| 	} | ||||
| ) | ||||
|  | ||||
| @@ -52,6 +59,9 @@ func BasicAuthWithConfig(config BasicAuthConfig) echo.MiddlewareFunc { | ||||
| 	if config.Skipper == nil { | ||||
| 		config.Skipper = DefaultBasicAuthConfig.Skipper | ||||
| 	} | ||||
| 	if config.Realm == "" { | ||||
| 		config.Realm = defaultRealm | ||||
| 	} | ||||
|  | ||||
| 	return func(next echo.HandlerFunc) echo.HandlerFunc { | ||||
| 		return func(c echo.Context) error { | ||||
| @@ -71,15 +81,25 @@ func BasicAuthWithConfig(config BasicAuthConfig) echo.MiddlewareFunc { | ||||
| 				for i := 0; i < len(cred); i++ { | ||||
| 					if cred[i] == ':' { | ||||
| 						// Verify credentials | ||||
| 						if config.Validator(cred[:i], cred[i+1:], c) { | ||||
| 						valid, err := config.Validator(cred[:i], cred[i+1:], c) | ||||
| 						if err != nil { | ||||
| 							return err | ||||
| 						} else if valid { | ||||
| 							return next(c) | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			realm := "" | ||||
| 			if config.Realm == defaultRealm { | ||||
| 				realm = defaultRealm | ||||
| 			} else { | ||||
| 				realm = strconv.Quote(config.Realm) | ||||
| 			} | ||||
|  | ||||
| 			// Need to return `401` for browsers to pop-up login box. | ||||
| 			c.Response().Header().Set(echo.HeaderWWWAuthenticate, basic+" realm=Restricted") | ||||
| 			c.Response().Header().Set(echo.HeaderWWWAuthenticate, basic+" realm="+realm) | ||||
| 			return echo.ErrUnauthorized | ||||
| 		} | ||||
| 	} | ||||
|   | ||||
							
								
								
									
										4
									
								
								vendor/github.com/labstack/echo/middleware/compress.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								vendor/github.com/labstack/echo/middleware/compress.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -108,8 +108,8 @@ func (w *gzipResponseWriter) Write(b []byte) (int, error) { | ||||
| 	return w.Writer.Write(b) | ||||
| } | ||||
|  | ||||
| func (w *gzipResponseWriter) Flush() error { | ||||
| 	return w.Writer.(*gzip.Writer).Flush() | ||||
| func (w *gzipResponseWriter) Flush() { | ||||
| 	w.Writer.(*gzip.Writer).Flush() | ||||
| } | ||||
|  | ||||
| func (w *gzipResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { | ||||
|   | ||||
							
								
								
									
										2
									
								
								vendor/github.com/labstack/echo/middleware/jwt.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/labstack/echo/middleware/jwt.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -91,7 +91,7 @@ func JWTWithConfig(config JWTConfig) echo.MiddlewareFunc { | ||||
| 		config.Skipper = DefaultJWTConfig.Skipper | ||||
| 	} | ||||
| 	if config.SigningKey == nil { | ||||
| 		panic("jwt middleware requires signing key") | ||||
| 		panic("echo: jwt middleware requires signing key") | ||||
| 	} | ||||
| 	if config.SigningMethod == "" { | ||||
| 		config.SigningMethod = DefaultJWTConfig.SigningMethod | ||||
|   | ||||
							
								
								
									
										7
									
								
								vendor/github.com/labstack/echo/middleware/key_auth.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								vendor/github.com/labstack/echo/middleware/key_auth.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -32,7 +32,7 @@ type ( | ||||
| 	} | ||||
|  | ||||
| 	// KeyAuthValidator defines a function to validate KeyAuth credentials. | ||||
| 	KeyAuthValidator func(string, echo.Context) bool | ||||
| 	KeyAuthValidator func(string, echo.Context) (bool, error) | ||||
|  | ||||
| 	keyExtractor func(echo.Context) (string, error) | ||||
| ) | ||||
| @@ -94,7 +94,10 @@ func KeyAuthWithConfig(config KeyAuthConfig) echo.MiddlewareFunc { | ||||
| 			if err != nil { | ||||
| 				return echo.NewHTTPError(http.StatusBadRequest, err.Error()) | ||||
| 			} | ||||
| 			if config.Validator(key, c) { | ||||
| 			valid, err := config.Validator(key, c) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} else if valid { | ||||
| 				return next(c) | ||||
| 			} | ||||
|  | ||||
|   | ||||
							
								
								
									
										15
									
								
								vendor/github.com/labstack/echo/middleware/logger.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										15
									
								
								vendor/github.com/labstack/echo/middleware/logger.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -26,7 +26,7 @@ type ( | ||||
| 		// - time_unix_nano | ||||
| 		// - time_rfc3339 | ||||
| 		// - time_rfc3339_nano | ||||
| 		// - id (Request ID - Not implemented) | ||||
| 		// - id (Request ID) | ||||
| 		// - remote_ip | ||||
| 		// - uri | ||||
| 		// - host | ||||
| @@ -62,7 +62,7 @@ var ( | ||||
| 	// DefaultLoggerConfig is the default Logger middleware config. | ||||
| 	DefaultLoggerConfig = LoggerConfig{ | ||||
| 		Skipper: DefaultSkipper, | ||||
| 		Format: `{"time":"${time_rfc3339_nano}","remote_ip":"${remote_ip}","host":"${host}",` + | ||||
| 		Format: `{"time":"${time_rfc3339_nano}","id":"${id}","remote_ip":"${remote_ip}","host":"${host}",` + | ||||
| 			`"method":"${method}","uri":"${uri}","status":${status}, "latency":${latency},` + | ||||
| 			`"latency_human":"${latency_human}","bytes_in":${bytes_in},` + | ||||
| 			`"bytes_out":${bytes_out}}` + "\n", | ||||
| @@ -126,6 +126,12 @@ func LoggerWithConfig(config LoggerConfig) echo.MiddlewareFunc { | ||||
| 					return buf.WriteString(time.Now().Format(time.RFC3339)) | ||||
| 				case "time_rfc3339_nano": | ||||
| 					return buf.WriteString(time.Now().Format(time.RFC3339Nano)) | ||||
| 				case "id": | ||||
| 					id := req.Header.Get(echo.HeaderXRequestID) | ||||
| 					if id == "" { | ||||
| 						id = res.Header().Get(echo.HeaderXRequestID) | ||||
| 					} | ||||
| 					return buf.WriteString(id) | ||||
| 				case "remote_ip": | ||||
| 					return buf.WriteString(c.RealIP()) | ||||
| 				case "host": | ||||
| @@ -177,6 +183,11 @@ func LoggerWithConfig(config LoggerConfig) echo.MiddlewareFunc { | ||||
| 						return buf.Write([]byte(c.QueryParam(tag[6:]))) | ||||
| 					case strings.HasPrefix(tag, "form:"): | ||||
| 						return buf.Write([]byte(c.FormValue(tag[5:]))) | ||||
| 					case strings.HasPrefix(tag, "cookie:"): | ||||
| 						cookie, err := c.Cookie(tag[7:]) | ||||
| 						if err == nil { | ||||
| 							return buf.Write([]byte(cookie.Value)) | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 				return 0, nil | ||||
|   | ||||
							
								
								
									
										2
									
								
								vendor/github.com/labstack/echo/middleware/middleware.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/labstack/echo/middleware/middleware.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -9,6 +9,6 @@ type ( | ||||
| ) | ||||
|  | ||||
| // DefaultSkipper returns false which processes the middleware. | ||||
| func DefaultSkipper(c echo.Context) bool { | ||||
| func DefaultSkipper(echo.Context) bool { | ||||
| 	return false | ||||
| } | ||||
|   | ||||
							
								
								
									
										160
									
								
								vendor/github.com/labstack/echo/middleware/proxy.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										160
									
								
								vendor/github.com/labstack/echo/middleware/proxy.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,160 @@ | ||||
| package middleware | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"math/rand" | ||||
| 	"net" | ||||
| 	"net/http" | ||||
| 	"net/http/httputil" | ||||
| 	"net/url" | ||||
| 	"sync/atomic" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/labstack/echo" | ||||
| ) | ||||
|  | ||||
| // TODO: Handle TLS proxy | ||||
|  | ||||
| type ( | ||||
| 	// ProxyConfig defines the config for Proxy middleware. | ||||
| 	ProxyConfig struct { | ||||
| 		// Skipper defines a function to skip middleware. | ||||
| 		Skipper Skipper | ||||
|  | ||||
| 		// Balancer defines a load balancing technique. | ||||
| 		// Required. | ||||
| 		// Possible values: | ||||
| 		// - RandomBalancer | ||||
| 		// - RoundRobinBalancer | ||||
| 		Balancer ProxyBalancer | ||||
| 	} | ||||
|  | ||||
| 	// ProxyTarget defines the upstream target. | ||||
| 	ProxyTarget struct { | ||||
| 		URL *url.URL | ||||
| 	} | ||||
|  | ||||
| 	// RandomBalancer implements a random load balancing technique. | ||||
| 	RandomBalancer struct { | ||||
| 		Targets []*ProxyTarget | ||||
| 		random  *rand.Rand | ||||
| 	} | ||||
|  | ||||
| 	// RoundRobinBalancer implements a round-robin load balancing technique. | ||||
| 	RoundRobinBalancer struct { | ||||
| 		Targets []*ProxyTarget | ||||
| 		i       uint32 | ||||
| 	} | ||||
|  | ||||
| 	// ProxyBalancer defines an interface to implement a load balancing technique. | ||||
| 	ProxyBalancer interface { | ||||
| 		Next() *ProxyTarget | ||||
| 	} | ||||
| ) | ||||
|  | ||||
| func proxyHTTP(t *ProxyTarget) http.Handler { | ||||
| 	return httputil.NewSingleHostReverseProxy(t.URL) | ||||
| } | ||||
|  | ||||
| func proxyRaw(t *ProxyTarget, c echo.Context) http.Handler { | ||||
| 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||||
| 		h, ok := w.(http.Hijacker) | ||||
| 		if !ok { | ||||
| 			c.Error(errors.New("proxy raw, not a hijacker")) | ||||
| 			return | ||||
| 		} | ||||
| 		in, _, err := h.Hijack() | ||||
| 		if err != nil { | ||||
| 			c.Error(fmt.Errorf("proxy raw, hijack error=%v, url=%s", r.URL, err)) | ||||
| 			return | ||||
| 		} | ||||
| 		defer in.Close() | ||||
|  | ||||
| 		out, err := net.Dial("tcp", t.URL.Host) | ||||
| 		if err != nil { | ||||
| 			he := echo.NewHTTPError(http.StatusBadGateway, fmt.Sprintf("proxy raw, dial error=%v, url=%s", r.URL, err)) | ||||
| 			c.Error(he) | ||||
| 			return | ||||
| 		} | ||||
| 		defer out.Close() | ||||
|  | ||||
| 		err = r.Write(out) | ||||
| 		if err != nil { | ||||
| 			he := echo.NewHTTPError(http.StatusBadGateway, fmt.Sprintf("proxy raw, request copy error=%v, url=%s", r.URL, err)) | ||||
| 			c.Error(he) | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		errc := make(chan error, 2) | ||||
| 		cp := func(dst io.Writer, src io.Reader) { | ||||
| 			_, err := io.Copy(dst, src) | ||||
| 			errc <- err | ||||
| 		} | ||||
|  | ||||
| 		go cp(out, in) | ||||
| 		go cp(in, out) | ||||
| 		err = <-errc | ||||
| 		if err != nil && err != io.EOF { | ||||
| 			c.Logger().Errorf("proxy raw, error=%v, url=%s", r.URL, err) | ||||
| 		} | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| // Next randomly returns an upstream target. | ||||
| func (r *RandomBalancer) Next() *ProxyTarget { | ||||
| 	if r.random == nil { | ||||
| 		r.random = rand.New(rand.NewSource(int64(time.Now().Nanosecond()))) | ||||
| 	} | ||||
| 	return r.Targets[r.random.Intn(len(r.Targets))] | ||||
| } | ||||
|  | ||||
| // Next returns an upstream target using round-robin technique. | ||||
| func (r *RoundRobinBalancer) Next() *ProxyTarget { | ||||
| 	r.i = r.i % uint32(len(r.Targets)) | ||||
| 	t := r.Targets[r.i] | ||||
| 	atomic.AddUint32(&r.i, 1) | ||||
| 	return t | ||||
| } | ||||
|  | ||||
| // Proxy returns an HTTP/WebSocket reverse proxy middleware. | ||||
| func Proxy(config ProxyConfig) echo.MiddlewareFunc { | ||||
| 	// Defaults | ||||
| 	if config.Skipper == nil { | ||||
| 		config.Skipper = DefaultLoggerConfig.Skipper | ||||
| 	} | ||||
| 	if config.Balancer == nil { | ||||
| 		panic("echo: proxy middleware requires balancer") | ||||
| 	} | ||||
|  | ||||
| 	return func(next echo.HandlerFunc) echo.HandlerFunc { | ||||
| 		return func(c echo.Context) (err error) { | ||||
| 			req := c.Request() | ||||
| 			res := c.Response() | ||||
| 			tgt := config.Balancer.Next() | ||||
|  | ||||
| 			// Fix header | ||||
| 			if req.Header.Get(echo.HeaderXRealIP) == "" { | ||||
| 				req.Header.Set(echo.HeaderXRealIP, c.RealIP()) | ||||
| 			} | ||||
| 			if req.Header.Get(echo.HeaderXForwardedProto) == "" { | ||||
| 				req.Header.Set(echo.HeaderXForwardedProto, c.Scheme()) | ||||
| 			} | ||||
| 			if c.IsWebSocket() && req.Header.Get(echo.HeaderXForwardedFor) == "" { // For HTTP, it is automatically set by Go HTTP reverse proxy. | ||||
| 				req.Header.Set(echo.HeaderXForwardedFor, c.RealIP()) | ||||
| 			} | ||||
|  | ||||
| 			// Proxy | ||||
| 			switch { | ||||
| 			case c.IsWebSocket(): | ||||
| 				proxyRaw(tgt, c).ServeHTTP(res, req) | ||||
| 			case req.Header.Get(echo.HeaderAccept) == "text/event-stream": | ||||
| 			default: | ||||
| 				proxyHTTP(tgt).ServeHTTP(res, req) | ||||
| 			} | ||||
|  | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										64
									
								
								vendor/github.com/labstack/echo/middleware/request_id.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								vendor/github.com/labstack/echo/middleware/request_id.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,64 @@ | ||||
| package middleware | ||||
|  | ||||
| import ( | ||||
| 	"github.com/labstack/echo" | ||||
| 	"github.com/labstack/gommon/random" | ||||
| ) | ||||
|  | ||||
| type ( | ||||
| 	// RequestIDConfig defines the config for RequestID middleware. | ||||
| 	RequestIDConfig struct { | ||||
| 		// Skipper defines a function to skip middleware. | ||||
| 		Skipper Skipper | ||||
|  | ||||
| 		// Generator defines a function to generate an ID. | ||||
| 		// Optional. Default value random.String(32). | ||||
| 		Generator func() string | ||||
| 	} | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	// DefaultRequestIDConfig is the default RequestID middleware config. | ||||
| 	DefaultRequestIDConfig = RequestIDConfig{ | ||||
| 		Skipper:   DefaultSkipper, | ||||
| 		Generator: generator, | ||||
| 	} | ||||
| ) | ||||
|  | ||||
| // RequestID returns a X-Request-ID middleware. | ||||
| func RequestID() echo.MiddlewareFunc { | ||||
| 	return RequestIDWithConfig(DefaultRequestIDConfig) | ||||
| } | ||||
|  | ||||
| // RequestIDWithConfig returns a X-Request-ID middleware with config. | ||||
| func RequestIDWithConfig(config RequestIDConfig) echo.MiddlewareFunc { | ||||
| 	// Defaults | ||||
| 	if config.Skipper == nil { | ||||
| 		config.Skipper = DefaultRequestIDConfig.Skipper | ||||
| 	} | ||||
| 	if config.Generator == nil { | ||||
| 		config.Generator = generator | ||||
| 	} | ||||
|  | ||||
| 	return func(next echo.HandlerFunc) echo.HandlerFunc { | ||||
| 		return func(c echo.Context) error { | ||||
| 			if config.Skipper(c) { | ||||
| 				return next(c) | ||||
| 			} | ||||
|  | ||||
| 			req := c.Request() | ||||
| 			res := c.Response() | ||||
| 			rid := req.Header.Get(echo.HeaderXRequestID) | ||||
| 			if rid == "" { | ||||
| 				rid = config.Generator() | ||||
| 			} | ||||
| 			res.Header().Set(echo.HeaderXRequestID, rid) | ||||
|  | ||||
| 			return next(c) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func generator() string { | ||||
| 	return random.String(32) | ||||
| } | ||||
							
								
								
									
										39
									
								
								vendor/github.com/labstack/echo/middleware/static.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										39
									
								
								vendor/github.com/labstack/echo/middleware/static.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -3,7 +3,9 @@ package middleware | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 	"path" | ||||
| 	"path/filepath" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/labstack/echo" | ||||
| ) | ||||
| @@ -53,6 +55,9 @@ func Static(root string) echo.MiddlewareFunc { | ||||
| // See `Static()`. | ||||
| func StaticWithConfig(config StaticConfig) echo.MiddlewareFunc { | ||||
| 	// Defaults | ||||
| 	if config.Root == "" { | ||||
| 		config.Root = "." // For security we want to restrict to CWD. | ||||
| 	} | ||||
| 	if config.Skipper == nil { | ||||
| 		config.Skipper = DefaultStaticConfig.Skipper | ||||
| 	} | ||||
| @@ -62,26 +67,44 @@ func StaticWithConfig(config StaticConfig) echo.MiddlewareFunc { | ||||
|  | ||||
| 	return func(next echo.HandlerFunc) echo.HandlerFunc { | ||||
| 		return func(c echo.Context) error { | ||||
| 			p := c.Param("*") | ||||
| 			name := filepath.Join(config.Root, p) | ||||
| 			fi, err := os.Stat(name) | ||||
| 			if config.Skipper(c) { | ||||
| 				return next(c) | ||||
| 			} | ||||
|  | ||||
| 			p := c.Request().URL.Path | ||||
| 			if strings.HasSuffix(c.Path(), "*") { // When serving from a group, e.g. `/static*`. | ||||
| 				p = c.Param("*") | ||||
| 			} | ||||
| 			name := filepath.Join(config.Root, path.Clean("/"+p)) // "/"+ for security | ||||
|  | ||||
| 			fi, err := os.Stat(name) | ||||
| 			if err != nil { | ||||
| 				if os.IsNotExist(err) { | ||||
| 					if config.HTML5 { | ||||
| 					if config.HTML5 && path.Ext(p) == "" { | ||||
| 						return c.File(filepath.Join(config.Root, config.Index)) | ||||
| 					} | ||||
| 					return echo.ErrNotFound | ||||
| 					return next(c) | ||||
| 				} | ||||
| 				return err | ||||
| 			} | ||||
|  | ||||
| 			if fi.IsDir() { | ||||
| 				if config.Browse { | ||||
| 					return listDir(name, c.Response()) | ||||
| 				index := filepath.Join(name, config.Index) | ||||
| 				fi, err = os.Stat(index) | ||||
|  | ||||
| 				if err != nil { | ||||
| 					if config.Browse { | ||||
| 						return listDir(name, c.Response()) | ||||
| 					} | ||||
| 					if os.IsNotExist(err) { | ||||
| 						return next(c) | ||||
| 					} | ||||
| 					return err | ||||
| 				} | ||||
| 				return c.File(filepath.Join(name, config.Index)) | ||||
|  | ||||
| 				return c.File(index) | ||||
| 			} | ||||
|  | ||||
| 			return c.File(name) | ||||
| 		} | ||||
| 	} | ||||
|   | ||||
							
								
								
									
										49
									
								
								vendor/github.com/labstack/echo/router.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										49
									
								
								vendor/github.com/labstack/echo/router.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -7,7 +7,7 @@ type ( | ||||
| 	// request matching and URL path parameter parsing. | ||||
| 	Router struct { | ||||
| 		tree   *node | ||||
| 		routes map[string]Route | ||||
| 		routes map[string]*Route | ||||
| 		echo   *Echo | ||||
| 	} | ||||
| 	node struct { | ||||
| @@ -47,7 +47,7 @@ func NewRouter(e *Echo) *Router { | ||||
| 		tree: &node{ | ||||
| 			methodHandler: new(methodHandler), | ||||
| 		}, | ||||
| 		routes: make(map[string]Route), | ||||
| 		routes: map[string]*Route{}, | ||||
| 		echo:   e, | ||||
| 	} | ||||
| } | ||||
| @@ -101,7 +101,7 @@ func (r *Router) insert(method, path string, h HandlerFunc, t kind, ppath string | ||||
|  | ||||
| 	cn := r.tree // Current node as root | ||||
| 	if cn == nil { | ||||
| 		panic("echo ⇛ invalid method") | ||||
| 		panic("echo: invalid method") | ||||
| 	} | ||||
| 	search := path | ||||
|  | ||||
| @@ -296,18 +296,19 @@ func (n *node) checkMethodNotAllowed() HandlerFunc { | ||||
| // - Get context from `Echo#AcquireContext()` | ||||
| // - Reset it `Context#Reset()` | ||||
| // - Return it `Echo#ReleaseContext()`. | ||||
| func (r *Router) Find(method, path string, context Context) { | ||||
| 	context.SetPath(path) | ||||
| func (r *Router) Find(method, path string, c Context) { | ||||
| 	ctx := c.(*context) | ||||
| 	ctx.path = path | ||||
| 	cn := r.tree // Current node as root | ||||
|  | ||||
| 	var ( | ||||
| 		search  = path | ||||
| 		c       *node  // Child node | ||||
| 		n       int    // Param counter | ||||
| 		nk      kind   // Next kind | ||||
| 		nn      *node  // Next node | ||||
| 		ns      string // Next search | ||||
| 		pvalues = context.ParamValues() | ||||
| 		child   *node         // Child node | ||||
| 		n       int           // Param counter | ||||
| 		nk      kind          // Next kind | ||||
| 		nn      *node         // Next node | ||||
| 		ns      string        // Next search | ||||
| 		pvalues = ctx.pvalues // Use the internal slice so the interface can keep the illusion of a dynamic slice | ||||
| 	) | ||||
|  | ||||
| 	// Search order static > param > any | ||||
| @@ -352,20 +353,20 @@ func (r *Router) Find(method, path string, context Context) { | ||||
| 		} | ||||
|  | ||||
| 		// Static node | ||||
| 		if c = cn.findChild(search[0], skind); c != nil { | ||||
| 		if child = cn.findChild(search[0], skind); child != nil { | ||||
| 			// Save next | ||||
| 			if cn.prefix[len(cn.prefix)-1] == '/' { // Issue #623 | ||||
| 				nk = pkind | ||||
| 				nn = cn | ||||
| 				ns = search | ||||
| 			} | ||||
| 			cn = c | ||||
| 			cn = child | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		// Param node | ||||
| 	Param: | ||||
| 		if c = cn.findChildByKind(pkind); c != nil { | ||||
| 		if child = cn.findChildByKind(pkind); child != nil { | ||||
| 			// Issue #378 | ||||
| 			if len(pvalues) == n { | ||||
| 				continue | ||||
| @@ -378,7 +379,7 @@ func (r *Router) Find(method, path string, context Context) { | ||||
| 				ns = search | ||||
| 			} | ||||
|  | ||||
| 			cn = c | ||||
| 			cn = child | ||||
| 			i, l := 0, len(search) | ||||
| 			for ; i < l && search[i] != '/'; i++ { | ||||
| 			} | ||||
| @@ -409,13 +410,13 @@ func (r *Router) Find(method, path string, context Context) { | ||||
| 	} | ||||
|  | ||||
| End: | ||||
| 	context.SetHandler(cn.findHandler(method)) | ||||
| 	context.SetPath(cn.ppath) | ||||
| 	context.SetParamNames(cn.pnames...) | ||||
| 	ctx.handler = cn.findHandler(method) | ||||
| 	ctx.path = cn.ppath | ||||
| 	ctx.pnames = cn.pnames | ||||
|  | ||||
| 	// NOTE: Slow zone... | ||||
| 	if context.Handler() == nil { | ||||
| 		context.SetHandler(cn.checkMethodNotAllowed()) | ||||
| 	if ctx.handler == nil { | ||||
| 		ctx.handler = cn.checkMethodNotAllowed() | ||||
|  | ||||
| 		// Dig further for any, might have an empty value for *, e.g. | ||||
| 		// serving a directory. Issue #207. | ||||
| @@ -423,12 +424,12 @@ End: | ||||
| 			return | ||||
| 		} | ||||
| 		if h := cn.findHandler(method); h != nil { | ||||
| 			context.SetHandler(h) | ||||
| 			ctx.handler = h | ||||
| 		} else { | ||||
| 			context.SetHandler(cn.checkMethodNotAllowed()) | ||||
| 			ctx.handler = cn.checkMethodNotAllowed() | ||||
| 		} | ||||
| 		context.SetPath(cn.ppath) | ||||
| 		context.SetParamNames(cn.pnames...) | ||||
| 		ctx.path = cn.ppath | ||||
| 		ctx.pnames = cn.pnames | ||||
| 		pvalues[len(cn.pnames)-1] = "" | ||||
| 	} | ||||
|  | ||||
|   | ||||
							
								
								
									
										42
									
								
								vendor/github.com/labstack/gommon/random/random.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										42
									
								
								vendor/github.com/labstack/gommon/random/random.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -2,22 +2,24 @@ package random | ||||
|  | ||||
| import ( | ||||
| 	"math/rand" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| ) | ||||
|  | ||||
| type ( | ||||
| 	Random struct { | ||||
| 		charset Charset | ||||
| 	} | ||||
|  | ||||
| 	Charset string | ||||
| ) | ||||
|  | ||||
| // Charsets | ||||
| const ( | ||||
| 	Alphanumeric Charset = Alphabetic + Numeric | ||||
| 	Alphabetic   Charset = "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" | ||||
| 	Numeric      Charset = "0123456789" | ||||
| 	Hex          Charset = Numeric + "abcdef" | ||||
| 	Uppercase    string = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" | ||||
| 	Lowercase           = "abcdefghijklmnopqrstuvwxyz" | ||||
| 	Alphabetic          = Uppercase + Lowercase | ||||
| 	Numeric             = "0123456789" | ||||
| 	Alphanumeric        = Alphabetic + Numeric | ||||
| 	Symbols             = "`" + `~!@#$%^&*()-_+={}[]|\;:"<>,./?` | ||||
| 	Hex                 = Numeric + "abcdef" | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| @@ -26,27 +28,21 @@ var ( | ||||
|  | ||||
| func New() *Random { | ||||
| 	rand.Seed(time.Now().UnixNano()) | ||||
| 	return &Random{ | ||||
| 		charset: Alphanumeric, | ||||
| 	return new(Random) | ||||
| } | ||||
|  | ||||
| func (r *Random) String(length uint8, charsets ...string) string { | ||||
| 	charset := strings.Join(charsets, "") | ||||
| 	if charset == "" { | ||||
| 		charset = Alphanumeric | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (r *Random) SetCharset(c Charset) { | ||||
| 	r.charset = c | ||||
| } | ||||
|  | ||||
| func (r *Random) String(length uint8) string { | ||||
| 	b := make([]byte, length) | ||||
| 	for i := range b { | ||||
| 		b[i] = r.charset[rand.Int63()%int64(len(r.charset))] | ||||
| 		b[i] = charset[rand.Int63()%int64(len(charset))] | ||||
| 	} | ||||
| 	return string(b) | ||||
| } | ||||
|  | ||||
| func SetCharset(c Charset) { | ||||
| 	global.SetCharset(c) | ||||
| } | ||||
|  | ||||
| func String(length uint8) string { | ||||
| 	return global.String(length) | ||||
| func String(length uint8, charsets ...string) string { | ||||
| 	return global.String(length, charsets...) | ||||
| } | ||||
|   | ||||
							
								
								
									
										5
									
								
								vendor/github.com/sromku/go-gitter/stream.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								vendor/github.com/sromku/go-gitter/stream.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -137,7 +137,10 @@ func (stream *Stream) connect() { | ||||
|  | ||||
| 	res, err := stream.gitter.getResponse(stream.url, stream) | ||||
| 	if err != nil || res.StatusCode != 200 { | ||||
| 		stream.gitter.log(fmt.Sprintf("Failed to get response, trying reconnect (Status code: %v)", res.StatusCode)) | ||||
| 		stream.gitter.log("Failed to get response, trying reconnect") | ||||
| 		if res != nil { | ||||
| 			stream.gitter.log(fmt.Sprintf("Status code: %v", res.StatusCode)) | ||||
| 		} | ||||
| 		stream.gitter.log(err) | ||||
|  | ||||
| 		// sleep and wait | ||||
|   | ||||
							
								
								
									
										2
									
								
								vendor/github.com/valyala/fasttemplate/unsafe.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/valyala/fasttemplate/unsafe.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,3 +1,5 @@ | ||||
| // +build !appengine | ||||
|  | ||||
| package fasttemplate | ||||
|  | ||||
| import ( | ||||
|   | ||||
							
								
								
									
										11
									
								
								vendor/github.com/valyala/fasttemplate/unsafe_gae.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								vendor/github.com/valyala/fasttemplate/unsafe_gae.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| // +build appengine | ||||
|  | ||||
| package fasttemplate | ||||
|  | ||||
| func unsafeBytes2String(b []byte) string { | ||||
| 	return string(b) | ||||
| } | ||||
|  | ||||
| func unsafeString2Bytes(s string) []byte { | ||||
| 	return []byte(s) | ||||
| } | ||||
							
								
								
									
										16
									
								
								vendor/manifest
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										16
									
								
								vendor/manifest
									
									
									
									
										vendored
									
									
								
							| @@ -53,7 +53,7 @@ | ||||
| 			"importpath": "github.com/dgrijalva/jwt-go", | ||||
| 			"repository": "https://github.com/dgrijalva/jwt-go", | ||||
| 			"vcs": "git", | ||||
| 			"revision": "2268707a8f0843315e2004ee4f1d021dc08baedf", | ||||
| 			"revision": "6c8dedd55f8a2e41f605de6d5d66e51ed1f299fc", | ||||
| 			"branch": "master", | ||||
| 			"notests": true | ||||
| 		}, | ||||
| @@ -170,7 +170,7 @@ | ||||
| 			"importpath": "github.com/labstack/echo", | ||||
| 			"repository": "https://github.com/labstack/echo", | ||||
| 			"vcs": "git", | ||||
| 			"revision": "0b53f397ad7709a27d37500a67735c0a639b5c38", | ||||
| 			"revision": "c3887ebb131d996411cf13a9688ab02c8dba599e", | ||||
| 			"branch": "master", | ||||
| 			"notests": true | ||||
| 		}, | ||||
| @@ -178,7 +178,7 @@ | ||||
| 			"importpath": "github.com/labstack/gommon/bytes", | ||||
| 			"repository": "https://github.com/labstack/gommon", | ||||
| 			"vcs": "git", | ||||
| 			"revision": "f72d3c883f8ea180da8f085dd320804c41332ad1", | ||||
| 			"revision": "1121fd3e243c202482226a7afe4dcd07ffc4139a", | ||||
| 			"branch": "master", | ||||
| 			"path": "/bytes", | ||||
| 			"notests": true | ||||
| @@ -187,7 +187,7 @@ | ||||
| 			"importpath": "github.com/labstack/gommon/color", | ||||
| 			"repository": "https://github.com/labstack/gommon", | ||||
| 			"vcs": "git", | ||||
| 			"revision": "f72d3c883f8ea180da8f085dd320804c41332ad1", | ||||
| 			"revision": "1121fd3e243c202482226a7afe4dcd07ffc4139a", | ||||
| 			"branch": "master", | ||||
| 			"path": "/color", | ||||
| 			"notests": true | ||||
| @@ -196,7 +196,7 @@ | ||||
| 			"importpath": "github.com/labstack/gommon/log", | ||||
| 			"repository": "https://github.com/labstack/gommon", | ||||
| 			"vcs": "git", | ||||
| 			"revision": "f72d3c883f8ea180da8f085dd320804c41332ad1", | ||||
| 			"revision": "1121fd3e243c202482226a7afe4dcd07ffc4139a", | ||||
| 			"branch": "master", | ||||
| 			"path": "/log", | ||||
| 			"notests": true | ||||
| @@ -205,7 +205,7 @@ | ||||
| 			"importpath": "github.com/labstack/gommon/random", | ||||
| 			"repository": "https://github.com/labstack/gommon", | ||||
| 			"vcs": "git", | ||||
| 			"revision": "f72d3c883f8ea180da8f085dd320804c41332ad1", | ||||
| 			"revision": "1121fd3e243c202482226a7afe4dcd07ffc4139a", | ||||
| 			"branch": "master", | ||||
| 			"path": "/random", | ||||
| 			"notests": true | ||||
| @@ -384,7 +384,7 @@ | ||||
| 			"importpath": "github.com/sromku/go-gitter", | ||||
| 			"repository": "https://github.com/sromku/go-gitter", | ||||
| 			"vcs": "git", | ||||
| 			"revision": "0b9f26da1844acd3c6c2fd4ee2bdd30eb1a40568", | ||||
| 			"revision": "16aadfbb65c6641501ea8eebb6bcf23b9011912b", | ||||
| 			"branch": "master", | ||||
| 			"notests": true | ||||
| 		}, | ||||
| @@ -424,7 +424,7 @@ | ||||
| 			"importpath": "github.com/valyala/fasttemplate", | ||||
| 			"repository": "https://github.com/valyala/fasttemplate", | ||||
| 			"vcs": "git", | ||||
| 			"revision": "d090d65668a286d9a180d43a19dfdc5dcad8fe88", | ||||
| 			"revision": "dcecefd839c4193db0d35b88ec65b4c12d360ab0", | ||||
| 			"branch": "master", | ||||
| 			"notests": true | ||||
| 		}, | ||||
|   | ||||
		Reference in New Issue
	
	Block a user