Compare commits
2 Commits
Author | SHA1 | Date |
---|---|---|
Martin/Geno | eda91728b6 | |
Martin/Geno | bd940a7656 |
|
@ -2,7 +2,6 @@ package bot
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
|
||||||
|
|
||||||
timeago "github.com/ararog/timeago"
|
timeago "github.com/ararog/timeago"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
@ -235,11 +234,16 @@ func (b *Bot) addRegex(answer func(string), from string, params []string) {
|
||||||
answer("invalid: CMD regex\n or\n CMD channel regex")
|
answer("invalid: CMD regex\n or\n CMD channel regex")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
regex := strings.Join(params, " ")
|
of := from
|
||||||
|
regex := params[0]
|
||||||
|
if len(params) > 1 {
|
||||||
|
of = params[0]
|
||||||
|
regex = params[1]
|
||||||
|
}
|
||||||
|
|
||||||
n := b.db.NotifiesByAddress[from]
|
n := b.db.NotifiesByAddress[of]
|
||||||
if err := n.AddRegex(regex); err == nil {
|
if err := n.AddRegex(regex); err == nil {
|
||||||
answer(fmt.Sprintf("add regex for \"%s\" to %s", from, regex))
|
answer(fmt.Sprintf("add regex for \"%s\" to %s", of, regex))
|
||||||
} else {
|
} else {
|
||||||
answer(fmt.Sprintf("\"%s\" is no valid regex expression: %s", regex, err))
|
answer(fmt.Sprintf("\"%s\" is no valid regex expression: %s", regex, err))
|
||||||
}
|
}
|
||||||
|
@ -251,8 +255,79 @@ func (b *Bot) delRegex(answer func(string), from string, params []string) {
|
||||||
answer("invalid: CMD regex\n or\n CMD channel regex")
|
answer("invalid: CMD regex\n or\n CMD channel regex")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
n := b.db.NotifiesByAddress[from]
|
of := from
|
||||||
regex := strings.Join(params, " ")
|
regex := params[0]
|
||||||
|
if len(params) > 1 {
|
||||||
|
of = params[0]
|
||||||
|
regex = params[1]
|
||||||
|
}
|
||||||
|
n := b.db.NotifiesByAddress[of]
|
||||||
delete(n.RegexIn, regex)
|
delete(n.RegexIn, regex)
|
||||||
b.listRegex(answer, from, []string{})
|
b.listRegex(answer, of, []string{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// list of regex replace
|
||||||
|
func (b *Bot) listRegexReplace(answer func(string), from string, params []string) {
|
||||||
|
msg := "replaces:\n"
|
||||||
|
if len(params) > 0 && params[0] == "all" {
|
||||||
|
for _, n := range b.db.Notifies {
|
||||||
|
msg = fmt.Sprintf("%s%s\n-------------\n", msg, n.Address())
|
||||||
|
for expression, value := range n.RegexReplace {
|
||||||
|
msg = fmt.Sprintf("%s - \"%s\" : \"%s\"\n", msg, expression, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
of := from
|
||||||
|
if len(params) > 0 {
|
||||||
|
of = params[0]
|
||||||
|
}
|
||||||
|
if n, ok := b.db.NotifiesByAddress[of]; ok {
|
||||||
|
msg = fmt.Sprintf("%s%s\n-------------\n", msg, of)
|
||||||
|
for expression, value := range n.RegexReplace {
|
||||||
|
msg = fmt.Sprintf("%s - \"%s\" : \"%s\"\n", msg, expression, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
answer(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// add a regex replace
|
||||||
|
func (b *Bot) addRegexReplace(answer func(string), from string, params []string) {
|
||||||
|
if len(params) < 1 {
|
||||||
|
answer("invalid: CMD regex replace\n or\n CMD channel regex replace")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
of := from
|
||||||
|
regex := params[0]
|
||||||
|
value := params[1]
|
||||||
|
if len(params) > 2 {
|
||||||
|
of = params[0]
|
||||||
|
regex = params[1]
|
||||||
|
value = params[2]
|
||||||
|
}
|
||||||
|
|
||||||
|
n := b.db.NotifiesByAddress[of]
|
||||||
|
if err := n.AddRegexReplace(regex, value); err == nil {
|
||||||
|
answer(fmt.Sprintf("add replace in \"%s\" for \"%s\" to \"%s\"", of, regex, value))
|
||||||
|
} else {
|
||||||
|
answer(fmt.Sprintf("\"%s\" to \"%s\" is no valid regex replace expression: %s", regex, value, err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// del a regex replace
|
||||||
|
func (b *Bot) delRegexReplace(answer func(string), from string, params []string) {
|
||||||
|
if len(params) < 1 {
|
||||||
|
answer("invalid: CMD regex\n or\n CMD channel regex")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
of := from
|
||||||
|
regex := params[0]
|
||||||
|
if len(params) > 1 {
|
||||||
|
of = params[0]
|
||||||
|
regex = params[1]
|
||||||
|
}
|
||||||
|
n := b.db.NotifiesByAddress[of]
|
||||||
|
|
||||||
|
delete(n.RegexReplace, regex)
|
||||||
|
b.listRegexReplace(answer, of, []string{})
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,9 @@ func NewBot(db *database.DB) *Bot {
|
||||||
"regex-add": b.addRegex,
|
"regex-add": b.addRegex,
|
||||||
"regex-list": b.listRegex,
|
"regex-list": b.listRegex,
|
||||||
"regex-del": b.delRegex,
|
"regex-del": b.delRegex,
|
||||||
|
"replace-add": b.addRegexReplace,
|
||||||
|
"replace-list": b.listRegexReplace,
|
||||||
|
"replace-del": b.delRegexReplace,
|
||||||
}
|
}
|
||||||
for k := range b.commandsMap {
|
for k := range b.commandsMap {
|
||||||
b.commands = append(b.commands, k)
|
b.commands = append(b.commands, k)
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
@ -57,7 +56,7 @@ var serverCmd = &cobra.Command{
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
for a := range logChannel {
|
for a := range logChannel {
|
||||||
notifier.Send(a)
|
notifier.Send(a, nil)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
@ -71,16 +70,6 @@ var serverCmd = &cobra.Command{
|
||||||
|
|
||||||
go receiver.Listen()
|
go receiver.Listen()
|
||||||
|
|
||||||
srv := &http.Server{
|
|
||||||
Addr: config.HTTPAddress,
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
if err := srv.ListenAndServe(); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Wait for system signal
|
// Wait for system signal
|
||||||
sigchan := make(chan os.Signal, 1)
|
sigchan := make(chan os.Signal, 1)
|
||||||
signal.Notify(sigchan, syscall.SIGTERM, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGUSR1)
|
signal.Notify(sigchan, syscall.SIGTERM, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGUSR1)
|
||||||
|
|
|
@ -64,7 +64,7 @@ func (db *DB) SendTo(e *log.Entry) (*log.Entry, *Host, []*Notify) {
|
||||||
return e, host, nil
|
return e, host, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) Alert(expired time.Duration, send func(e *log.Entry) error) {
|
func (db *DB) Alert(expired time.Duration, send func(e *log.Entry, n *Notify) bool) {
|
||||||
c := time.Tick(time.Minute)
|
c := time.Tick(time.Minute)
|
||||||
|
|
||||||
for range c {
|
for range c {
|
||||||
|
@ -81,7 +81,7 @@ func (db *DB) Alert(expired time.Duration, send func(e *log.Entry) error) {
|
||||||
entry.Level = log.ErrorLevel
|
entry.Level = log.ErrorLevel
|
||||||
entry.Message = AlertMsg
|
entry.Message = AlertMsg
|
||||||
entry.WithField("hostname", h.Address)
|
entry.WithField("hostname", h.Address)
|
||||||
send(entry)
|
send(entry, nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,22 +8,36 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Notify struct {
|
type Notify struct {
|
||||||
Protocoll string `json:"proto"`
|
Protocol string `json:"proto"`
|
||||||
To string `json:"to"`
|
To string `json:"to"`
|
||||||
RegexIn map[string]*regexp.Regexp `json:"regexIn"`
|
RegexIn map[string]*regexp.Regexp `json:"regexIn"`
|
||||||
MaxPrioIn log.Level `json:"maxLevel"`
|
RegexReplace map[string]string `json:"regexReplace"`
|
||||||
|
MaxPrioIn log.Level `json:"maxLevel"`
|
||||||
|
regexReplaceExpression map[string]*regexp.Regexp
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Notify) Init() {
|
func (n *Notify) Init() {
|
||||||
if n.RegexIn == nil {
|
if n.RegexIn == nil {
|
||||||
n.RegexIn = make(map[string]*regexp.Regexp)
|
n.RegexIn = make(map[string]*regexp.Regexp)
|
||||||
}
|
}
|
||||||
|
if n.RegexReplace == nil {
|
||||||
|
n.RegexReplace = make(map[string]string)
|
||||||
|
}
|
||||||
|
if n.regexReplaceExpression == nil {
|
||||||
|
n.regexReplaceExpression = make(map[string]*regexp.Regexp)
|
||||||
|
}
|
||||||
for exp := range n.RegexIn {
|
for exp := range n.RegexIn {
|
||||||
regex, err := regexp.Compile(exp)
|
regex, err := regexp.Compile(exp)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
n.RegexIn[exp] = regex
|
n.RegexIn[exp] = regex
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for exp := range n.RegexReplace {
|
||||||
|
regex, err := regexp.Compile(exp)
|
||||||
|
if err == nil {
|
||||||
|
n.regexReplaceExpression[exp] = regex
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Notify) AddRegex(expression string) error {
|
func (n *Notify) AddRegex(expression string) error {
|
||||||
|
@ -33,9 +47,24 @@ func (n *Notify) AddRegex(expression string) error {
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
func (n *Notify) AddRegexReplace(expression, value string) error {
|
||||||
|
regex, err := regexp.Compile(expression)
|
||||||
|
if err == nil {
|
||||||
|
n.regexReplaceExpression[expression] = regex
|
||||||
|
n.RegexReplace[expression] = value
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
func (n *Notify) RunReplace(msg string) string {
|
||||||
|
for key, re := range n.regexReplaceExpression {
|
||||||
|
value := n.RegexReplace[key]
|
||||||
|
msg = re.ReplaceAllString(msg, value)
|
||||||
|
}
|
||||||
|
return msg
|
||||||
|
}
|
||||||
|
|
||||||
func (n *Notify) Address() string {
|
func (n *Notify) Address() string {
|
||||||
return n.Protocoll + ":" + n.To
|
return n.Protocol + ":" + n.To
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- global notify
|
// -- global notify
|
||||||
|
@ -58,9 +87,9 @@ func (db *DB) AddNotify(n *Notify) {
|
||||||
func (db *DB) NewNotify(to string) *Notify {
|
func (db *DB) NewNotify(to string) *Notify {
|
||||||
addr := strings.Split(to, ":")
|
addr := strings.Split(to, ":")
|
||||||
n := &Notify{
|
n := &Notify{
|
||||||
Protocoll: addr[0],
|
Protocol: addr[0],
|
||||||
To: addr[1],
|
To: addr[1],
|
||||||
RegexIn: make(map[string]*regexp.Regexp),
|
RegexIn: make(map[string]*regexp.Regexp),
|
||||||
}
|
}
|
||||||
db.AddNotify(n)
|
db.AddNotify(n)
|
||||||
return n
|
return n
|
||||||
|
|
|
@ -3,10 +3,9 @@ package lib
|
||||||
// Struct of the configuration
|
// Struct of the configuration
|
||||||
// e.g. under dev.sum7.eu/genofire/logmania/logmania_example.conf
|
// e.g. under dev.sum7.eu/genofire/logmania/logmania_example.conf
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Notify NotifyConfig `toml:"notify"`
|
Notify NotifyConfig `toml:"notify"`
|
||||||
Receive ReceiveConfig `toml:"receive"`
|
Receive ReceiveConfig `toml:"receive"`
|
||||||
DB string `toml:"database"`
|
DB string `toml:"database"`
|
||||||
HTTPAddress string `toml:"http_address"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type NotifyConfig struct {
|
type NotifyConfig struct {
|
||||||
|
@ -16,7 +15,10 @@ type NotifyConfig struct {
|
||||||
JID string `toml:"jid"`
|
JID string `toml:"jid"`
|
||||||
Password string `toml:"password"`
|
Password string `toml:"password"`
|
||||||
} `toml:"xmpp"`
|
} `toml:"xmpp"`
|
||||||
File string `toml:"file"`
|
Websocket struct {
|
||||||
|
Address string `toml:"address"`
|
||||||
|
Webroot string `toml:"webroot"`
|
||||||
|
} `toml:"websocket"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ReceiveConfig struct {
|
type ReceiveConfig struct {
|
||||||
|
|
|
@ -1,7 +1,3 @@
|
||||||
[notify]
|
|
||||||
state_file = "/tmp/logmania.state.json"
|
|
||||||
debug = true
|
|
||||||
|
|
||||||
[receive.syslog]
|
[receive.syslog]
|
||||||
type = "udp"
|
type = "udp"
|
||||||
address = ":10001"
|
address = ":10001"
|
||||||
|
@ -9,3 +5,15 @@ address = ":10001"
|
||||||
[receive.journald_json]
|
[receive.journald_json]
|
||||||
type = "udp"
|
type = "udp"
|
||||||
address = ":10002"
|
address = ":10002"
|
||||||
|
|
||||||
|
[notify]
|
||||||
|
state_file = "/tmp/logmania.state.json"
|
||||||
|
debug = true
|
||||||
|
|
||||||
|
[notify.xmpp]
|
||||||
|
jid = "user@example.org"
|
||||||
|
password = "password"
|
||||||
|
|
||||||
|
[notify.websocket]
|
||||||
|
address = ":8080"
|
||||||
|
webroot = "./webroot/"
|
||||||
|
|
|
@ -9,9 +9,12 @@ import (
|
||||||
"dev.sum7.eu/genofire/logmania/notify"
|
"dev.sum7.eu/genofire/logmania/notify"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var logger = log.WithField("notify", "all")
|
||||||
|
|
||||||
type Notifier struct {
|
type Notifier struct {
|
||||||
notify.Notifier
|
notify.Notifier
|
||||||
list []notify.Notifier
|
list []notify.Notifier
|
||||||
|
db *database.DB
|
||||||
channelNotify chan *log.Entry
|
channelNotify chan *log.Entry
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,6 +30,7 @@ func Init(config *lib.NotifyConfig, db *database.DB, bot *bot.Bot) notify.Notifi
|
||||||
}
|
}
|
||||||
|
|
||||||
n := &Notifier{
|
n := &Notifier{
|
||||||
|
db: db,
|
||||||
list: list,
|
list: list,
|
||||||
channelNotify: make(chan *log.Entry),
|
channelNotify: make(chan *log.Entry),
|
||||||
}
|
}
|
||||||
|
@ -36,15 +40,25 @@ func Init(config *lib.NotifyConfig, db *database.DB, bot *bot.Bot) notify.Notifi
|
||||||
|
|
||||||
func (n *Notifier) sender() {
|
func (n *Notifier) sender() {
|
||||||
for c := range n.channelNotify {
|
for c := range n.channelNotify {
|
||||||
for _, item := range n.list {
|
e, _, tos := n.db.SendTo(c)
|
||||||
item.Send(c)
|
for _, to := range tos {
|
||||||
|
send := false
|
||||||
|
for _, item := range n.list {
|
||||||
|
send = item.Send(e, to)
|
||||||
|
if send {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !send {
|
||||||
|
logger.Warn("notify not send to anybody: [%s] %s", c.Level.String(), c.Message)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Notifier) Send(e *log.Entry) error {
|
func (n *Notifier) Send(e *log.Entry, to *database.Notify) bool {
|
||||||
n.channelNotify <- e
|
n.channelNotify <- e
|
||||||
return nil
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Notifier) Close() {
|
func (n *Notifier) Close() {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package all
|
package all
|
||||||
|
|
||||||
import (
|
import (
|
||||||
_ "dev.sum7.eu/genofire/logmania/notify/file"
|
_ "dev.sum7.eu/genofire/logmania/notify/websocket"
|
||||||
_ "dev.sum7.eu/genofire/logmania/notify/xmpp"
|
_ "dev.sum7.eu/genofire/logmania/notify/xmpp"
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,65 +0,0 @@
|
||||||
package xmpp
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
|
|
||||||
"dev.sum7.eu/genofire/logmania/bot"
|
|
||||||
"dev.sum7.eu/genofire/logmania/database"
|
|
||||||
"dev.sum7.eu/genofire/logmania/lib"
|
|
||||||
"dev.sum7.eu/genofire/logmania/notify"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
proto = "file:"
|
|
||||||
)
|
|
||||||
|
|
||||||
var logger = log.WithField("notify", proto)
|
|
||||||
|
|
||||||
type Notifier struct {
|
|
||||||
notify.Notifier
|
|
||||||
formatter log.Formatter
|
|
||||||
file *os.File
|
|
||||||
path string
|
|
||||||
}
|
|
||||||
|
|
||||||
func Init(config *lib.NotifyConfig, db *database.DB, bot *bot.Bot) notify.Notifier {
|
|
||||||
logger.Info("startup")
|
|
||||||
if config.File == "" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
file, err := os.OpenFile(config.File, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
|
|
||||||
if err != nil {
|
|
||||||
logger.Errorf("could not open file: %s", err.Error())
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return &Notifier{
|
|
||||||
formatter: &log.JSONFormatter{},
|
|
||||||
file: file,
|
|
||||||
path: config.File,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *Notifier) Send(e *log.Entry) error {
|
|
||||||
text, err := n.formatter.Format(e)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_, err = n.file.Write(text)
|
|
||||||
if err != nil {
|
|
||||||
logger.Warnf("could not write to logfile: %s - try to reopen it", err.Error())
|
|
||||||
file, err := os.OpenFile(n.path, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
n.file = file
|
|
||||||
_, err = n.file.Write(text)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
notify.AddNotifier(Init)
|
|
||||||
}
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
var NotifyRegister []NotifyInit
|
var NotifyRegister []NotifyInit
|
||||||
|
|
||||||
type Notifier interface {
|
type Notifier interface {
|
||||||
Send(entry *log.Entry) error
|
Send(entry *log.Entry, to *database.Notify) bool
|
||||||
Close()
|
Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
package xmpp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"dev.sum7.eu/genofire/golang-lib/websocket"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
"dev.sum7.eu/genofire/logmania/bot"
|
||||||
|
"dev.sum7.eu/genofire/logmania/database"
|
||||||
|
"dev.sum7.eu/genofire/logmania/lib"
|
||||||
|
"dev.sum7.eu/genofire/logmania/notify"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
proto = "ws"
|
||||||
|
)
|
||||||
|
|
||||||
|
var logger = log.WithField("notify", proto)
|
||||||
|
|
||||||
|
type Notifier struct {
|
||||||
|
notify.Notifier
|
||||||
|
ws *websocket.Server
|
||||||
|
formatter log.Formatter
|
||||||
|
}
|
||||||
|
|
||||||
|
func Init(config *lib.NotifyConfig, db *database.DB, bot *bot.Bot) notify.Notifier {
|
||||||
|
inputMSG := make(chan *websocket.Message)
|
||||||
|
ws := websocket.NewServer(inputMSG, nil)
|
||||||
|
|
||||||
|
http.HandleFunc("/ws", ws.Handler)
|
||||||
|
http.Handle("/", http.FileServer(http.Dir(config.Websocket.Webroot)))
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for msg := range inputMSG {
|
||||||
|
if msg.Subject != "bot" {
|
||||||
|
logger.Warnf("receive unknown websocket message: %s", msg.Subject)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
bot.Handle(func(answer string) {
|
||||||
|
msg.Answer("bot", answer)
|
||||||
|
}, "", msg.Body.(string))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
srv := &http.Server{
|
||||||
|
Addr: config.Websocket.Address,
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
if err := srv.ListenAndServe(); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
logger.Info("startup")
|
||||||
|
return &Notifier{
|
||||||
|
ws: ws,
|
||||||
|
formatter: &log.TextFormatter{
|
||||||
|
DisableTimestamp: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Notifier) Send(e *log.Entry, to *database.Notify) bool {
|
||||||
|
if to.Protocol != proto {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
n.ws.SendAll(&websocket.Message{
|
||||||
|
Subject: to.Address(),
|
||||||
|
Body: e,
|
||||||
|
})
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Notifier) Close() {
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
notify.AddNotifier(Init)
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
package xmpp
|
package xmpp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"strings"
|
||||||
|
|
||||||
xmpp_client "dev.sum7.eu/genofire/yaja/client"
|
xmpp_client "dev.sum7.eu/genofire/yaja/client"
|
||||||
xmpp "dev.sum7.eu/genofire/yaja/xmpp"
|
xmpp "dev.sum7.eu/genofire/yaja/xmpp"
|
||||||
|
@ -26,7 +26,6 @@ type Notifier struct {
|
||||||
notify.Notifier
|
notify.Notifier
|
||||||
client *xmpp_client.Client
|
client *xmpp_client.Client
|
||||||
channels map[string]bool
|
channels map[string]bool
|
||||||
db *database.DB
|
|
||||||
formatter log.Formatter
|
formatter log.Formatter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,7 +115,7 @@ func Init(config *lib.NotifyConfig, db *database.DB, bot *bot.Bot) notify.Notifi
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
for toAddr, toAddresses := range db.NotifiesByAddress {
|
for toAddr, toAddresses := range db.NotifiesByAddress {
|
||||||
if toAddresses.Protocoll == protoGroup {
|
if toAddresses.Protocol == protoGroup {
|
||||||
toJID := xmppbase.NewJID(toAddresses.To)
|
toJID := xmppbase.NewJID(toAddresses.To)
|
||||||
toJID.Resource = nickname
|
toJID.Resource = nickname
|
||||||
err := client.Send(&xmpp.PresenceClient{
|
err := client.Send(&xmpp.PresenceClient{
|
||||||
|
@ -133,56 +132,54 @@ func Init(config *lib.NotifyConfig, db *database.DB, bot *bot.Bot) notify.Notifi
|
||||||
return &Notifier{
|
return &Notifier{
|
||||||
channels: channels,
|
channels: channels,
|
||||||
client: client,
|
client: client,
|
||||||
db: db,
|
|
||||||
formatter: &log.TextFormatter{
|
formatter: &log.TextFormatter{
|
||||||
DisableTimestamp: true,
|
DisableTimestamp: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Notifier) Send(e *log.Entry) error {
|
func (n *Notifier) Send(e *log.Entry, to *database.Notify) bool {
|
||||||
e, _, tos := n.db.SendTo(e)
|
textByte, err := n.formatter.Format(e)
|
||||||
if tos == nil || len(tos) <= 0 {
|
|
||||||
return errors.New("no receiver found")
|
|
||||||
}
|
|
||||||
text, err := n.formatter.Format(e)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
logger.Error("during format notify", err)
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
for _, to := range tos {
|
text := strings.TrimRight(to.RegexReplace(string(textByte)), "\n")
|
||||||
if to.Protocoll == protoGroup {
|
if to.Protocol == protoGroup {
|
||||||
if _, ok := n.channels[to.To]; ok {
|
if _, ok := n.channels[to.To]; ok {
|
||||||
toJID := xmppbase.NewJID(to.To)
|
toJID := xmppbase.NewJID(to.To)
|
||||||
toJID.Resource = nickname
|
toJID.Resource = nickname
|
||||||
err := n.client.Send(&xmpp.PresenceClient{
|
err := n.client.Send(&xmpp.PresenceClient{
|
||||||
To: toJID,
|
To: toJID,
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
logger.Error("xmpp could not join ", toJID.String(), " error:", err)
|
|
||||||
} else {
|
|
||||||
n.channels[to.To] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
err := n.client.Send(&xmpp.MessageClient{
|
|
||||||
Type: xmpp.MessageTypeGroupchat,
|
|
||||||
To: xmppbase.NewJID(to.To),
|
|
||||||
Body: string(text),
|
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("xmpp to ", to.To, " error:", err)
|
logger.Error("xmpp could not join ", toJID.String(), " error:", err)
|
||||||
}
|
} else {
|
||||||
} else {
|
n.channels[to.To] = true
|
||||||
err := n.client.Send(&xmpp.MessageClient{
|
|
||||||
Type: xmpp.MessageTypeChat,
|
|
||||||
To: xmppbase.NewJID(to.To),
|
|
||||||
Body: string(text),
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
logger.Error("xmpp to ", to, " error:", err)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
err := n.client.Send(&xmpp.MessageClient{
|
||||||
|
Type: xmpp.MessageTypeGroupchat,
|
||||||
|
To: xmppbase.NewJID(to.To),
|
||||||
|
Body: text,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("xmpp to ", to.To, " error:", err)
|
||||||
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
return nil
|
if to.Protocol == proto {
|
||||||
|
err := n.client.Send(&xmpp.MessageClient{
|
||||||
|
Type: xmpp.MessageTypeChat,
|
||||||
|
To: xmppbase.NewJID(to.To),
|
||||||
|
Body: text,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("xmpp to ", to, " error:", err)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Notifier) Close() {
|
func (n *Notifier) Close() {
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"presets": ["env"]
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
{
|
||||||
|
extends: eslint:all,
|
||||||
|
rules: {
|
||||||
|
no-tabs: [off],
|
||||||
|
indent: [error, tab],
|
||||||
|
quotes: [error, single],
|
||||||
|
padded-blocks: [error, { blocks: never }],
|
||||||
|
no-console: [error, { allow: [log, warn, error] }],
|
||||||
|
func-style: [error, declaration],
|
||||||
|
object-curly-newline: off,
|
||||||
|
wrap-iife: [error, inside],
|
||||||
|
object-shorthand: ["error", "always", { "avoidQuotes": true }],
|
||||||
|
require-jsdoc: [off],
|
||||||
|
max-statements: [off],
|
||||||
|
no-magic-numbers: ["error", { "ignore": [0,1,-1] }],
|
||||||
|
sort-vars: [off],
|
||||||
|
max-len: [off],
|
||||||
|
id-length: [error, { exceptions: ["i"] }],
|
||||||
|
no-ternary: [off]
|
||||||
|
|
||||||
|
},
|
||||||
|
parserOptions: {
|
||||||
|
sourceType: module
|
||||||
|
},
|
||||||
|
env: {
|
||||||
|
es6: true,
|
||||||
|
},
|
||||||
|
globals: {
|
||||||
|
document: true,
|
||||||
|
window: true,
|
||||||
|
console: true,
|
||||||
|
localStorage: true,
|
||||||
|
location: true,
|
||||||
|
navigator: true,
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
.notifications {
|
||||||
|
position: fixed;
|
||||||
|
top: 16px;
|
||||||
|
right: 72px;
|
||||||
|
z-index: 105;
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
.status {
|
||||||
|
margin: 0 auto;
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
background-color: #80FF00;
|
||||||
|
border-radius: 50%;
|
||||||
|
box-shadow: #000 0 -1px 6px 1px, inset #460 0 -1px 8px, #80FF00 0 3px 11px;
|
||||||
|
|
||||||
|
&.connecting,
|
||||||
|
&.running {
|
||||||
|
background-color: #FF0;
|
||||||
|
box-shadow: #000 0 -1px 6px 1px, inset #660 0 -1px 8px, #FF0 0 3px 11px;
|
||||||
|
animation: blinkDot 2s infinite;
|
||||||
|
}
|
||||||
|
&.offline,
|
||||||
|
&.failed {
|
||||||
|
background-color: #F00;
|
||||||
|
box-shadow: #000 0 -1px 6px 1px, inset #600 0 -1px 8px, #F00 0 3px 11px;
|
||||||
|
animation: blinkDot 1s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@keyframes blinkDot {
|
||||||
|
50% {
|
||||||
|
background-color: rgba(255, 255, 255, 0.25);
|
||||||
|
box-shadow: #000 0 -1px 6px 1px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
@import "../node_modules/semantic-ui-less/semantic.less";
|
||||||
|
|
||||||
|
@import "_status.less";
|
||||||
|
@import "_notify.less";
|
||||||
|
|
||||||
|
body {
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main.container {
|
||||||
|
margin-top: 7em;
|
||||||
|
}
|
|
@ -0,0 +1,92 @@
|
||||||
|
import browserSync from 'browser-sync';
|
||||||
|
import browserify from 'browserify';
|
||||||
|
import buffer from 'vinyl-buffer';
|
||||||
|
import gulp from 'gulp';
|
||||||
|
import gulpLoadPlugins from 'gulp-load-plugins';
|
||||||
|
import source from 'vinyl-source-stream';
|
||||||
|
import sourcemaps from 'gulp-sourcemaps';
|
||||||
|
import watchify from 'watchify';
|
||||||
|
|
||||||
|
const gulpPlugins = gulpLoadPlugins();
|
||||||
|
|
||||||
|
function bundle (watching = false) {
|
||||||
|
const browserifyConf = {
|
||||||
|
'debug': true,
|
||||||
|
'entries': ['js/index.js'],
|
||||||
|
'transform': ['babelify']};
|
||||||
|
|
||||||
|
if (watching) {
|
||||||
|
browserifyConf.plugin = [watchify];
|
||||||
|
}
|
||||||
|
|
||||||
|
const browser = browserify(browserifyConf);
|
||||||
|
|
||||||
|
function bundler () {
|
||||||
|
return browser.bundle().
|
||||||
|
on('error', (err) => {
|
||||||
|
console.log(err.message);
|
||||||
|
}).
|
||||||
|
pipe(source('app.js')).
|
||||||
|
pipe(buffer()).
|
||||||
|
pipe(sourcemaps.init({'loadMaps': true})).
|
||||||
|
pipe(gulpPlugins.uglify()).
|
||||||
|
pipe(sourcemaps.write('./')).
|
||||||
|
pipe(gulp.dest('./'));
|
||||||
|
}
|
||||||
|
|
||||||
|
browser.on('update', () => {
|
||||||
|
bundler();
|
||||||
|
console.log('scripts rebuild');
|
||||||
|
});
|
||||||
|
|
||||||
|
return bundler();
|
||||||
|
}
|
||||||
|
|
||||||
|
gulp.task('scripts', () => {
|
||||||
|
bundle();
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task('styles', () => {
|
||||||
|
gulp.src('css/styles.less').
|
||||||
|
pipe(gulpPlugins.plumber()).
|
||||||
|
pipe(sourcemaps.init()).
|
||||||
|
pipe(gulpPlugins.less({
|
||||||
|
'includePaths': ['.']
|
||||||
|
})).
|
||||||
|
pipe(gulpPlugins.autoprefixer()).
|
||||||
|
pipe(sourcemaps.write('./')).
|
||||||
|
pipe(gulp.dest('./'));
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task('build', [
|
||||||
|
'scripts',
|
||||||
|
'styles'
|
||||||
|
]);
|
||||||
|
|
||||||
|
gulp.task('watch', () => {
|
||||||
|
bundle(true);
|
||||||
|
gulp.watch('css/**/*.less', ['styles']);
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task('serve', ['watch'], () => {
|
||||||
|
browserSync({
|
||||||
|
'notify': false,
|
||||||
|
'port': 9000,
|
||||||
|
// Proxy: 'example.com',
|
||||||
|
'server': {
|
||||||
|
'baseDir': '.'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.watch([
|
||||||
|
'**/*.html',
|
||||||
|
'**/*.php',
|
||||||
|
'styles.css',
|
||||||
|
'app.js'
|
||||||
|
]).on('change', browserSync.reload);
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task('default', [
|
||||||
|
'build',
|
||||||
|
'serve'
|
||||||
|
]);
|
|
@ -0,0 +1,15 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
|
||||||
|
<title>logmania</title>
|
||||||
|
<link rel="stylesheet" href="styles.css">
|
||||||
|
<script src="app.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<noscript>
|
||||||
|
<strong>JavaScript required</strong>
|
||||||
|
</noscript>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,9 @@
|
||||||
|
/* eslint no-magic-numbers: "off"*/
|
||||||
|
/* eslint sort-keys: "off"*/
|
||||||
|
|
||||||
|
export default {
|
||||||
|
'title': 'Logmania',
|
||||||
|
'backend': 'wss://cetus.h.sum7.eu/logmania/ws'
|
||||||
|
|
||||||
|
// 'backend': `ws${location.protocol === 'https:' ? 's' :''}://${location.host}${location.pathname.replace(/\/$/, '')}/ws`
|
||||||
|
};
|
|
@ -0,0 +1,54 @@
|
||||||
|
export function setProps (el, props) {
|
||||||
|
if (props) {
|
||||||
|
if (props.class) {
|
||||||
|
let classList = props.class;
|
||||||
|
if (typeof props.class === 'string') {
|
||||||
|
classList = classList.split(' ');
|
||||||
|
}
|
||||||
|
el.classList.add(...classList);
|
||||||
|
delete props.class;
|
||||||
|
}
|
||||||
|
Object.keys(props).map((key) => {
|
||||||
|
if (key.indexOf('on') === 0 && typeof props[key] === 'function') {
|
||||||
|
// eslint-disable-next-line no-magic-numbers
|
||||||
|
return el.addEventListener(key.slice(2), props[key]);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
Object.keys(props).map((key) => el.setAttribute(key, props[key]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function newEl (eltype, props, content) {
|
||||||
|
const el = document.createElement(eltype);
|
||||||
|
setProps(el, props);
|
||||||
|
if (content) {
|
||||||
|
el.innerHTML = content;
|
||||||
|
}
|
||||||
|
|
||||||
|
return el;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function appendChild (el, child) {
|
||||||
|
if (child && !child.parentNode) {
|
||||||
|
el.appendChild(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function removeChild (el) {
|
||||||
|
if (el && el.parentNode) {
|
||||||
|
el.parentNode.removeChild(el);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line max-params
|
||||||
|
export function newAt (at, eltype, props, content) {
|
||||||
|
const el = document.createElement(eltype);
|
||||||
|
setProps(el, props);
|
||||||
|
if (content) {
|
||||||
|
el.innerHTML = content;
|
||||||
|
}
|
||||||
|
at.appendChild(el);
|
||||||
|
|
||||||
|
return el;
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
import * as V from 'picodom';
|
||||||
|
import * as domlib from '../domlib';
|
||||||
|
import * as socket from '../socket';
|
||||||
|
import * as store from '../store';
|
||||||
|
import View from '../view';
|
||||||
|
import {singelton as notify} from './notify';
|
||||||
|
import {render} from '../gui';
|
||||||
|
|
||||||
|
export class MenuView extends View {
|
||||||
|
constructor () {
|
||||||
|
super();
|
||||||
|
this.elStatus = document.createElement('div');
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const socketStatus = socket.getStatus();
|
||||||
|
let statusClass = 'status ',
|
||||||
|
vLogin = V.h('a', {
|
||||||
|
'class': 'item',
|
||||||
|
'href': '#/log'
|
||||||
|
}, 'Logs');
|
||||||
|
if (socketStatus !== 1) {
|
||||||
|
// eslint-disable-next-line no-magic-numbers
|
||||||
|
if (socketStatus === 0 || socketStatus === 2) {
|
||||||
|
statusClass += 'connecting';
|
||||||
|
} else {
|
||||||
|
statusClass += 'offline';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (store.isLogin) {
|
||||||
|
vLogin = V.h('a', {
|
||||||
|
'class': 'item',
|
||||||
|
'href': '#/',
|
||||||
|
'onclick': () => socket.sendjson({'subject': 'logout'}, (msg) => {
|
||||||
|
if (msg.body) {
|
||||||
|
store.isLogin = false;
|
||||||
|
store.login = {};
|
||||||
|
render();
|
||||||
|
} else {
|
||||||
|
notify.send({
|
||||||
|
'header': 'Abmeldung ist fehlgeschlagen',
|
||||||
|
'type': 'error'
|
||||||
|
}, 'Logout');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}, 'Logout');
|
||||||
|
}
|
||||||
|
|
||||||
|
V.patch(this.vStatus, this.vStatus = V.h('div', {'class': statusClass}), this.elStatus);
|
||||||
|
|
||||||
|
|
||||||
|
if (!this.init) {
|
||||||
|
domlib.setProps(this.el, {'class': 'ui fixed inverted menu'});
|
||||||
|
const menuContainer = domlib.newAt(this.el, 'div', {'class': 'ui container'});
|
||||||
|
this.menuRight = domlib.newAt(menuContainer, 'div', {'class': 'menu right'});
|
||||||
|
this.elStatus.classList.add('item');
|
||||||
|
this.menuRight.appendChild(this.elStatus);
|
||||||
|
this.init = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
V.patch(this.vLogin, this.vLogin = vLogin, this.menuRight);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,94 @@
|
||||||
|
import * as V from 'picodom';
|
||||||
|
import View from '../view';
|
||||||
|
|
||||||
|
|
||||||
|
const DELAY_OF_NOTIFY = 15000,
|
||||||
|
MAX_MESSAGE_SHOW = 5;
|
||||||
|
|
||||||
|
class NotifyView extends View {
|
||||||
|
constructor () {
|
||||||
|
super();
|
||||||
|
if ('Notification' in window) {
|
||||||
|
window.Notification.requestPermission();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.messages = [];
|
||||||
|
window.setInterval(this.removeLast.bind(this), DELAY_OF_NOTIFY);
|
||||||
|
}
|
||||||
|
|
||||||
|
removeLast () {
|
||||||
|
this.messages.splice(0, 1);
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
renderMSG (msg) {
|
||||||
|
const {messages} = this,
|
||||||
|
content = [msg.content];
|
||||||
|
|
||||||
|
let {render} = this;
|
||||||
|
render = render.bind(this);
|
||||||
|
|
||||||
|
if (msg.header) {
|
||||||
|
content.unshift(V.h('div', {'class': 'header'}, msg.header));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return V.h(
|
||||||
|
'div', {
|
||||||
|
'class': `ui floating message ${msg.type}`
|
||||||
|
},
|
||||||
|
V.h('i', {
|
||||||
|
'class': 'close icon',
|
||||||
|
'onclick': () => {
|
||||||
|
const index = messages.indexOf(msg);
|
||||||
|
if (index !== -1) {
|
||||||
|
messages.splice(index, 1);
|
||||||
|
render();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}), V.h('div', {'class': 'content'}, content)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
send (props, content) {
|
||||||
|
let msg = props;
|
||||||
|
if (typeof props === 'object') {
|
||||||
|
msg.content = content;
|
||||||
|
} else {
|
||||||
|
msg = {
|
||||||
|
'content': content,
|
||||||
|
'type': props
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if ('Notification' in window &&
|
||||||
|
window.Notification.permission === 'granted') {
|
||||||
|
let body = msg.type,
|
||||||
|
title = content;
|
||||||
|
if (msg.header) {
|
||||||
|
title = msg.header;
|
||||||
|
body = msg.content;
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-new
|
||||||
|
new window.Notification(title, {
|
||||||
|
'body': body,
|
||||||
|
'icon': '/img/logo.jpg'
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.messages.length > MAX_MESSAGE_SHOW) {
|
||||||
|
this.removeLast();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.messages.push(msg);
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
V.patch(this.vel, this.vel = V.h('div', {'class': 'notifications'}, this.messages.map(this.renderMSG.bind(this))), this.el);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line one-var
|
||||||
|
const singelton = new NotifyView();
|
||||||
|
export {singelton, NotifyView};
|
|
@ -0,0 +1,43 @@
|
||||||
|
import * as domlib from './domlib';
|
||||||
|
import {MenuView} from './element/menu';
|
||||||
|
import Navigo from '../node_modules/navigo/lib/navigo';
|
||||||
|
import View from './view';
|
||||||
|
import {singelton as notify} from './element/notify';
|
||||||
|
|
||||||
|
const router = new Navigo(null, true, '#'),
|
||||||
|
elMain = domlib.newEl('div', {'class': 'ui main container'}),
|
||||||
|
elMenu = new MenuView();
|
||||||
|
|
||||||
|
export {router};
|
||||||
|
|
||||||
|
let init = false,
|
||||||
|
currentView = new View();
|
||||||
|
|
||||||
|
export function render () {
|
||||||
|
if (!document.body) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!init) {
|
||||||
|
notify.bind(document.body);
|
||||||
|
elMenu.bind(document.body);
|
||||||
|
|
||||||
|
document.body.appendChild(elMain);
|
||||||
|
|
||||||
|
init = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentView.render();
|
||||||
|
|
||||||
|
notify.render();
|
||||||
|
elMenu.render();
|
||||||
|
|
||||||
|
router.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setView (toView) {
|
||||||
|
currentView.unbind();
|
||||||
|
currentView = toView;
|
||||||
|
currentView.bind(elMain);
|
||||||
|
currentView.render();
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
import * as gui from './gui';
|
||||||
|
import config from './config';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Self binding with router
|
||||||
|
*/
|
||||||
|
/* eslint-disable no-unused-vars */
|
||||||
|
import home from './view/home';
|
||||||
|
import login from './view/log';
|
||||||
|
/* eslint-enable no-unused-vars */
|
||||||
|
|
||||||
|
|
||||||
|
document.title = config.title;
|
||||||
|
window.onload = () => gui.render();
|
|
@ -0,0 +1,163 @@
|
||||||
|
import config from './config';
|
||||||
|
import {singelton as notify} from './element/notify';
|
||||||
|
import {render} from './gui';
|
||||||
|
|
||||||
|
const RECONNECT_AFTER = 5000,
|
||||||
|
RETRY_QUERY = 300,
|
||||||
|
PREFIX_EVENT = true,
|
||||||
|
query = [],
|
||||||
|
eventMSGID = {},
|
||||||
|
eventTo = {};
|
||||||
|
|
||||||
|
let socket = null;
|
||||||
|
|
||||||
|
function newUUID () {
|
||||||
|
/* eslint-disable */
|
||||||
|
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
||||||
|
const r = Math.random() * 16 | 0,
|
||||||
|
v = c === 'x' ? r : r & 0x3 | 0x8;
|
||||||
|
return v.toString(16);
|
||||||
|
});
|
||||||
|
/* eslint-enable */
|
||||||
|
}
|
||||||
|
|
||||||
|
function correctMSG (obj) {
|
||||||
|
if (!obj.id) {
|
||||||
|
obj.id = newUUID();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onerror (err) {
|
||||||
|
console.warn(err);
|
||||||
|
// eslint-disable-next-line no-magic-numbers
|
||||||
|
if (socket.readyState !== 3) {
|
||||||
|
notify.send({
|
||||||
|
'header': 'Verbindung',
|
||||||
|
'type': 'error'
|
||||||
|
}, 'Verbindung zum Server unterbrochen!');
|
||||||
|
}
|
||||||
|
render();
|
||||||
|
socket.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onopen () {
|
||||||
|
render();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function sendjson (obj, callback) {
|
||||||
|
if (socket.readyState !== 1) {
|
||||||
|
query.push({
|
||||||
|
'callback': callback,
|
||||||
|
'obj': obj
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
correctMSG(obj);
|
||||||
|
const socketMSG = JSON.stringify(obj);
|
||||||
|
socket.send(socketMSG);
|
||||||
|
if (typeof callback === 'function') {
|
||||||
|
eventMSGID[obj.id] = callback;
|
||||||
|
console.log('callback bind', obj.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onmessage (raw) {
|
||||||
|
const msg = JSON.parse(raw.data),
|
||||||
|
msgFunc = eventMSGID[msg.id];
|
||||||
|
let eventFuncs = eventTo[msg.subject];
|
||||||
|
|
||||||
|
if (msgFunc) {
|
||||||
|
msgFunc(msg);
|
||||||
|
delete eventMSGID[msg.id];
|
||||||
|
render();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof eventFuncs === 'object' && eventFuncs.length > 0) {
|
||||||
|
// eslint-disable-next-line guard-for-in
|
||||||
|
for (const i in eventFuncs) {
|
||||||
|
const func = eventFuncs[i];
|
||||||
|
if (func) {
|
||||||
|
func(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
render();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (PREFIX_EVENT) {
|
||||||
|
for (const key in eventTo) {
|
||||||
|
if (msg.subject.indexOf(key) === 0) {
|
||||||
|
eventFuncs = eventTo[key];
|
||||||
|
// eslint-disable-next-line guard-for-in
|
||||||
|
for (const i in eventFuncs) {
|
||||||
|
const func = eventFuncs[i];
|
||||||
|
if (func) {
|
||||||
|
func(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
render();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
notify.send('warning', `unable to identify message: ${raw.data}`);
|
||||||
|
render();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onclose () {
|
||||||
|
console.log('socket closed by server');
|
||||||
|
notify.send({
|
||||||
|
'header': 'Verbindung',
|
||||||
|
'type': 'warning'
|
||||||
|
}, 'Verbindung zum Server beendet!');
|
||||||
|
render();
|
||||||
|
// eslint-disable-next-line no-use-before-define
|
||||||
|
window.setTimeout(connect, RECONNECT_AFTER);
|
||||||
|
}
|
||||||
|
|
||||||
|
function connect () {
|
||||||
|
socket = new window.WebSocket(config.backend);
|
||||||
|
socket.onopen = onopen;
|
||||||
|
socket.onerror = onerror;
|
||||||
|
socket.onmessage = onmessage;
|
||||||
|
socket.onclose = onclose;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.setInterval(() => {
|
||||||
|
const queryEntry = query.pop();
|
||||||
|
if (queryEntry) {
|
||||||
|
sendjson(queryEntry.obj, queryEntry.callback);
|
||||||
|
}
|
||||||
|
console.log('query length: ', query.length);
|
||||||
|
}, RETRY_QUERY);
|
||||||
|
|
||||||
|
|
||||||
|
export function getStatus () {
|
||||||
|
if (socket) {
|
||||||
|
return socket.readyState;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setEvent (to, func) {
|
||||||
|
eventTo[to] = [func];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addEvent (to, func) {
|
||||||
|
if (typeof eventTo[to] !== 'object') {
|
||||||
|
eventTo[to] = [];
|
||||||
|
}
|
||||||
|
eventTo[to].push(func);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function delEvent (to, func) {
|
||||||
|
if (typeof eventTo[to] === 'object' && eventTo[to].length > 1) {
|
||||||
|
eventTo[to].pop(func);
|
||||||
|
} else {
|
||||||
|
eventTo[to] = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
connect();
|
|
@ -0,0 +1,6 @@
|
||||||
|
const store = {
|
||||||
|
'bot': [],
|
||||||
|
'channel': {}
|
||||||
|
};
|
||||||
|
|
||||||
|
export {store};
|
|
@ -0,0 +1,22 @@
|
||||||
|
export default class View {
|
||||||
|
constructor () {
|
||||||
|
this.el = document.createElement('div');
|
||||||
|
}
|
||||||
|
|
||||||
|
unbind () {
|
||||||
|
if (this.el && this.el.parentNode) {
|
||||||
|
this.el.parentNode.removeChild(this.el);
|
||||||
|
} else {
|
||||||
|
console.warn('unbind view not possible');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bind (el) {
|
||||||
|
el.appendChild(this.el);
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line class-methods-use-this
|
||||||
|
render () {
|
||||||
|
console.log('abstract view');
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
import * as dom from '../domlib';
|
||||||
|
import * as gui from '../gui';
|
||||||
|
import View from '../view';
|
||||||
|
|
||||||
|
class HomeView extends View {
|
||||||
|
// eslint-disable-next-line class-methods-use-this
|
||||||
|
render () {
|
||||||
|
if (!this.init) {
|
||||||
|
const h1 = dom.newAt(this.el, 'h1');
|
||||||
|
h1.innerHTML = 'Home';
|
||||||
|
this.init = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const homeView = new HomeView();
|
||||||
|
|
||||||
|
gui.router.on('/', () => {
|
||||||
|
gui.setView(homeView);
|
||||||
|
});
|
|
@ -0,0 +1,57 @@
|
||||||
|
import * as domlib from '../domlib';
|
||||||
|
import * as gui from '../gui';
|
||||||
|
import * as socket from '../socket';
|
||||||
|
import View from '../view';
|
||||||
|
import {store} from '../store';
|
||||||
|
|
||||||
|
|
||||||
|
function levelToColor (lvl) {
|
||||||
|
return lvl;
|
||||||
|
}
|
||||||
|
|
||||||
|
function addItem (el, msg) {
|
||||||
|
const div = domlib.newAt(el, 'div', {
|
||||||
|
'class': levelToColor(msg.Leve)
|
||||||
|
});
|
||||||
|
domlib.newAt(div, 'span', null, msg.Data.hostname);
|
||||||
|
domlib.newAt(div, 'span', null, msg.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
class LogView extends View {
|
||||||
|
|
||||||
|
// eslint-disable-next-line class-methods-use-this
|
||||||
|
render () {
|
||||||
|
if (!this.init) {
|
||||||
|
this.init = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Domlib.newAt(this.el, 'h2', {'class': 'ui header'}, 'Log');
|
||||||
|
* for (const msg in store.channel.ffhb) {
|
||||||
|
* domlib.newAt(this.el, 'div', null, msg.Data.hostname, msg);
|
||||||
|
*}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
constructor () {
|
||||||
|
super();
|
||||||
|
socket.addEvent('ws:', (msg) => {
|
||||||
|
// Length('ws:') = 3
|
||||||
|
// eslint-disable-next-line no-magic-numbers
|
||||||
|
const channel = msg.subject.substr(3);
|
||||||
|
if (!store.channel[channel]) {
|
||||||
|
store.channel[channel] = [];
|
||||||
|
}
|
||||||
|
store.channel[channel].push(msg.body);
|
||||||
|
addItem(this.el, msg.body);
|
||||||
|
this.render();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const logView = new LogView();
|
||||||
|
|
||||||
|
gui.router.on('/log', () => {
|
||||||
|
gui.setView(logView);
|
||||||
|
});
|
|
@ -0,0 +1,37 @@
|
||||||
|
{
|
||||||
|
"name": "logmania",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"main": "index.js",
|
||||||
|
"license": "GPL-3.0",
|
||||||
|
"dependencies": {
|
||||||
|
"babel-core": "^6.26.0",
|
||||||
|
"babel-preset-env": "^1.6.1",
|
||||||
|
"babel-preset-es2017": "^6.24.1",
|
||||||
|
"babel-register": "^6.26.0",
|
||||||
|
"babelify": "^8.0.0",
|
||||||
|
"browser-sync": "^2.18.13",
|
||||||
|
"browserify": "^14.5.0",
|
||||||
|
"gulp": "3.9.0",
|
||||||
|
"gulp-autoprefixer": "^4.0.0",
|
||||||
|
"gulp-cli": "^1.4.0",
|
||||||
|
"gulp-less": "^3.3.2",
|
||||||
|
"gulp-load-plugins": "^1.5.0",
|
||||||
|
"gulp-plumber": "^1.1.0",
|
||||||
|
"gulp-sourcemaps": "^2.6.1",
|
||||||
|
"gulp-uglify": "^3.0.0",
|
||||||
|
"navigo": "^5.3.3",
|
||||||
|
"picodom": "^1.0.2",
|
||||||
|
"semantic-ui-less": "^2.2.12",
|
||||||
|
"vinyl-buffer": "^1.0.0",
|
||||||
|
"vinyl-source-stream": "^1.1.0",
|
||||||
|
"watchify": "^3.9.0"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"gulp": "gulp"
|
||||||
|
},
|
||||||
|
"babel": {
|
||||||
|
"presets": [
|
||||||
|
"es2017"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue