Show last IRC connection error in status tab, log IRC connection errors

This commit is contained in:
Ken-Håvard Lieng 2017-07-02 03:31:00 +02:00
parent 786d8013b9
commit 18aff3ded6
19 changed files with 294 additions and 189 deletions

View file

@ -20,7 +20,7 @@ type connectDefaults struct {
type indexData struct {
Defaults connectDefaults `json:"defaults"`
Servers []storage.Server `json:"servers,omitempty"`
Servers []Server `json:"servers,omitempty"`
Channels []storage.Channel `json:"channels,omitempty"`
// Users in the selected channel
@ -57,32 +57,32 @@ func (d *indexData) addUsersAndMessages(server, channel string, session *Session
}
func getIndexData(r *http.Request, session *Session) *indexData {
data := indexData{}
servers := session.user.GetServers()
connections := session.getConnectionStates()
for i, server := range servers {
servers[i].Connected = connections[server.Host]
servers[i].Port = ""
servers[i].TLS = false
servers[i].Password = ""
servers[i].Username = ""
servers[i].Realname = ""
for _, server := range servers {
server.Password = ""
server.Username = ""
server.Realname = ""
data.Servers = append(data.Servers, Server{
Server: server,
Status: newConnectionUpdate(server.Host, connections[server.Host]),
})
}
channels := session.user.GetChannels()
for i, channel := range channels {
channels[i].Topic = channelStore.GetTopic(channel.Server, channel.Name)
}
data.Channels = channels
data := indexData{
Defaults: connectDefaults{
Name: viper.GetString("defaults.name"),
Address: viper.GetString("defaults.address"),
Channels: viper.GetStringSlice("defaults.channels"),
Password: viper.GetString("defaults.password") != "",
SSL: viper.GetBool("defaults.ssl"),
},
Servers: servers,
Channels: channels,
data.Defaults = connectDefaults{
Name: viper.GetString("defaults.name"),
Address: viper.GetString("defaults.address"),
Channels: viper.GetStringSlice("defaults.channels"),
Password: viper.GetString("defaults.password") != "",
SSL: viper.GetBool("defaults.ssl"),
}
server, channel := getTabFromPath(r.URL.EscapedPath())

View file

@ -27,31 +27,7 @@ func reconnectIRC() {
channels := user.GetChannels()
for _, server := range user.GetServers() {
i := irc.NewClient(server.Nick, server.Username)
i.TLS = server.TLS
i.Password = server.Password
i.Realname = server.Realname
i.HandleNickInUse = createNickInUseHandler(i, session)
if i.TLS {
i.TLSConfig = &tls.Config{
InsecureSkipVerify: !viper.GetBool("verify_certificates"),
}
if cert := user.GetCertificate(); cert != nil {
i.TLSConfig.Certificates = []tls.Certificate{*cert}
}
}
session.setIRC(server.Host, i)
if server.Port != "" {
i.Connect(net.JoinHostPort(server.Host, server.Port))
} else {
i.Connect(server.Host)
}
go newIRCHandler(i, session).run()
i := connectIRC(server, session)
var joining []string
for _, channel := range channels {
@ -63,3 +39,39 @@ func reconnectIRC() {
}
}
}
func connectIRC(server storage.Server, session *Session) *irc.Client {
i := irc.NewClient(server.Nick, server.Username)
i.TLS = server.TLS
i.Realname = server.Realname
i.HandleNickInUse = createNickInUseHandler(i, session)
address := server.Host
if server.Port != "" {
address = net.JoinHostPort(server.Host, server.Port)
}
if server.Password == "" &&
viper.GetString("defaults.password") != "" &&
address == viper.GetString("defaults.address") {
i.Password = viper.GetString("defaults.password")
} else {
i.Password = server.Password
}
if i.TLS {
i.TLSConfig = &tls.Config{
InsecureSkipVerify: !viper.GetBool("verify_certificates"),
}
if cert := session.user.GetCertificate(); cert != nil {
i.TLSConfig.Certificates = []tls.Certificate{*cert}
}
}
session.setIRC(server.Host, i)
i.Connect(address)
go newIRCHandler(i, session).run()
return i
}

View file

@ -1,6 +1,7 @@
package server
import (
"fmt"
"log"
"strings"
"unicode"
@ -37,6 +38,7 @@ func newIRCHandler(client *irc.Client, session *Session) *ircHandler {
}
func (i *ircHandler) run() {
var lastConnErr error
for {
select {
case msg, ok := <-i.client.Messages:
@ -47,11 +49,17 @@ func (i *ircHandler) run() {
i.dispatchMessage(msg)
case connected := <-i.client.ConnectionChanged:
i.session.sendJSON("connection_update", map[string]bool{
i.client.Host: connected,
})
i.session.setConnectionState(i.client.Host, connected)
case state := <-i.client.ConnectionChanged:
i.session.sendJSON("connection_update", newConnectionUpdate(i.client.Host, state))
i.session.setConnectionState(i.client.Host, state)
if state.Error != nil && (lastConnErr == nil ||
state.Error.Error() != lastConnErr.Error()) {
lastConnErr = state.Error
i.log("Connection error:", state.Error)
} else if state.Connected {
i.log("Connected")
}
}
}
}
@ -312,6 +320,11 @@ func (i *ircHandler) initHandlers() {
}
}
func (i *ircHandler) log(v ...interface{}) {
s := fmt.Sprintln(v...)
log.Println("[IRC]", i.session.user.ID, i.client.Host, s[:len(s)-1])
}
func parseMode(mode string) *Mode {
m := Mode{}
add := false

View file

@ -3,6 +3,7 @@ package server
import (
"encoding/json"
"github.com/khlieng/dispatch/irc"
"github.com/khlieng/dispatch/storage"
)
@ -16,14 +17,31 @@ type WSResponse struct {
Data interface{} `json:"data"`
}
type Connect struct {
Name string `json:"name"`
Server string `json:"server"`
TLS bool `json:"tls"`
Password string `json:"password"`
Nick string `json:"nick"`
Username string `json:"username"`
Realname string `json:"realname"`
type Server struct {
storage.Server
Status ConnectionUpdate `json:"status"`
}
type ServerName struct {
Server string `json:"server"`
Name string `json:"name"`
}
type ConnectionUpdate struct {
Server string `json:"server"`
Connected bool `json:"connected"`
Error string `json:"error,omitempty"`
}
func newConnectionUpdate(server string, state irc.ConnectionState) ConnectionUpdate {
status := ConnectionUpdate{
Server: server,
Connected: state.Connected,
}
if state.Error != nil {
status.Error = state.Error.Error()
}
return status
}
type Nick struct {

View file

@ -16,7 +16,7 @@ const (
type Session struct {
irc map[string]*irc.Client
connectionState map[string]bool
connectionState map[string]irc.ConnectionState
ircLock sync.Mutex
ws map[string]*wsConn
@ -31,7 +31,7 @@ type Session struct {
func NewSession(user *storage.User) *Session {
return &Session{
irc: make(map[string]*irc.Client),
connectionState: make(map[string]bool),
connectionState: make(map[string]irc.ConnectionState),
ws: make(map[string]*wsConn),
broadcast: make(chan WSResponse, 32),
user: user,
@ -51,7 +51,9 @@ func (s *Session) getIRC(server string) (*irc.Client, bool) {
func (s *Session) setIRC(server string, i *irc.Client) {
s.ircLock.Lock()
s.irc[server] = i
s.connectionState[server] = false
s.connectionState[server] = irc.ConnectionState{
Connected: false,
}
s.ircLock.Unlock()
s.reset <- 0
@ -74,9 +76,9 @@ func (s *Session) numIRC() int {
return n
}
func (s *Session) getConnectionStates() map[string]bool {
func (s *Session) getConnectionStates() map[string]irc.ConnectionState {
s.ircLock.Lock()
state := make(map[string]bool, len(s.connectionState))
state := make(map[string]irc.ConnectionState, len(s.connectionState))
for k, v := range s.connectionState {
state[k] = v
@ -86,9 +88,9 @@ func (s *Session) getConnectionStates() map[string]bool {
return state
}
func (s *Session) setConnectionState(server string, connected bool) {
func (s *Session) setConnectionState(server string, state irc.ConnectionState) {
s.ircLock.Lock()
s.connectionState[server] = connected
s.connectionState[server] = state
s.ircLock.Unlock()
}

View file

@ -1,19 +1,13 @@
package server
import (
"crypto/tls"
"encoding/json"
"log"
"net"
"net/http"
"strings"
"github.com/gorilla/websocket"
"github.com/kjk/betterguid"
"github.com/spf13/viper"
"github.com/khlieng/dispatch/irc"
"github.com/khlieng/dispatch/storage"
)
type wsHandler struct {
@ -87,56 +81,17 @@ func (h *wsHandler) init(r *http.Request) {
}
func (h *wsHandler) connect(b []byte) {
var data Connect
var data Server
json.Unmarshal(b, &data)
host, port, err := net.SplitHostPort(data.Server)
if err != nil {
host = data.Server
}
if _, ok := h.session.getIRC(host); !ok {
if _, ok := h.session.getIRC(data.Host); !ok {
log.Println(h.addr, "[IRC] Add server", data.Server)
i := irc.NewClient(data.Nick, data.Username)
i.TLS = data.TLS
i.Realname = data.Realname
i.HandleNickInUse = createNickInUseHandler(i, h.session)
connectIRC(data.Server, h.session)
if data.Password == "" &&
viper.GetString("defaults.password") != "" &&
data.Server == viper.GetString("defaults.address") {
i.Password = viper.GetString("defaults.password")
} else {
i.Password = data.Password
}
if i.TLS {
i.TLSConfig = &tls.Config{
InsecureSkipVerify: !viper.GetBool("verify_certificates"),
}
if cert := h.session.user.GetCertificate(); cert != nil {
i.TLSConfig.Certificates = []tls.Certificate{*cert}
}
}
h.session.setIRC(host, i)
i.Connect(data.Server)
go newIRCHandler(i, h.session).run()
go h.session.user.AddServer(storage.Server{
Name: data.Name,
Host: host,
Port: port,
TLS: data.TLS,
Password: data.Password,
Nick: data.Nick,
Username: data.Username,
Realname: data.Realname,
})
go h.session.user.AddServer(data.Server)
} else {
log.Println(h.addr, "[IRC]", data.Server, "already added")
log.Println(h.addr, "[IRC]", data.Host, "already added")
}
}
@ -287,7 +242,7 @@ func (h *wsHandler) fetchMessages(b []byte) {
}
func (h *wsHandler) setServerName(b []byte) {
var data Connect
var data ServerName
json.Unmarshal(b, &data)
if isValidServerName(data.Name) {