<commit_before>// Copyright 2013 The StudyGolang Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// http://studygolang.com
// Author：polaris	studygolang@gmail.com

package service

import (
	"bytes"
	"html/template"
	"net/smtp"
	"strings"
	"time"

	. "config"
	"logger"
	"model"
	"util"
)

// 发送电子邮件功能
func SendMail(subject, content string, tos []string) error {
	message := `From: Go语言中文网 | Golang中文社区 | Go语言学习园地<` + Config["from_email"] + `>
To: ` + strings.Join(tos, ",") + `
Subject: ` + subject + `
Content-Type: text/html;charset=UTF-8

` + content

	auth := smtp.PlainAuth("", Config["smtp_username"], Config["smtp_password"], Config["smtp_host"])
	err := smtp.SendMail(Config["smtp_addr"], auth, Config["from_email"], tos, []byte(message))
	if err != nil {
		logger.Errorln("Send Mail to", strings.Join(tos, ","), "error:", err)
		return err
	}
	logger.Infoln("Send Mail to", strings.Join(tos, ","), "Successfully")
	return nil
}

// 自定义模板函数
var emailFuncMap = template.FuncMap{
	"time_format": func(s string) string {
		t, err := time.ParseInLocation("2006-01-02 15:04:05", s, time.Local)
		if err != nil {
			return s
		}

		return t.Format("01-02")
	},
	"substring": util.Substring,
}

var emailTpl = template.Must(template.New("email.html").Funcs(emailFuncMap).ParseFiles(ROOT + "/template/email.html"))

// 订阅邮件通知
func EmailNotice() {

	beginDate := time.Now().Add(-7 * 24 * time.Hour).Format("2006-01-02")
	endDate := time.Now().Add(-24 * time.Hour).Format("2006-01-02")

	beginTime := beginDate + " 00:00:00"

	// 本周晨读（过去 7 天）
	readings, err := model.NewMorningReading().Where("ctime>? AND rtype=0", beginTime).Order("id DESC").FindAll()
	if err != nil {
		logger.Errorln("find morning reading error:", err)
	}

	// 本周精彩文章
	articles, err := model.NewArticle().Where("ctime>? AND status!=2", beginTime).Order("cmtnum DESC, likenum DESC, viewnum DESC").Limit("10").FindAll()
	if err != nil {
		logger.Errorln("find article error:", err)
	}

	// 本周热门主题
	topics, err := model.NewTopic().Where("ctime>? AND flag IN(0,1)", beginTime).Order("tid DESC").Limit("10").FindAll()
	if err != nil {
		logger.Errorln("find topic error:", err)
	}

	data := map[string]interface{}{
		"readings":  readings,
		"articles":  articles,
		"topics":    topics,
		"beginDate": beginDate,
		"endDate":   endDate,
	}

	// 给所有用户发送邮件
	userModel := model.NewUser()

	var (
		lastUid = 0
		limit   = "500"
		users   []*model.User
	)

	for {
		users, err = userModel.Where("uid>?", lastUid).Order("uid ASC").Limit(limit).FindAll()
		if err != nil {
			logger.Errorln("find user error:", err)
			continue
		}

		if len(users) == 0 {
			break
		}

		for _, user := range users {
			if user.Unsubscribe == 1 {
				logger.Infoln(user)
				continue
			}

			data["email"] = user.Email
			data["token"] = GenUnsubscribeToken(user)

			content, err := genEmailContent(data)
			if err != nil {
				logger.Errorln("from email.html gen email content error:", err)
				continue
			}

			SendMail("每周精选", content, []string{user.Email})

			if lastUid < user.Uid {
				lastUid = user.Uid
			}
		}
	}

}

// 生成 退订 邮件的 token
func GenUnsubscribeToken(user *model.User) string {
	return util.Md5(user.String() + Config["unsubscribe_token_key"])
}

func genEmailContent(data map[string]interface{}) (string, error) {
	buffer := &bytes.Buffer{}
	if err := emailTpl.Execute(buffer, data); err != nil {
		logger.Errorln("execute template error:", err)
		return "", err
	}

	return buffer.String(), nil
}
<commit_msg>修改log<commit_after>// Copyright 2013 The StudyGolang Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// http://studygolang.com
// Author：polaris	studygolang@gmail.com

package service

import (
	"bytes"
	"html/template"
	"net/smtp"
	"strings"
	"time"

	. "config"
	"logger"
	"model"
	"util"
)

// 发送电子邮件功能
func SendMail(subject, content string, tos []string) error {
	message := `From: Go语言中文网 | Golang中文社区 | Go语言学习园地<` + Config["from_email"] + `>
To: ` + strings.Join(tos, ",") + `
Subject: ` + subject + `
Content-Type: text/html;charset=UTF-8

` + content

	auth := smtp.PlainAuth("", Config["smtp_username"], Config["smtp_password"], Config["smtp_host"])
	err := smtp.SendMail(Config["smtp_addr"], auth, Config["from_email"], tos, []byte(message))
	if err != nil {
		logger.Errorln("Send Mail to", strings.Join(tos, ","), "error:", err)
		return err
	}
	logger.Infoln("Send Mail to", strings.Join(tos, ","), "Successfully")
	return nil
}

// 自定义模板函数
var emailFuncMap = template.FuncMap{
	"time_format": func(s string) string {
		t, err := time.ParseInLocation("2006-01-02 15:04:05", s, time.Local)
		if err != nil {
			return s
		}

		return t.Format("01-02")
	},
	"substring": util.Substring,
}

var emailTpl = template.Must(template.New("email.html").Funcs(emailFuncMap).ParseFiles(ROOT + "/template/email.html"))

// 订阅邮件通知
func EmailNotice() {

	beginDate := time.Now().Add(-7 * 24 * time.Hour).Format("2006-01-02")
	endDate := time.Now().Add(-24 * time.Hour).Format("2006-01-02")

	beginTime := beginDate + " 00:00:00"

	// 本周晨读（过去 7 天）
	readings, err := model.NewMorningReading().Where("ctime>? AND rtype=0", beginTime).Order("id DESC").FindAll()
	if err != nil {
		logger.Errorln("find morning reading error:", err)
	}

	// 本周精彩文章
	articles, err := model.NewArticle().Where("ctime>? AND status!=2", beginTime).Order("cmtnum DESC, likenum DESC, viewnum DESC").Limit("10").FindAll()
	if err != nil {
		logger.Errorln("find article error:", err)
	}

	// 本周热门主题
	topics, err := model.NewTopic().Where("ctime>? AND flag IN(0,1)", beginTime).Order("tid DESC").Limit("10").FindAll()
	if err != nil {
		logger.Errorln("find topic error:", err)
	}

	data := map[string]interface{}{
		"readings":  readings,
		"articles":  articles,
		"topics":    topics,
		"beginDate": beginDate,
		"endDate":   endDate,
	}

	// 给所有用户发送邮件
	userModel := model.NewUser()

	var (
		lastUid = 0
		limit   = "500"
		users   []*model.User
	)

	for {
		users, err = userModel.Where("uid>?", lastUid).Order("uid ASC").Limit(limit).FindAll()
		if err != nil {
			logger.Errorln("find user error:", err)
			continue
		}

		if len(users) == 0 {
			break
		}

		for _, user := range users {
			if user.Unsubscribe == 1 {
				logger.Infoln("user unsubscribe", user)
				continue
			}

			data["email"] = user.Email
			data["token"] = GenUnsubscribeToken(user)

			content, err := genEmailContent(data)
			if err != nil {
				logger.Errorln("from email.html gen email content error:", err)
				continue
			}

			SendMail("每周精选", content, []string{user.Email})

			if lastUid < user.Uid {
				lastUid = user.Uid
			}
		}
	}

}

// 生成 退订 邮件的 token
func GenUnsubscribeToken(user *model.User) string {
	return util.Md5(user.String() + Config["unsubscribe_token_key"])
}

func genEmailContent(data map[string]interface{}) (string, error) {
	buffer := &bytes.Buffer{}
	if err := emailTpl.Execute(buffer, data); err != nil {
		logger.Errorln("execute template error:", err)
		return "", err
	}

	return buffer.String(), nil
}
<|endoftext|>


<commit_before>// Copyright 2013 The lime Authors.
// Use of this source code is governed by a 2-clause
// BSD-style license that can be found in the LICENSE file.

package sublime

import (
	"bytes"
	"fmt"
	"github.com/limetext/gopy/lib"
	"github.com/limetext/lime/backend"
	_ "github.com/limetext/lime/backend/commands"
	"github.com/limetext/lime/backend/log"
	"github.com/limetext/lime/backend/packages"
	"github.com/limetext/lime/backend/util"
	"github.com/limetext/text"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"testing"
	"time"
)

var dummyClipboard string

type consoleObserver struct {
	T *testing.T
}

func (o *consoleObserver) Erased(changed_buffer text.Buffer, region_removed text.Region, data_removed []rune) {
	// do nothing
}

func (o *consoleObserver) Inserted(changed_buffer text.Buffer, region_inserted text.Region, data_inserted []rune) {
	o.T.Logf("%s", string(data_inserted))
}

func TestSublime(t *testing.T) {
	ed := backend.GetEditor()
	ed.SetClipboardFuncs(func(n string) (err error) {
		dummyClipboard = n
		return nil
	}, func() (string, error) {
		return dummyClipboard, nil
	})
	ed.Init()

	ed.Console().Buffer().AddObserver(&consoleObserver{T: t})
	w := ed.NewWindow()
	l := py.NewLock()
	py.AddToPath("testdata")
	py.AddToPath("testdata/plugins")
	if m, err := py.Import("sublime_plugin"); err != nil {
		t.Fatal(err)
	} else {
		plugins := packages.ScanPlugins("testdata/", ".py")
		for _, p := range plugins {
			newPlugin(p, m)
		}
	}

	subl, err := py.Import("sublime")
	if err != nil {
		t.Fatal(err)
	}

	if w, err := _windowClass.Alloc(1); err != nil {
		t.Fatal(err)
	} else {
		(w.(*Window)).data = &backend.Window{}
		subl.AddObject("test_window", w)
	}

	// Testing plugin reload
	data := []byte(`import sublime, sublime_plugin

class TestToxt(sublime_plugin.TextCommand):
    def run(self, edit):
        print("my view's id is: %d" % self.view.id())
        self.view.insert(edit, 0, "Tada")
		`)
	if err := ioutil.WriteFile("testdata/plugins/reload.py", data, 0644); err != nil {
		t.Fatalf("Couldn't write testdata/plugins/reload.py: %s", err)
	}
	defer os.Remove("testdata/plugins/reload.py")
	time.Sleep(time.Millisecond * 50)

	if dir, err := os.Open("testdata"); err != nil {
		t.Error(err)
	} else if files, err := dir.Readdirnames(0); err != nil {
		t.Error(err)
	} else {
		for _, fn := range files {
			if filepath.Ext(fn) == ".py" {
				log.Debug("Running %s", fn)
				if _, err := py.Import(fn[:len(fn)-3]); err != nil {
					log.Error(err)
					t.Error(err)
				} else {
					log.Debug("Ran %s", fn)
				}
			}
		}
	}

	var f func(indent string, v py.Object, buf *bytes.Buffer)
	f = func(indent string, v py.Object, buf *bytes.Buffer) {
		b := v.Base()
		if dir, err := b.Dir(); err != nil {
			t.Error(err)
		} else {
			if l, ok := dir.(*py.List); ok {
				sl := l.Slice()

				if indent == "" {
					for _, v2 := range sl {
						if item, err := b.GetAttr(v2); err != nil {
							t.Error(err)
						} else {
							ty := item.Type()
							line := fmt.Sprintf("%s%s\n", indent, v2)
							buf.WriteString(line)
							if ty == py.TypeType {
								f(indent+"\t", item, buf)
							}
							item.Decref()
						}
					}
				} else {
					for _, v2 := range sl {
						buf.WriteString(fmt.Sprintf("%s%s\n", indent, v2))
					}
				}

			} else {
				ty := dir.Type()
				t.Error("Unexpected type:", ty)
			}
			dir.Decref()
		}
	}
	buf := bytes.NewBuffer(nil)
	f("", subl, buf)

	l.Unlock()

	const expfile = "testdata/api.txt"
	if d, err := ioutil.ReadFile(expfile); err != nil {
		if err := ioutil.WriteFile(expfile, buf.Bytes(), 0644); err != nil {
			t.Error(err)
		}
	} else if diff := util.Diff(string(d), buf.String()); diff != "" {
		t.Error(diff)
	}
	ed.LogCommands(true)
	tests := []string{
		"state",
		"registers",
		"settings",
		"constants",
		"registers",
		"cmd_data",
		"marks",
	}

	for _, test := range tests {
		ed.CommandHandler().RunWindowCommand(w, "vintage_ex_run_data_file_based_tests", backend.Args{"suite_name": test})
	}
	for _, w := range ed.Windows() {
		for _, v := range w.Views() {
			if strings.HasSuffix(v.Buffer().FileName(), "sample.txt") {
				continue
			}
			if strings.Index(v.Buffer().Substr(text.Region{A: 0, B: v.Buffer().Size()}), "FAILED") != -1 {
				t.Error(v.Buffer())
			}
		}
	}

	var v *backend.View
	for _, v2 := range w.Views() {
		if v == nil || v2.Buffer().Size() > v.Buffer().Size() {
			v = v2
		}
	}
}
<commit_msg>[ci] Skip reload_test.py on OSX to try and work around #531.<commit_after>// Copyright 2013 The lime Authors.
// Use of this source code is governed by a 2-clause
// BSD-style license that can be found in the LICENSE file.

package sublime

import (
	"bytes"
	"fmt"
	"github.com/limetext/gopy/lib"
	"github.com/limetext/lime/backend"
	_ "github.com/limetext/lime/backend/commands"
	"github.com/limetext/lime/backend/log"
	"github.com/limetext/lime/backend/packages"
	"github.com/limetext/lime/backend/util"
	"github.com/limetext/text"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"testing"
	"time"
)

var dummyClipboard string

type consoleObserver struct {
	T *testing.T
}

func (o *consoleObserver) Erased(changed_buffer text.Buffer, region_removed text.Region, data_removed []rune) {
	// do nothing
}

func (o *consoleObserver) Inserted(changed_buffer text.Buffer, region_inserted text.Region, data_inserted []rune) {
	o.T.Logf("%s", string(data_inserted))
}

func TestSublime(t *testing.T) {
	ed := backend.GetEditor()
	ed.SetClipboardFuncs(func(n string) (err error) {
		dummyClipboard = n
		return nil
	}, func() (string, error) {
		return dummyClipboard, nil
	})
	ed.Init()

	ed.Console().Buffer().AddObserver(&consoleObserver{T: t})
	w := ed.NewWindow()
	l := py.NewLock()
	py.AddToPath("testdata")
	py.AddToPath("testdata/plugins")
	if m, err := py.Import("sublime_plugin"); err != nil {
		t.Fatal(err)
	} else {
		plugins := packages.ScanPlugins("testdata/", ".py")
		for _, p := range plugins {
			newPlugin(p, m)
		}
	}

	subl, err := py.Import("sublime")
	if err != nil {
		t.Fatal(err)
	}

	if w, err := _windowClass.Alloc(1); err != nil {
		t.Fatal(err)
	} else {
		(w.(*Window)).data = &backend.Window{}
		subl.AddObject("test_window", w)
	}

	// Testing plugin reload
	data := []byte(`import sublime, sublime_plugin

class TestToxt(sublime_plugin.TextCommand):
    def run(self, edit):
        print("my view's id is: %d" % self.view.id())
        self.view.insert(edit, 0, "Tada")
		`)
	if err := ioutil.WriteFile("testdata/plugins/reload.py", data, 0644); err != nil {
		t.Fatalf("Couldn't write testdata/plugins/reload.py: %s", err)
	}
	defer os.Remove("testdata/plugins/reload.py")
	time.Sleep(time.Millisecond * 50)

	if dir, err := os.Open("testdata"); err != nil {
		t.Error(err)
	} else if files, err := dir.Readdirnames(0); err != nil {
		t.Error(err)
	} else {
		for _, fn := range files {
			// FIXME: Skip reload_test.py to work around #531 on OSX.
			if fn == "reload_test.py" && ed.Platform() == "darwin" {
				continue
			}

			if filepath.Ext(fn) == ".py" {
				log.Debug("Running %s", fn)
				if _, err := py.Import(fn[:len(fn)-3]); err != nil {
					log.Error(err)
					t.Error(err)
				} else {
					log.Debug("Ran %s", fn)
				}
			}
		}
	}

	var f func(indent string, v py.Object, buf *bytes.Buffer)
	f = func(indent string, v py.Object, buf *bytes.Buffer) {
		b := v.Base()
		if dir, err := b.Dir(); err != nil {
			t.Error(err)
		} else {
			if l, ok := dir.(*py.List); ok {
				sl := l.Slice()

				if indent == "" {
					for _, v2 := range sl {
						if item, err := b.GetAttr(v2); err != nil {
							t.Error(err)
						} else {
							ty := item.Type()
							line := fmt.Sprintf("%s%s\n", indent, v2)
							buf.WriteString(line)
							if ty == py.TypeType {
								f(indent+"\t", item, buf)
							}
							item.Decref()
						}
					}
				} else {
					for _, v2 := range sl {
						buf.WriteString(fmt.Sprintf("%s%s\n", indent, v2))
					}
				}

			} else {
				ty := dir.Type()
				t.Error("Unexpected type:", ty)
			}
			dir.Decref()
		}
	}
	buf := bytes.NewBuffer(nil)
	f("", subl, buf)

	l.Unlock()

	const expfile = "testdata/api.txt"
	if d, err := ioutil.ReadFile(expfile); err != nil {
		if err := ioutil.WriteFile(expfile, buf.Bytes(), 0644); err != nil {
			t.Error(err)
		}
	} else if diff := util.Diff(string(d), buf.String()); diff != "" {
		t.Error(diff)
	}
	ed.LogCommands(true)
	tests := []string{
		"state",
		"registers",
		"settings",
		"constants",
		"registers",
		"cmd_data",
		"marks",
	}

	for _, test := range tests {
		ed.CommandHandler().RunWindowCommand(w, "vintage_ex_run_data_file_based_tests", backend.Args{"suite_name": test})
	}
	for _, w := range ed.Windows() {
		for _, v := range w.Views() {
			if strings.HasSuffix(v.Buffer().FileName(), "sample.txt") {
				continue
			}
			if strings.Index(v.Buffer().Substr(text.Region{A: 0, B: v.Buffer().Size()}), "FAILED") != -1 {
				t.Error(v.Buffer())
			}
		}
	}

	var v *backend.View
	for _, v2 := range w.Views() {
		if v == nil || v2.Buffer().Size() > v.Buffer().Size() {
			v = v2
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2016 Cyako Author

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

//     http://www.apache.org/licenses/LICENSE-2.0

// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package realtime

import (
	cyako "github.com/Cyako/Cyako.go"
	"github.com/Cyako/Cyako.go/kvstore"

	"fmt"
	ns "github.com/Centimitr/namespace"
	"golang.org/x/net/websocket"
)

/*
	define
*/
type dep struct {
	KVStore *kvstore.KVStore
}

type Listener struct {
	Conn   *websocket.Conn
	Id     string
	Method string
}

func (l *Listener) Receive(res *cyako.Res) {
	fmt.Println("Receive:", l.Conn, res)
	if l.Conn == nil {
		return
	}
	if err := websocket.JSON.Send(l.Conn, res); err != nil {
		// fmt.Println("SEND ERR:", err)
		return
	}
}

type Realtime struct {
	Dependences dep
	Scope       ns.Scope
}

// realtime use the prefix to store data in KVStore
// const KVSTORE_SCOPE_LISTENER_GROUPS = "service.realtime.listnerGroups"

// This method add specific *websocket.Conn to listeners list
func (r *Realtime) AddListener(groupName string, conn *websocket.Conn, id string, method string) {
	// kvstore := r.Dependences.KVStore
	listeners := []Listener{}
	// if kvstore.HasWithScoped(KVSTORE_SCOPE_LISTENER_GROUPS, groupName) {
	// 	listeners = kvstore.GetWithScoped(KVSTORE_SCOPE_LISTENER_GROUPS, groupName).([]Listener)
	// }
	listeners = append(listeners, Listener{Conn: conn, Id: id})
	// kvstore.SetWithScoped(KVSTORE_SCOPE_LISTENER_GROUPS, groupName, listeners)
	if r.Scope.Handler(groupName).Has() {
		r.Scope.Handler(groupName).Set(listeners)
	}
}

func (r *Realtime) AddListenerDefault(groupName string, ctx *cyako.Ctx) {
	r.AddListener(groupName, ctx.Conn, ctx.Id, ctx.Method)
}

// Send response to listeners in some group
func (r *Realtime) Send(groupName string, res *cyako.Res) {
	fmt.Println("Start Sending.")
	// kvstore := r.Dependences.KVStore
	listeners := []Listener{}
	// if kvstore.HasWithScoped(KVSTORE_SCOPE_LISTENER_GROUPS, groupName) {
	// 	listeners = kvstore.GetWithScoped(KVSTORE_SCOPE_LISTENER_GROUPS, groupName).([]Listener)
	// }
	if r.Scope.Handler(groupName).Has() {
		listeners = r.Scope.Handler(groupName).Get().([]Listener)
	}
	fmt.Println("listners:", listeners)
	for _, listener := range listeners {
		res.Id = listener.Id
		res.Method = listener.Method
		listener.Receive(res)
	}
}

/*
	init
*/

func init() {
	r := &Realtime{
		Dependences: dep{
			KVStore: cyako.Svc["KVStore"].(*kvstore.KVStore),
		},
	}
	_, r.Scope = r.Dependences.KVStore.Service.Apply("REALTIME")
	cyako.LoadService(r)
	fmt.Println("LOAD REALTIME.", r)
}
<commit_msg>updateclear<commit_after>// Copyright 2016 Cyako Author

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

//     http://www.apache.org/licenses/LICENSE-2.0

// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package realtime

import (
	cyako "github.com/Cyako/Cyako.go"
	"github.com/Cyako/Cyako.go/kvstore"

	"fmt"
	ns "github.com/Centimitr/namespace"
	"golang.org/x/net/websocket"
)

/*
	define
*/
type dep struct {
	KVStore *kvstore.KVStore
}

type Listener struct {
	Conn   *websocket.Conn
	Id     string
	Method string
}

func (l *Listener) Receive(res *cyako.Res) {
	fmt.Println("Receive:", l.Conn, res)
	if l.Conn == nil {
		return
	}
	if err := websocket.JSON.Send(l.Conn, res); err != nil {
		// fmt.Println("SEND ERR:", err)
		return
	}
}

type Realtime struct {
	Dependences dep
	Scope       ns.Scope
}

// realtime use the prefix to store data in KVStore
// const KVSTORE_SCOPE_LISTENER_GROUPS = "service.realtime.listnerGroups"

// This method add specific *websocket.Conn to listeners list
func (r *Realtime) AddListener(groupName string, conn *websocket.Conn, id string, method string) {
	listeners := []Listener{}
	if r.Scope.Handler(groupName).Has() {
		listeners = r.Scope.Handler(groupName).Get().([]Listener)
	}
	listeners = append(listeners, Listener{Conn: conn, Id: id})
	r.Scope.Handler(groupName).Set(listeners)
}

func (r *Realtime) AddListenerDefault(groupName string, ctx *cyako.Ctx) {
	r.AddListener(groupName, ctx.Conn, ctx.Id, ctx.Method)
}

// Send response to listeners in some group
func (r *Realtime) Send(groupName string, res *cyako.Res) {
	fmt.Println("Start Sending.")
	listeners := []Listener{}
	if r.Scope.Handler(groupName).Has() {
		listeners = r.Scope.Handler(groupName).Get().([]Listener)
	}
	fmt.Println("listners:", listeners)
	for _, listener := range listeners {
		res.Id = listener.Id
		res.Method = listener.Method
		listener.Receive(res)
	}
}

/*
	init
*/

func init() {
	r := &Realtime{
		Dependences: dep{
			KVStore: cyako.Svc["KVStore"].(*kvstore.KVStore),
		},
	}
	_, r.Scope = r.Dependences.KVStore.Service.Apply("REALTIME")
	cyako.LoadService(r)
}
<|endoftext|>


<commit_before>package main

import (
  "encoding/json"
  "log"
  "os"
)

func WriteRegistry(state map[string]*FileState, path string) {
  tmp := path + ".new"
  file, err := os.Create(tmp)
  if err != nil {
    log.Printf("Failed to open .logstash-forwarder.new for writing: %s\n", err)
    return
  }

  encoder := json.NewEncoder(file)
  encoder.Encode(state)
  file.Close()

  old := path + ".old"

  if _, err = os.Stat(old); err != nil && os.IsNotExist(err) {
    err = nil
  } else {
    err = os.Remove(old)
  }

  if err != nil {
    log.Printf("Registrar save problem: Failed to delete backup file: %s\n", err)
  }

  err = os.Rename(path, old)
  if err != nil {
    log.Printf("Registrar save problem: Failed to perform backup: %s\n", err)
    return
  }

  err = os.Rename(tmp, path)
  if err != nil {
    log.Printf("Registrar save problem: Failed to move the new file into place: %s\n", err)
  }
}
<commit_msg>Fix startup error on Windows<commit_after>package main

import (
  "encoding/json"
  "log"
  "os"
)

func WriteRegistry(state map[string]*FileState, path string) {
  tmp := path + ".new"
  file, err := os.Create(tmp)
  if err != nil {
    log.Printf("Failed to open .logstash-forwarder.new for writing: %s\n", err)
    return
  }

  encoder := json.NewEncoder(file)
  encoder.Encode(state)
  file.Close()

  old := path + ".old"

  if _, err = os.Stat(old); err != nil && os.IsNotExist(err) {
  } else {
    err = os.Remove(old)
    if err != nil {
      log.Printf("Registrar save problem: Failed to delete backup file: %s\n", err)
    }
  }

  if _, err = os.Stat(path); err != nil && os.IsNotExist(err) {
  } else {
    err = os.Rename(path, old)
    if err != nil {
      log.Printf("Registrar save problem: Failed to perform backup: %s\n", err)
    }
  }

  err = os.Rename(tmp, path)
  if err != nil {
    log.Printf("Registrar save problem: Failed to move the new file into place: %s\n", err)
  }
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"github.com/alecthomas/kingpin"
	"github.com/codegangsta/martini"
	"github.com/jackpal/Taipei-Torrent/torrent"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"strings"
)

var (
	host     = kingpin.Flag("host", "Set host and port of bittorrent tracker. Example: -host 10.240.101.85:8940 Note: This cannot be set to localhost, since this is the tracker in which all the torrents will be created with. They have to be some accessible ip address from outside").Short('h').Default("10.240.101.85:8940").String()
	port     = kingpin.Flag("port", "Set port of docket registry.").Short('p').Default("9090").Int()
	location = kingpin.Flag("location", "Set location to save torrents and docker images.").Short('l').Default("/var/local/docket").String()
)

// The one and only martini instance.
var store *Store
var m *martini.Martini

func init() {
	m = martini.New()
	// Setup routes
	r := martini.NewRouter()
	r.Post(`/images`, postImage)
	r.Get(`/torrents`, getTorrent)
	r.Get(`/images/all`, getImagesList)
	r.Get(`/images`, getImages)
	// Add the router action
	m.Action(r.Handle)
}

func postImage(w http.ResponseWriter, r *http.Request) (int, string) {
	w.Header().Set("Content-Type", "application/json")

	loc := *location
	fmt.Println("location, ", loc)

	// the FormFile function takes in the POST input id file
	file, header, err := r.FormFile("file")

	if err != nil {
		fmt.Println(err)
		return 500, "bad"
	}

	defer file.Close()

	//Get metadata
	image := r.Header.Get("image")
	id := r.Header.Get("id")
	created := r.Header.Get("created")
	fileName := header.Filename

	fmt.Println("Got image: ", image, " id = ", id, " created = ", created, " filename = ", fileName)

	s := []string{loc, "/", fileName}
	t := []string{loc, "/", fileName, ".torrent"}
	filePath := strings.Join(s, "")
	torrentFile := fileName + ".torrent"
	torrentPath := strings.Join(t, "")

	//JSON string of metadata
	imageMeta := map[string]string{
		"image":    image,
		"id":       id,
		"created":  created,
		"fileName": fileName,
	}
	imageMetaJson, _ := json.Marshal(imageMeta)

	out, err := os.Create(filePath)
	if err != nil {
		fmt.Println(err)
		return 500, "bad"
	}

	defer out.Close()

	// write the content from POST to the file
	_, err = io.Copy(out, file)
	if err != nil {
		fmt.Println(err)
		return 500, "bad"
	}

	fmt.Println("File uploaded successfully")

	btHost := *host
	err = createTorrentFile(torrentPath, filePath, btHost)
	if err != nil {
		return 500, "torrent creation failed"
	}

	//Write to datastore
	err = writeToStore(store, "docket", image, string(imageMetaJson))
	if err != nil {
		fmt.Println("Error writing result: ", err)
	}

	//Seed the torrent
	fmt.Println("Seeding torrent in the background...")
	os.Chdir(loc)
	importCmd := fmt.Sprintf("ctorrent -d -e 9999 %s", torrentFile)
	_, err2 := exec.Command("sh", "-c", importCmd).Output()
	if err2 != nil {
		fmt.Printf("Failed to seed torrent..")
		fmt.Println(err2)
		return 500, "bad"
	}

	return http.StatusOK, "{\"status\":\"OK\"}"
}

func getTorrent(w http.ResponseWriter, r *http.Request) int {
	query := r.URL.Query()
	queryJson := query.Get("q")

	var queryObj map[string]interface{}
	if err := json.Unmarshal([]byte(queryJson), &queryObj); err != nil {
		return 500
	}

	imageInterface := queryObj["image"]
	image := imageInterface.(string)

	//Query db and find if image exists. If not throw error (done)
	jsonVal, err := getFromStore(store, "docket", image)
	if err != nil {
		fmt.Println("Error reading from file : %v\n", err)
		return 500
	}

	if jsonVal == "" {
		fmt.Println("Invalid image requested")
		return 500
	}

	//Unmarshall
	var imageObj map[string]interface{}
	if err := json.Unmarshal([]byte(jsonVal), &imageObj); err != nil {
		return 500
	}

	//find location to torrent
	torrentFileInterface := imageObj["fileName"]
	torrentFile := torrentFileInterface.(string) + ".torrent"

	torrentPath := *location + "/" + torrentFile
	//Check if file exists
	if _, err := os.Stat(torrentPath); os.IsNotExist(err) {
		fmt.Println("no such file or directory: %s", torrentPath)
		return 500
	}

	//set filepath to that
	file, err := ioutil.ReadFile(torrentPath)
	if err != nil {
		return 500
	}

	w.Header().Set("Content-Type", "application/x-bittorrent")
	if file != nil {
		w.Write(file)
		return http.StatusOK
	}

	return 500
}

func getImages(w http.ResponseWriter, r *http.Request) (int, string) {
	query := r.URL.Query()
	queryJson := query.Get("q")

	var queryObj map[string]interface{}
	if err := json.Unmarshal([]byte(queryJson), &queryObj); err != nil {
		return 500, ""
	}

	imageInterface := queryObj["image"]
	image := imageInterface.(string)

	fmt.Println("image = ", image)

	//Query db and find if image exists. If not throw error (done)
	jsonVal, err := getFromStore(store, "docket", image)
	if err != nil {
		fmt.Println("Error reading from file : %v\n", err)
		return 500, ""
	}

	if jsonVal == "" {
		fmt.Println("Invalid image requested")
		return 500, ""
	}

	w.Header().Set("Content-Type", "application/json")
	return http.StatusOK, jsonVal
}

func getImagesList(w http.ResponseWriter, r *http.Request) (int, string) {
	//Query db and find if image exists. If not throw error (done)
	keys, err := iterateStore(store, "docket")
	if err != nil {
		fmt.Println("Error reading from file : %v\n", err)
		return 500, ""
	}

	if keys == "" {
		fmt.Println("Invalid image requested")
		return 500, ""
	}

	w.Header().Set("Content-Type", "text/plain")
	return http.StatusOK, keys
}

func createTorrentFile(torrentFileName, root, announcePath string) (err error) {
	var metaInfo *torrent.MetaInfo
	metaInfo, err = torrent.CreateMetaInfoFromFileSystem(nil, root, 0, false)
	if err != nil {
		return
	}
	btHost := *host
	metaInfo.Announce = "http://" + btHost + "/announce"
	metaInfo.CreatedBy = "docket-registry"
	var torrentFile *os.File
	torrentFile, err = os.Create(torrentFileName)
	if err != nil {
		return
	}
	defer torrentFile.Close()
	err = metaInfo.Bencode(torrentFile)
	if err != nil {
		return
	}
	return
}

func main() {
	kingpin.CommandLine.Help = "Docket Registry"
	kingpin.Parse()

	loc := *location
	if _, err := os.Stat(loc); os.IsNotExist(err) {
		os.Mkdir(loc, 0644)
	}

	var storeErr error

	store, storeErr = openStore()
	if storeErr != nil {
		log.Fatal("Failed to open data store: %v", storeErr)
	}
	deferCloseStore(store)

	if err := http.ListenAndServe(":8000", m); err != nil {
		log.Fatal(err)
	}
}
<commit_msg>override port through flag<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"github.com/alecthomas/kingpin"
	"github.com/codegangsta/martini"
	"github.com/jackpal/Taipei-Torrent/torrent"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"strings"
)

var (
	tracker  = kingpin.Flag("tracker", "Set host and port of bittorrent tracker. Example: -host 10.240.101.85:8940 Note: This cannot be set to localhost, since this is the tracker in which all the torrents will be created with. They have to be some accessible ip address from outside").Short('t').Default("10.240.101.85:8940").String()
	port     = kingpin.Flag("port", "Set port of docket registry.").Short('p').Default("8000").String()
	location = kingpin.Flag("location", "Set location to save torrents and docker images.").Short('l').Default("/var/local/docket").String()
)

// The one and only martini instance.
var store *Store
var m *martini.Martini

func init() {
	m = martini.New()
	// Setup routes
	r := martini.NewRouter()
	r.Post(`/images`, postImage)
	r.Get(`/torrents`, getTorrent)
	r.Get(`/images/all`, getImagesList)
	r.Get(`/images`, getImages)
	// Add the router action
	m.Action(r.Handle)
}

func postImage(w http.ResponseWriter, r *http.Request) (int, string) {
	w.Header().Set("Content-Type", "application/json")

	loc := *location
	fmt.Println("location, ", loc)

	// the FormFile function takes in the POST input id file
	file, header, err := r.FormFile("file")

	if err != nil {
		fmt.Println(err)
		return 500, "bad"
	}

	defer file.Close()

	//Get metadata
	image := r.Header.Get("image")
	id := r.Header.Get("id")
	created := r.Header.Get("created")
	fileName := header.Filename

	fmt.Println("Got image: ", image, " id = ", id, " created = ", created, " filename = ", fileName)

	s := []string{loc, "/", fileName}
	t := []string{loc, "/", fileName, ".torrent"}
	filePath := strings.Join(s, "")
	torrentFile := fileName + ".torrent"
	torrentPath := strings.Join(t, "")

	//JSON string of metadata
	imageMeta := map[string]string{
		"image":    image,
		"id":       id,
		"created":  created,
		"fileName": fileName,
	}
	imageMetaJson, _ := json.Marshal(imageMeta)

	out, err := os.Create(filePath)
	if err != nil {
		fmt.Println(err)
		return 500, "bad"
	}

	defer out.Close()

	// write the content from POST to the file
	_, err = io.Copy(out, file)
	if err != nil {
		fmt.Println(err)
		return 500, "bad"
	}

	fmt.Println("File uploaded successfully")

	btHost := *tracker
	fmt.Println("btHost = ", btHost)
	err = createTorrentFile(torrentPath, filePath, btHost)
	if err != nil {
		return 500, "torrent creation failed"
	}

	//Write to datastore
	err = writeToStore(store, "docket", image, string(imageMetaJson))
	if err != nil {
		fmt.Println("Error writing result: ", err)
	}

	//Seed the torrent
	fmt.Println("Seeding torrent in the background...")
	os.Chdir(loc)
	importCmd := fmt.Sprintf("ctorrent -d -e 9999 %s", torrentFile)
	_, err2 := exec.Command("sh", "-c", importCmd).Output()
	if err2 != nil {
		fmt.Printf("Failed to seed torrent..")
		fmt.Println(err2)
		return 500, "bad"
	}

	return http.StatusOK, "{\"status\":\"OK\"}"
}

func getTorrent(w http.ResponseWriter, r *http.Request) int {
	query := r.URL.Query()
	queryJson := query.Get("q")

	var queryObj map[string]interface{}
	if err := json.Unmarshal([]byte(queryJson), &queryObj); err != nil {
		return 500
	}

	imageInterface := queryObj["image"]
	image := imageInterface.(string)

	//Query db and find if image exists. If not throw error (done)
	jsonVal, err := getFromStore(store, "docket", image)
	if err != nil {
		fmt.Println("Error reading from file : %v\n", err)
		return 500
	}

	if jsonVal == "" {
		fmt.Println("Invalid image requested")
		return 500
	}

	//Unmarshall
	var imageObj map[string]interface{}
	if err := json.Unmarshal([]byte(jsonVal), &imageObj); err != nil {
		return 500
	}

	//find location to torrent
	torrentFileInterface := imageObj["fileName"]
	torrentFile := torrentFileInterface.(string) + ".torrent"

	torrentPath := *location + "/" + torrentFile
	//Check if file exists
	if _, err := os.Stat(torrentPath); os.IsNotExist(err) {
		fmt.Println("no such file or directory: %s", torrentPath)
		return 500
	}

	//set filepath to that
	file, err := ioutil.ReadFile(torrentPath)
	if err != nil {
		return 500
	}

	w.Header().Set("Content-Type", "application/x-bittorrent")
	if file != nil {
		w.Write(file)
		return http.StatusOK
	}

	return 500
}

func getImages(w http.ResponseWriter, r *http.Request) (int, string) {
	query := r.URL.Query()
	queryJson := query.Get("q")

	var queryObj map[string]interface{}
	if err := json.Unmarshal([]byte(queryJson), &queryObj); err != nil {
		return 500, ""
	}

	imageInterface := queryObj["image"]
	image := imageInterface.(string)

	fmt.Println("image = ", image)

	//Query db and find if image exists. If not throw error (done)
	jsonVal, err := getFromStore(store, "docket", image)
	if err != nil {
		fmt.Println("Error reading from file : %v\n", err)
		return 500, ""
	}

	if jsonVal == "" {
		fmt.Println("Invalid image requested")
		return 500, ""
	}

	w.Header().Set("Content-Type", "application/json")
	return http.StatusOK, jsonVal
}

func getImagesList(w http.ResponseWriter, r *http.Request) (int, string) {
	//Query db and find if image exists. If not throw error (done)
	keys, err := iterateStore(store, "docket")
	if err != nil {
		fmt.Println("Error reading from file : %v\n", err)
		return 500, ""
	}

	if keys == "" {
		fmt.Println("Invalid image requested")
		return 500, ""
	}

	w.Header().Set("Content-Type", "text/plain")
	return http.StatusOK, keys
}

func createTorrentFile(torrentFileName, root, announcePath string) (err error) {
	var metaInfo *torrent.MetaInfo
	metaInfo, err = torrent.CreateMetaInfoFromFileSystem(nil, root, 0, false)
	if err != nil {
		return
	}
	btHost := *tracker
	metaInfo.Announce = "http://" + btHost + "/announce"
	metaInfo.CreatedBy = "docket-registry"
	var torrentFile *os.File
	torrentFile, err = os.Create(torrentFileName)
	if err != nil {
		return
	}
	defer torrentFile.Close()
	err = metaInfo.Bencode(torrentFile)
	if err != nil {
		return
	}
	return
}

func main() {
	kingpin.CommandLine.Help = "Docket Registry"
	kingpin.Parse()

	loc := *location
	if _, err := os.Stat(loc); os.IsNotExist(err) {
		os.Mkdir(loc, 0644)
	}

	var storeErr error

	store, storeErr = openStore()
	if storeErr != nil {
		log.Fatal("Failed to open data store: %v", storeErr)
	}
	deferCloseStore(store)

	pString := ":" + *port

	if err := http.ListenAndServe(pString, m); err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package billing_test

import (
	"fmt"
	"testing"

	"github.com/BytemarkHosting/bytemark-client/lib"
	"github.com/BytemarkHosting/bytemark-client/lib/billing"
	billingMethods "github.com/BytemarkHosting/bytemark-client/lib/requests/billing"
	"github.com/BytemarkHosting/bytemark-client/lib/testutil"
	"github.com/BytemarkHosting/bytemark-client/lib/testutil/assert"
)

func TestAssentToAgreement(t *testing.T) {
	tests := []struct {
		assent    billing.Assent
		expected  map[string]interface{}
		shouldErr bool
	}{
		{
			assent: billing.Assent{
				AgreementID: "jeff",
				AccountID:   123456,
				PersonID:    789101,
				Name:        "geoff",
				Email:       "geoff@bytemark.com",
			},
			expected: map[string]interface{}{
				"account_id": 123456.0,
				"person_id":  789101.0,
				"name":       "geoff",
				"email":      "geoff@bytemark.com",
			},
		},
	}

	for i, test := range tests {
		testName := testutil.Name(i)
		rts := testutil.RequestTestSpec{
			Method:        "POST",
			Endpoint:      lib.BillingEndpoint,
			URL:           fmt.Sprintf("/api/v1/agreements/%s/assents", test.assent.AgreementID),
			AssertRequest: assert.BodyUnmarshalEqual(test.expected),
		}
		rts.Run(t, testName, true, func(client lib.Client) {
			err := billingMethods.AssentToAgreement(client, test.assent)
			if test.shouldErr {
				assert.NotEqual(t, testName, nil, err)
			} else {
				assert.Equal(t, testName, nil, err)
			}
		})
	}
}
<commit_msg>update test<commit_after>package billing_test

import (
	"fmt"
	"testing"

	"github.com/BytemarkHosting/bytemark-client/lib"
	"github.com/BytemarkHosting/bytemark-client/lib/billing"
	billingMethods "github.com/BytemarkHosting/bytemark-client/lib/requests/billing"
	"github.com/BytemarkHosting/bytemark-client/lib/testutil"
	"github.com/BytemarkHosting/bytemark-client/lib/testutil/assert"
)

func TestAssentToAgreement(t *testing.T) {
	tests := []struct {
		assent    billing.Assent
		expected  map[string]interface{}
		shouldErr bool
	}{
		{
			assent: billing.Assent{
				AgreementID: "jeff",
				AccountID:   123456,
				PersonID:    789101,
				Name:        "geoff",
				Email:       "geoff@bytemark.com",
			},
			expected: map[string]interface{}{
				"agreement_id": "jeff",
				"account_id":   123456.0,
				"person_id":    789101.0,
				"name":         "geoff",
				"email":        "geoff@bytemark.com",
			},
		},
	}

	for i, test := range tests {
		testName := testutil.Name(i)
		rts := testutil.RequestTestSpec{
			Method:        "POST",
			Endpoint:      lib.BillingEndpoint,
			URL:           fmt.Sprintf("/api/v1/agreements/%s/assents", test.assent.AgreementID),
			AssertRequest: assert.BodyUnmarshalEqual(test.expected),
		}
		rts.Run(t, testName, true, func(client lib.Client) {
			err := billingMethods.AssentToAgreement(client, test.assent)
			if test.shouldErr {
				assert.NotEqual(t, testName, nil, err)
			} else {
				assert.Equal(t, testName, nil, err)
			}
		})
	}
}
<|endoftext|>


<commit_before>package mpuwsgivassal

import (
	"encoding/json"
	"flag"
	"net"
	"net/http"
	"reflect"
	"strings"

	mp "github.com/mackerelio/go-mackerel-plugin-helper"
)

// UWSGIVassalPlugin mackerel plugin for uWSGI
type UWSGIVassalPlugin struct {
	Socket      string
	Prefix      string
	LabelPrefix string
}

// {
//   "workers": [{
//     "id": 1,
//     "pid": 31759,
//     "requests": 0,
//     "exceptions": 0,
//     "status": "idle",
//     "rss": 0,
//     "vsz": 0,
//     "running_time": 0,
//     "last_spawn": 1317235041,
//     "respawn_count": 1,
//     "tx": 0,
//     "avg_rt": 0,
//     "apps": [{
//       "id": 0,
//       "modifier1": 0,
//       "mountpoint": "",
//       "requests": 0,
//       "exceptions": 0,
//       "chdir": ""
//     }]
//   }, {
//     "id": 2,
//     "pid": 31760,
//     "requests": 0,
//     "exceptions": 0,
//     "status": "idle",
//     "rss": 0,
//     "vsz": 0,
//     "running_time": 0,
//     "last_spawn": 1317235041,
//     "respawn_count": 1,
//     "tx": 0,
//     "avg_rt": 0,
//     "apps": [{
//       "id": 0,
//       "modifier1": 0,
//       "mountpoint": "",
//       "requests": 0,
//       "exceptions": 0,
//       "chdir": ""
//     }]
//   }, {
//     "id": 3,
//     "pid": 31761,
//     "requests": 0,
//     "exceptions": 0,
//     "status": "idle",
//     "rss": 0,
//     "vsz": 0,
//     "running_time": 0,
//     "last_spawn": 1317235041,
//     "respawn_count": 1,
//     "tx": 0,
//     "avg_rt": 0,
//     "apps": [{
//       "id": 0,
//       "modifier1": 0,
//       "mountpoint": "",
//       "requests": 0,
//       "exceptions": 0,
//       "chdir": ""
//     }]
//   }, {
//     "id": 4,
//     "pid": 31762,
//     "requests": 0,
//     "exceptions": 0,
//     "status": "idle",
//     "rss": 0,
//     "vsz": 0,
//     "running_time": 0,
//     "last_spawn": 1317235041,
//     "respawn_count": 1,
//     "tx": 0,
//     "avg_rt": 0,
//     "apps": [{
//       "id": 0,
//       "modifier1": 0,
//       "mountpoint": "",
//       "requests": 0,
//       "exceptions": 0,
//       "chdir": ""
//     }]
//   }
// }

// field types vary between versions

// UWSGIWorker struct
type UWSGIWorker struct {
	Requests uint64 `json:"requests"`
	Status   string `json:"status"`
}

// UWSGIWorkers sturct for json struct
type UWSGIWorkers struct {
	Workers []UWSGIWorker `json:"workers"`
}

// FetchMetrics interface for mackerelplugin
func (p UWSGIVassalPlugin) FetchMetrics() (map[string]interface{}, error) {
	stat := make(map[string]interface{})
	stat["busy"] = uint64(0)
	stat["idle"] = uint64(0)
	stat["cheap"] = uint64(0)
	stat["pause"] = uint64(0)
	stat["requests"] = uint64(0)

	var decoder *json.Decoder
	if strings.HasPrefix(p.Socket, "unix://") {
		conn, err := net.Dial("unix", strings.TrimPrefix(p.Socket, "unix://"))
		if err != nil {
			return nil, err
		}
		defer conn.Close()
		decoder = json.NewDecoder(conn)
	} else if strings.HasPrefix(p.Socket, "http://") {
		resp, err := http.Get(p.Socket)
		if err != nil {
			return nil, err
		}
		defer resp.Body.Close()
		decoder = json.NewDecoder(resp.Body)
	}

	var workers UWSGIWorkers
	if err := decoder.Decode(&workers); err != nil {
		return nil, err
	}

	for _, worker := range workers.Workers {
		switch worker.Status {
		case "idle", "busy", "cheap", "pause":
			stat[worker.Status] = reflect.ValueOf(stat[worker.Status]).Uint() + 1
		}
		stat["requests"] = reflect.ValueOf(stat["requests"]).Uint() + worker.Requests
	}

	return stat, nil
}

// GraphDefinition interface for mackerelplugin
func (p UWSGIVassalPlugin) GraphDefinition() map[string]mp.Graphs {
	labelPrefix := strings.Title(p.Prefix)

	var graphdef = map[string]mp.Graphs{
		(p.Prefix + ".workers"): {
			Label: labelPrefix + " Workers",
			Unit:  "integer",
			Metrics: []mp.Metrics{
				{Name: "busy", Label: "Busy", Diff: false, Stacked: true},
				{Name: "idle", Label: "Idle", Diff: false, Stacked: true},
				{Name: "cheap", Label: "Cheap", Diff: false, Stacked: true},
				{Name: "pause", Label: "Pause", Diff: false, Stacked: true},
			},
		},
		(p.Prefix + ".req"): {
			Label: labelPrefix + " Requests",
			Unit:  "integer",
			Metrics: []mp.Metrics{
				{Name: "requests", Label: "Requests", Diff: true, Type: "uint64"},
			},
		},
	}

	return graphdef
}

// MetricKeyPrefix interface for PluginWithPrefix
func (p UWSGIVassalPlugin) MetricKeyPrefix() string {
	if p.Prefix == "" {
		p.Prefix = "uWSGI"
	}
	return p.Prefix
}

// Do the plugin
func Do() {
	optSocket := flag.String("socket", "", "Socket")
	optPrefix := flag.String("metric-key-prefix", "uWSGI", "Prefix")
	optTempfile := flag.String("tempfile", "", "Temp file name")
	flag.Parse()

	uwsgi := UWSGIVassalPlugin{Socket: *optSocket, Prefix: *optPrefix}
	if uwsgi.LabelPrefix == "" {
		uwsgi.LabelPrefix = strings.Title(uwsgi.Prefix)
	}

	helper := mp.NewMackerelPlugin(uwsgi)
	helper.Tempfile = *optTempfile
	helper.Run()
}
<commit_msg>Switch to go-mackerel-plugin<commit_after>package mpuwsgivassal

import (
	"encoding/json"
	"flag"
	"net"
	"net/http"
	"strings"

	mp "github.com/mackerelio/go-mackerel-plugin"
)

// UWSGIVassalPlugin mackerel plugin for uWSGI
type UWSGIVassalPlugin struct {
	Socket      string
	Prefix      string
	LabelPrefix string
}

// {
//   "workers": [{
//     "id": 1,
//     "pid": 31759,
//     "requests": 0,
//     "exceptions": 0,
//     "status": "idle",
//     "rss": 0,
//     "vsz": 0,
//     "running_time": 0,
//     "last_spawn": 1317235041,
//     "respawn_count": 1,
//     "tx": 0,
//     "avg_rt": 0,
//     "apps": [{
//       "id": 0,
//       "modifier1": 0,
//       "mountpoint": "",
//       "requests": 0,
//       "exceptions": 0,
//       "chdir": ""
//     }]
//   }, {
//     "id": 2,
//     "pid": 31760,
//     "requests": 0,
//     "exceptions": 0,
//     "status": "idle",
//     "rss": 0,
//     "vsz": 0,
//     "running_time": 0,
//     "last_spawn": 1317235041,
//     "respawn_count": 1,
//     "tx": 0,
//     "avg_rt": 0,
//     "apps": [{
//       "id": 0,
//       "modifier1": 0,
//       "mountpoint": "",
//       "requests": 0,
//       "exceptions": 0,
//       "chdir": ""
//     }]
//   }, {
//     "id": 3,
//     "pid": 31761,
//     "requests": 0,
//     "exceptions": 0,
//     "status": "idle",
//     "rss": 0,
//     "vsz": 0,
//     "running_time": 0,
//     "last_spawn": 1317235041,
//     "respawn_count": 1,
//     "tx": 0,
//     "avg_rt": 0,
//     "apps": [{
//       "id": 0,
//       "modifier1": 0,
//       "mountpoint": "",
//       "requests": 0,
//       "exceptions": 0,
//       "chdir": ""
//     }]
//   }, {
//     "id": 4,
//     "pid": 31762,
//     "requests": 0,
//     "exceptions": 0,
//     "status": "idle",
//     "rss": 0,
//     "vsz": 0,
//     "running_time": 0,
//     "last_spawn": 1317235041,
//     "respawn_count": 1,
//     "tx": 0,
//     "avg_rt": 0,
//     "apps": [{
//       "id": 0,
//       "modifier1": 0,
//       "mountpoint": "",
//       "requests": 0,
//       "exceptions": 0,
//       "chdir": ""
//     }]
//   }
// }

// field types vary between versions

// UWSGIWorker struct
type UWSGIWorker struct {
	Requests uint64 `json:"requests"`
	Status   string `json:"status"`
}

// UWSGIWorkers sturct for json struct
type UWSGIWorkers struct {
	Workers []UWSGIWorker `json:"workers"`
}

// FetchMetrics interface for mackerelplugin
func (p UWSGIVassalPlugin) FetchMetrics() (map[string]float64, error) {
	stat := make(map[string]float64)
	stat["busy"] = 0.0
	stat["idle"] = 0.0
	stat["cheap"] = 0.0
	stat["pause"] = 0.0
	stat["requests"] = 0.0

	var decoder *json.Decoder
	if strings.HasPrefix(p.Socket, "unix://") {
		conn, err := net.Dial("unix", strings.TrimPrefix(p.Socket, "unix://"))
		if err != nil {
			return nil, err
		}
		defer conn.Close()
		decoder = json.NewDecoder(conn)
	} else if strings.HasPrefix(p.Socket, "http://") {
		resp, err := http.Get(p.Socket)
		if err != nil {
			return nil, err
		}
		defer resp.Body.Close()
		decoder = json.NewDecoder(resp.Body)
	}

	var workers UWSGIWorkers
	if err := decoder.Decode(&workers); err != nil {
		return nil, err
	}

	for _, worker := range workers.Workers {
		switch worker.Status {
		case "idle", "busy", "cheap", "pause":
			stat[worker.Status]++
		}
		stat["requests"] += float64(worker.Requests)
	}

	return stat, nil
}

// GraphDefinition interface for mackerelplugin
func (p UWSGIVassalPlugin) GraphDefinition() map[string]mp.Graphs {
	labelPrefix := strings.Title(p.Prefix)

	var graphdef = map[string]mp.Graphs{
		(p.Prefix + ".workers"): {
			Label: labelPrefix + " Workers",
			Unit:  "integer",
			Metrics: []mp.Metrics{
				{Name: "busy", Label: "Busy", Diff: false, Stacked: true},
				{Name: "idle", Label: "Idle", Diff: false, Stacked: true},
				{Name: "cheap", Label: "Cheap", Diff: false, Stacked: true},
				{Name: "pause", Label: "Pause", Diff: false, Stacked: true},
			},
		},
		(p.Prefix + ".req"): {
			Label: labelPrefix + " Requests",
			Unit:  "float",
			Metrics: []mp.Metrics{
				{Name: "requests", Label: "Requests", Diff: true},
			},
		},
	}

	return graphdef
}

// MetricKeyPrefix interface for PluginWithPrefix
func (p UWSGIVassalPlugin) MetricKeyPrefix() string {
	if p.Prefix == "" {
		p.Prefix = "uWSGI"
	}
	return p.Prefix
}

// Do the plugin
func Do() {
	optSocket := flag.String("socket", "", "Socket")
	optPrefix := flag.String("metric-key-prefix", "uWSGI", "Prefix")
	optTempfile := flag.String("tempfile", "", "Temp file name")
	flag.Parse()

	uwsgi := UWSGIVassalPlugin{Socket: *optSocket, Prefix: *optPrefix}
	if uwsgi.LabelPrefix == "" {
		uwsgi.LabelPrefix = strings.Title(uwsgi.Prefix)
	}

	helper := mp.NewMackerelPlugin(uwsgi)
	helper.Tempfile = *optTempfile
	helper.Run()
}
<|endoftext|>


<commit_before>package center

import (
	"errors"
	"fmt"
	"strings"
	"time"

	"github.com/cozy/cozy-stack/model/app"
	"github.com/cozy/cozy-stack/model/instance"
	"github.com/cozy/cozy-stack/model/instance/lifecycle"
	"github.com/cozy/cozy-stack/model/job"
	"github.com/cozy/cozy-stack/model/notification"
	"github.com/cozy/cozy-stack/model/oauth"
	"github.com/cozy/cozy-stack/model/permission"
	"github.com/cozy/cozy-stack/model/vfs"
	"github.com/cozy/cozy-stack/pkg/consts"
	"github.com/cozy/cozy-stack/pkg/couchdb"
	"github.com/cozy/cozy-stack/pkg/couchdb/mango"
	"github.com/cozy/cozy-stack/pkg/mail"
	multierror "github.com/hashicorp/go-multierror"
)

const (
	// NotificationDiskQuota category for sending alert when reaching 90% of disk
	// usage quota.
	NotificationDiskQuota = "disk-quota"
)

var (
	stackNotifications = map[string]*notification.Properties{
		NotificationDiskQuota: {
			Description:  "Warn about the diskquota reaching a high level",
			Collapsible:  true,
			Stateful:     true,
			MailTemplate: "notifications_diskquota",
			MinInterval:  7 * 24 * time.Hour,
		},
	}
)

func init() {
	vfs.RegisterDiskQuotaAlertCallback(func(domain string, exceeded bool) {
		i, err := lifecycle.GetInstance(domain)
		if err != nil {
			return
		}
		offersLink, err := i.ManagerURL(instance.ManagerPremiumURL)
		if err != nil {
			return
		}
		cozyDriveLink := i.SubDomain(consts.DriveSlug)
		n := &notification.Notification{
			State: exceeded,
			Data: map[string]interface{}{
				"OffersLink":    offersLink,
				"CozyDriveLink": cozyDriveLink.String(),
			},
		}
		_ = PushStack(domain, NotificationDiskQuota, n)
	})
}

// PushStack creates and sends a new notification where the source is the stack.
func PushStack(domain string, category string, n *notification.Notification) error {
	inst, err := lifecycle.GetInstance(domain)
	if err != nil {
		return err
	}
	n.Originator = "stack"
	n.Category = category
	p := stackNotifications[category]
	if p == nil {
		return ErrCategoryNotFound
	}
	return makePush(inst, p, n)
}

// Push creates and sends a new notification in database. This method verifies
// the permissions associated with this creation in order to check that it is
// granted to create a notification and to extract its source.
func Push(inst *instance.Instance, perm *permission.Permission, n *notification.Notification) error {
	if n.Title == "" {
		return ErrBadNotification
	}

	var p notification.Properties
	switch perm.Type {
	case permission.TypeOauth:
		c, ok := perm.Client.(*oauth.Client)
		if !ok {
			return ErrUnauthorized
		}
		n.Slug = ""
		if slug := oauth.GetLinkedAppSlug(c.SoftwareID); slug != "" {
			n.Slug = slug
			m, err := app.GetWebappBySlug(inst, slug)
			if err != nil || m.Notifications == nil {
				return err
			}
			p, ok = m.Notifications[n.Category]
		} else if c.Notifications != nil {
			p, ok = c.Notifications[n.Category]
		}
		if !ok {
			return ErrUnauthorized
		}
		n.Originator = "oauth"
	case permission.TypeWebapp:
		slug := strings.TrimPrefix(perm.SourceID, consts.Apps+"/")
		m, err := app.GetWebappBySlug(inst, slug)
		if err != nil || m.Notifications == nil {
			return err
		}
		var ok bool
		p, ok = m.Notifications[n.Category]
		if !ok {
			return ErrUnauthorized
		}
		n.Slug = m.Slug()
		n.Originator = "app"
	case permission.TypeKonnector:
		slug := strings.TrimPrefix(perm.SourceID, consts.Apps+"/")
		m, err := app.GetKonnectorBySlug(inst, slug)
		if err != nil || m.Notifications == nil {
			return err
		}
		var ok bool
		p, ok = m.Notifications[n.Category]
		if !ok {
			return ErrUnauthorized
		}
		n.Slug = m.Slug()
		n.Originator = "konnector"
	default:
		return ErrUnauthorized
	}

	return makePush(inst, &p, n)
}

func makePush(inst *instance.Instance, p *notification.Properties, n *notification.Notification) error {
	lastSent := time.Now()
	skipNotification := false

	// XXX: for retro-compatibility, we do not yet block applications from
	// sending notification from unknown category.
	if p != nil && p.Stateful {
		last, err := findLastNotification(inst, n.Source())
		if err != nil {
			return err
		}
		// when the state is the same for the last notification from this source,
		// we do not bother sending or creating a new notification.
		if last != nil {
			if last.State == n.State {
				inst.Logger().WithField("nspace", "notifications").
					Debugf("Notification %v was not sent (collapsed by same state %s)", p, n.State)
				return nil
			}
			if p.MinInterval > 0 && time.Until(last.LastSent) <= p.MinInterval {
				skipNotification = true
			}
		}

		if p.Stateful && !skipNotification {
			if b, ok := n.State.(bool); ok && !b {
				skipNotification = true
			} else if i, ok := n.State.(int); ok && i == 0 {
				skipNotification = true
			}
		}

		if skipNotification && last != nil {
			lastSent = last.LastSent
		}
	}

	preferredChannels := ensureMailFallback(n.PreferredChannels)
	at := n.At

	n.NID = ""
	n.NRev = ""
	n.SourceID = n.Source()
	n.CreatedAt = time.Now()
	n.LastSent = lastSent
	n.PreferredChannels = nil
	n.At = ""

	if err := couchdb.CreateDoc(inst, n); err != nil {
		return err
	}
	if skipNotification {
		return nil
	}

	var errm error
	log := inst.Logger().WithField("nspace", "notifications")
	for _, channel := range preferredChannels {
		switch channel {
		case "mobile":
			if p != nil {
				log.Infof("Sending push %#v: %v", p, n.State)
				err := sendPush(inst, p, n, at)
				if err == nil {
					return nil
				}
				log.Errorf("Error while sending push %#v: %v", p, n.State)
				errm = multierror.Append(errm, err)
			}
		case "mail":
			err := sendMail(inst, p, n, at)
			if err == nil {
				return nil
			}
			errm = multierror.Append(errm, err)
		case "sms":
			log.Infof("Sending SMS: %v", n.State)
			err := sendSMS(inst, p, n, at)
			if err == nil {
				return nil
			}
			log.Errorf("Error while sending sms: %s", err)
			errm = multierror.Append(errm, err)
		default:
			err := fmt.Errorf("Unknown channel for notification: %s", channel)
			errm = multierror.Append(errm, err)
		}
	}
	return errm
}

func findLastNotification(inst *instance.Instance, source string) (*notification.Notification, error) {
	var notifs []*notification.Notification
	req := &couchdb.FindRequest{
		UseIndex: "by-source-id",
		Selector: mango.Equal("source_id", source),
		Sort: mango.SortBy{
			{Field: "source_id", Direction: mango.Desc},
			{Field: "created_at", Direction: mango.Desc},
		},
		Limit: 1,
	}
	err := couchdb.FindDocs(inst, consts.Notifications, req, &notifs)
	if err != nil {
		return nil, err
	}
	if len(notifs) == 0 {
		return nil, nil
	}
	return notifs[0], nil
}

func sendPush(inst *instance.Instance,
	p *notification.Properties,
	n *notification.Notification,
	at string,
) error {
	if !hasNotifiableDevice(inst) {
		return errors.New("No device with push notification")
	}
	email := buildMailMessage(p, n)
	push := PushMessage{
		NotificationID: n.ID(),
		Source:         n.Source(),
		Title:          n.Title,
		Message:        n.Message,
		Priority:       n.Priority,
		Sound:          n.Sound,
		Data:           n.Data,
		Collapsible:    p.Collapsible,
		MailFallback:   email,
	}
	msg, err := job.NewMessage(&push)
	if err != nil {
		return err
	}
	return pushJobOrTrigger(inst, msg, "push", at)
}

func sendMail(inst *instance.Instance,
	p *notification.Properties,
	n *notification.Notification,
	at string,
) error {
	email := buildMailMessage(p, n)
	if email == nil {
		return nil
	}
	msg, err := job.NewMessage(&email)
	if err != nil {
		return err
	}
	return pushJobOrTrigger(inst, msg, "sendmail", at)
}

func sendSMS(inst *instance.Instance,
	p *notification.Properties,
	n *notification.Notification,
	at string,
) error {
	email := buildMailMessage(p, n)
	msg, err := job.NewMessage(&SMS{
		NotificationID: n.ID(),
		Message:        n.Message,
		MailFallback:   email,
	})
	if err != nil {
		return err
	}
	return pushJobOrTrigger(inst, msg, "sms", at)
}

func buildMailMessage(p *notification.Properties, n *notification.Notification) *mail.Options {
	email := mail.Options{Mode: mail.ModeFromStack}

	// Notifications from the stack have their own mail templates defined
	if p != nil && p.MailTemplate != "" {
		email.TemplateName = p.MailTemplate
		email.TemplateValues = n.Data
	} else if n.ContentHTML != "" {
		email.Subject = n.Title
		email.Parts = make([]*mail.Part, 0, 2)
		if n.Content != "" {
			email.Parts = append(email.Parts,
				&mail.Part{Body: n.Content, Type: "text/plain"})
		}
		if n.ContentHTML != "" {
			email.Parts = append(email.Parts,
				&mail.Part{Body: n.ContentHTML, Type: "text/html"})
		}
	} else {
		return nil
	}

	return &email
}

func pushJobOrTrigger(inst *instance.Instance, msg job.Message, worker, at string) error {
	if at == "" {
		_, err := job.System().PushJob(inst, &job.JobRequest{
			WorkerType: worker,
			Message:    msg,
		})
		return err
	}
	t, err := job.NewTrigger(inst, job.TriggerInfos{
		Type:       "@at",
		WorkerType: worker,
		Arguments:  at,
	}, msg)
	if err != nil {
		return err
	}
	return job.System().AddTrigger(t)
}

func ensureMailFallback(channels []string) []string {
	for _, c := range channels {
		if c == "mail" {
			return channels
		}
	}
	return append(channels, "mail")
}

func hasNotifiableDevice(inst *instance.Instance) bool {
	cs, err := oauth.GetNotifiables(inst)
	return err == nil && len(cs) > 0
}
<commit_msg>fix: Do not return err if m.Notifications is nil (#2781)<commit_after>package center

import (
	"errors"
	"fmt"
	"strings"
	"time"

	"github.com/cozy/cozy-stack/model/app"
	"github.com/cozy/cozy-stack/model/instance"
	"github.com/cozy/cozy-stack/model/instance/lifecycle"
	"github.com/cozy/cozy-stack/model/job"
	"github.com/cozy/cozy-stack/model/notification"
	"github.com/cozy/cozy-stack/model/oauth"
	"github.com/cozy/cozy-stack/model/permission"
	"github.com/cozy/cozy-stack/model/vfs"
	"github.com/cozy/cozy-stack/pkg/consts"
	"github.com/cozy/cozy-stack/pkg/couchdb"
	"github.com/cozy/cozy-stack/pkg/couchdb/mango"
	"github.com/cozy/cozy-stack/pkg/mail"
	multierror "github.com/hashicorp/go-multierror"
)

const (
	// NotificationDiskQuota category for sending alert when reaching 90% of disk
	// usage quota.
	NotificationDiskQuota = "disk-quota"
)

var (
	stackNotifications = map[string]*notification.Properties{
		NotificationDiskQuota: {
			Description:  "Warn about the diskquota reaching a high level",
			Collapsible:  true,
			Stateful:     true,
			MailTemplate: "notifications_diskquota",
			MinInterval:  7 * 24 * time.Hour,
		},
	}
)

func init() {
	vfs.RegisterDiskQuotaAlertCallback(func(domain string, exceeded bool) {
		i, err := lifecycle.GetInstance(domain)
		if err != nil {
			return
		}
		offersLink, err := i.ManagerURL(instance.ManagerPremiumURL)
		if err != nil {
			return
		}
		cozyDriveLink := i.SubDomain(consts.DriveSlug)
		n := &notification.Notification{
			State: exceeded,
			Data: map[string]interface{}{
				"OffersLink":    offersLink,
				"CozyDriveLink": cozyDriveLink.String(),
			},
		}
		_ = PushStack(domain, NotificationDiskQuota, n)
	})
}

// PushStack creates and sends a new notification where the source is the stack.
func PushStack(domain string, category string, n *notification.Notification) error {
	inst, err := lifecycle.GetInstance(domain)
	if err != nil {
		return err
	}
	n.Originator = "stack"
	n.Category = category
	p := stackNotifications[category]
	if p == nil {
		return ErrCategoryNotFound
	}
	return makePush(inst, p, n)
}

// Push creates and sends a new notification in database. This method verifies
// the permissions associated with this creation in order to check that it is
// granted to create a notification and to extract its source.
func Push(inst *instance.Instance, perm *permission.Permission, n *notification.Notification) error {
	if n.Title == "" {
		return ErrBadNotification
	}

	var p notification.Properties
	switch perm.Type {
	case permission.TypeOauth:
		c, ok := perm.Client.(*oauth.Client)
		if !ok {
			return ErrUnauthorized
		}
		n.Slug = ""
		if slug := oauth.GetLinkedAppSlug(c.SoftwareID); slug != "" {
			n.Slug = slug
			m, err := app.GetWebappBySlug(inst, slug)
			if err != nil || m.Notifications == nil {
				return err
			}
			p, ok = m.Notifications[n.Category]
		} else if c.Notifications != nil {
			p, ok = c.Notifications[n.Category]
		}
		if !ok {
			return ErrUnauthorized
		}
		n.Originator = "oauth"
	case permission.TypeWebapp:
		slug := strings.TrimPrefix(perm.SourceID, consts.Apps+"/")
		m, err := app.GetWebappBySlug(inst, slug)
		if err != nil {
			return err
		}
		if m.Notifications == nil {
			return ErrUnauthorized
		}
		var ok bool
		p, ok = m.Notifications[n.Category]
		if !ok {
			return ErrUnauthorized
		}
		n.Slug = m.Slug()
		n.Originator = "app"
	case permission.TypeKonnector:
		slug := strings.TrimPrefix(perm.SourceID, consts.Apps+"/")
		m, err := app.GetKonnectorBySlug(inst, slug)
		if err != nil {
			return err
		}
		if m.Notifications == nil {
			return ErrUnauthorized
		}
		var ok bool
		p, ok = m.Notifications[n.Category]
		if !ok {
			return ErrUnauthorized
		}
		n.Slug = m.Slug()
		n.Originator = "konnector"
	default:
		return ErrUnauthorized
	}

	return makePush(inst, &p, n)
}

func makePush(inst *instance.Instance, p *notification.Properties, n *notification.Notification) error {
	lastSent := time.Now()
	skipNotification := false

	// XXX: for retro-compatibility, we do not yet block applications from
	// sending notification from unknown category.
	if p != nil && p.Stateful {
		last, err := findLastNotification(inst, n.Source())
		if err != nil {
			return err
		}
		// when the state is the same for the last notification from this source,
		// we do not bother sending or creating a new notification.
		if last != nil {
			if last.State == n.State {
				inst.Logger().WithField("nspace", "notifications").
					Debugf("Notification %v was not sent (collapsed by same state %s)", p, n.State)
				return nil
			}
			if p.MinInterval > 0 && time.Until(last.LastSent) <= p.MinInterval {
				skipNotification = true
			}
		}

		if p.Stateful && !skipNotification {
			if b, ok := n.State.(bool); ok && !b {
				skipNotification = true
			} else if i, ok := n.State.(int); ok && i == 0 {
				skipNotification = true
			}
		}

		if skipNotification && last != nil {
			lastSent = last.LastSent
		}
	}

	preferredChannels := ensureMailFallback(n.PreferredChannels)
	at := n.At

	n.NID = ""
	n.NRev = ""
	n.SourceID = n.Source()
	n.CreatedAt = time.Now()
	n.LastSent = lastSent
	n.PreferredChannels = nil
	n.At = ""

	if err := couchdb.CreateDoc(inst, n); err != nil {
		return err
	}
	if skipNotification {
		return nil
	}

	var errm error
	log := inst.Logger().WithField("nspace", "notifications")
	for _, channel := range preferredChannels {
		switch channel {
		case "mobile":
			if p != nil {
				log.Infof("Sending push %#v: %v", p, n.State)
				err := sendPush(inst, p, n, at)
				if err == nil {
					return nil
				}
				log.Errorf("Error while sending push %#v: %v", p, n.State)
				errm = multierror.Append(errm, err)
			}
		case "mail":
			err := sendMail(inst, p, n, at)
			if err == nil {
				return nil
			}
			errm = multierror.Append(errm, err)
		case "sms":
			log.Infof("Sending SMS: %v", n.State)
			err := sendSMS(inst, p, n, at)
			if err == nil {
				return nil
			}
			log.Errorf("Error while sending sms: %s", err)
			errm = multierror.Append(errm, err)
		default:
			err := fmt.Errorf("Unknown channel for notification: %s", channel)
			errm = multierror.Append(errm, err)
		}
	}
	return errm
}

func findLastNotification(inst *instance.Instance, source string) (*notification.Notification, error) {
	var notifs []*notification.Notification
	req := &couchdb.FindRequest{
		UseIndex: "by-source-id",
		Selector: mango.Equal("source_id", source),
		Sort: mango.SortBy{
			{Field: "source_id", Direction: mango.Desc},
			{Field: "created_at", Direction: mango.Desc},
		},
		Limit: 1,
	}
	err := couchdb.FindDocs(inst, consts.Notifications, req, &notifs)
	if err != nil {
		return nil, err
	}
	if len(notifs) == 0 {
		return nil, nil
	}
	return notifs[0], nil
}

func sendPush(inst *instance.Instance,
	p *notification.Properties,
	n *notification.Notification,
	at string,
) error {
	if !hasNotifiableDevice(inst) {
		return errors.New("No device with push notification")
	}
	email := buildMailMessage(p, n)
	push := PushMessage{
		NotificationID: n.ID(),
		Source:         n.Source(),
		Title:          n.Title,
		Message:        n.Message,
		Priority:       n.Priority,
		Sound:          n.Sound,
		Data:           n.Data,
		Collapsible:    p.Collapsible,
		MailFallback:   email,
	}
	msg, err := job.NewMessage(&push)
	if err != nil {
		return err
	}
	return pushJobOrTrigger(inst, msg, "push", at)
}

func sendMail(inst *instance.Instance,
	p *notification.Properties,
	n *notification.Notification,
	at string,
) error {
	email := buildMailMessage(p, n)
	if email == nil {
		return nil
	}
	msg, err := job.NewMessage(&email)
	if err != nil {
		return err
	}
	return pushJobOrTrigger(inst, msg, "sendmail", at)
}

func sendSMS(inst *instance.Instance,
	p *notification.Properties,
	n *notification.Notification,
	at string,
) error {
	email := buildMailMessage(p, n)
	msg, err := job.NewMessage(&SMS{
		NotificationID: n.ID(),
		Message:        n.Message,
		MailFallback:   email,
	})
	if err != nil {
		return err
	}
	return pushJobOrTrigger(inst, msg, "sms", at)
}

func buildMailMessage(p *notification.Properties, n *notification.Notification) *mail.Options {
	email := mail.Options{Mode: mail.ModeFromStack}

	// Notifications from the stack have their own mail templates defined
	if p != nil && p.MailTemplate != "" {
		email.TemplateName = p.MailTemplate
		email.TemplateValues = n.Data
	} else if n.ContentHTML != "" {
		email.Subject = n.Title
		email.Parts = make([]*mail.Part, 0, 2)
		if n.Content != "" {
			email.Parts = append(email.Parts,
				&mail.Part{Body: n.Content, Type: "text/plain"})
		}
		if n.ContentHTML != "" {
			email.Parts = append(email.Parts,
				&mail.Part{Body: n.ContentHTML, Type: "text/html"})
		}
	} else {
		return nil
	}

	return &email
}

func pushJobOrTrigger(inst *instance.Instance, msg job.Message, worker, at string) error {
	if at == "" {
		_, err := job.System().PushJob(inst, &job.JobRequest{
			WorkerType: worker,
			Message:    msg,
		})
		return err
	}
	t, err := job.NewTrigger(inst, job.TriggerInfos{
		Type:       "@at",
		WorkerType: worker,
		Arguments:  at,
	}, msg)
	if err != nil {
		return err
	}
	return job.System().AddTrigger(t)
}

func ensureMailFallback(channels []string) []string {
	for _, c := range channels {
		if c == "mail" {
			return channels
		}
	}
	return append(channels, "mail")
}

func hasNotifiableDevice(inst *instance.Instance) bool {
	cs, err := oauth.GetNotifiables(inst)
	return err == nil && len(cs) > 0
}
<|endoftext|>


<commit_before>// File: ./blockfreight/bft/bf_tx/bf_tx.go
// Summary: Application code for Blockfreight™ | The blockchain of global freight.
// License: MIT License
// Company: Blockfreight, Inc.
// Author: Julian Nunez, Neil Tran, Julian Smith & contributors
// Site: https://blockfreight.com
// Support: <support@blockfreight.com>

// Copyright 2017 Blockfreight, Inc.

// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

// =================================================================================================================================================
// =================================================================================================================================================
//
// BBBBBBBBBBBb     lll                                kkk             ffff                         iii                  hhh            ttt
// BBBB``````BBBB   lll                                kkk            fff                           ```                  hhh            ttt
// BBBB      BBBB   lll      oooooo        ccccccc     kkk    kkkk  fffffff  rrr  rrr    eeeee      iii     gggggg ggg   hhh  hhhhh   tttttttt
// BBBBBBBBBBBB     lll    ooo    oooo    ccc    ccc   kkk   kkk    fffffff  rrrrrrrr eee    eeee   iii   gggg   ggggg   hhhh   hhhh  tttttttt
// BBBBBBBBBBBBBB   lll   ooo      ooo   ccc           kkkkkkk        fff    rrrr    eeeeeeeeeeeee  iii  gggg      ggg   hhh     hhh    ttt
// BBBB       BBB   lll   ooo      ooo   ccc           kkkk kkkk      fff    rrr     eeeeeeeeeeeee  iii   ggg      ggg   hhh     hhh    ttt
// BBBB      BBBB   lll   oooo    oooo   cccc    ccc   kkk   kkkk     fff    rrr      eee      eee  iii    ggg    gggg   hhh     hhh    tttt    ....
// BBBBBBBBBBBBB    lll     oooooooo       ccccccc     kkk     kkkk   fff    rrr       eeeeeeeee    iii     gggggg ggg   hhh     hhh     ttttt  ....
//                                                                                ggg      ggg
//   Blockfreight™ | The blockchain of global freight.                                                      ggggggggg
//
// =================================================================================================================================================
// =================================================================================================================================================

// Package bf_tx is a package that defines the Blockfreight™ Transaction (BF_TX) transaction standard
// and provides some useful functions to work with the BF_TX.
package bf_tx

import (
    // =======================
    // Golang Standard library
    // =======================
    "crypto/ecdsa"      // Implements the Elliptic Curve Digital Signature Algorithm, as defined in FIPS 186-3.
    "encoding/json"     // Implements encoding and decoding of JSON as defined in RFC 4627.

    // ====================
    // Third-party packages
    // ====================
    "github.com/davecgh/go-spew/spew"   // Implements a deep pretty printer for Go data structures to aid in debugging.

    // ======================
    // Blockfreight™ packages
    // ======================
    "github.com/blockfreight/blockfreight-alpha/blockfreight/bft/common"    // Implements common functions for Blockfreight™
)

// SetBF_TX receives the path of a JSON, reads it and returns the BF_TX structure with all attributes.
func SetBF_TX(jsonpath string) (BF_TX, error) {
    var bf_tx BF_TX
    file, err := common.ReadJSON(jsonpath)
    if err != nil {
        return bf_tx, err
    }
    json.Unmarshal(file, &bf_tx)
    return bf_tx, nil
}

// BF_TXContent receives the BF_TX structure, applies it the json.Marshal procedure and return the content of the BF_TX JSON.
func BF_TXContent(bf_tx BF_TX) (string, error) {
    jsonContent, err := json.Marshal(bf_tx)
    return string(jsonContent), err
}

// PrintBF_TX receives a BF_TX and prints it clearly.
func PrintBF_TX(bf_tx BF_TX){
    spew.Dump(bf_tx)
}

// State reports the current state of a BF_TX
func State(bf_tx BF_TX) string {
    if(bf_tx.Transmitted){
        return "Transmitted!"
    } else if(bf_tx.Verified){
        return "Signed!"
    } else {
        return "Constructed!"
    }
}

// BF_TX structure respresents an logical abstraction of a Blockfreight™ Transaction.
type BF_TX struct {
    // =========================
    // Bill of Lading attributes
    // =========================
    Type       string
    Properties Properties

    // ===================================
    // Blockfreight Transaction attributes
    // ===================================
    Id          int
    PrivateKey  ecdsa.PrivateKey
    Signhash    []uint8
    Signature   string
    Verified    bool
    Transmitted bool
    Amendment   int
}

type Properties struct {
    Shipper              Shipper
    Bol_Num              BolNum
    Ref_Num              RefNum
    Consignee            Consignee
    Vessel               Vessel
    Port_of_Loading      PortLoading
    Port_of_Discharge    PortDischarge
    Notify_Address       NotifyAddress
    Desc_of_Goods        DescGoods
    Gross_Weight         GrossWeight
    Freight_Payable_Amt  FreightPayableAmt
    Freight_Adv_Amt      FreightAdvAmt
    General_Instructions GeneralInstructions
    Date_Shipped         Date
    Issue_Details        IssueDetails
    Num_Bol              NumBol
    Master_Info          MasterInfo
    Agent_for_Master     AgentMaster
    Agent_for_Owner      AgentOwner
}

type Shipper struct {
    Type string
}

type BolNum struct {
    Type int
}

type RefNum struct {
    Type int
}

type Consignee struct {
    Type string //Null
}

type Vessel struct {
    Type int
}

type PortLoading struct {
    Type int
}

type PortDischarge struct {
    Type int
}

type NotifyAddress struct {
    Type string
}

type DescGoods struct {
    Type string
}

type GrossWeight struct {
    Type int
}

type FreightPayableAmt struct {
    Type int
}

type FreightAdvAmt struct {
    Type int
}

type GeneralInstructions struct {
    Type string
}

type Date struct {
    Type   int
    Format string
}

type IssueDetails struct {
    Type       string
    Properties IssueDetailsProperties
}

type IssueDetailsProperties struct {
    Place_of_Issue PlaceIssue
    Date_of_Issue  Date
}

type PlaceIssue struct {
    Type string
}

type NumBol struct {
    Type int
}

type MasterInfo struct {
    Type       string
    Properties MasterInfoProperties
}

type MasterInfoProperties struct {
    First_Name FirstName
    Last_Name  LastName
    Sig        Sig
}

type AgentMaster struct {
    Type       string
    Properties AgentMasterProperties
}

type AgentMasterProperties struct {
    First_Name FirstName
    Last_Name  LastName
    Sig        Sig
}

type AgentOwner struct {
    Type       string
    Properties AgentOwnerProperties
}

type AgentOwnerProperties struct {
    First_Name              FirstName
    Last_Name               LastName
    Sig                     Sig
    Conditions_for_Carriage ConditionsCarriage
}

type FirstName struct {
    Type string
}

type LastName struct {
    Type string
}

type Sig struct {
    Type string
}

type ConditionsCarriage struct {
    Type string
}

// =================================================
// Blockfreight™ | The blockchain of global freight.
// =================================================

// BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
// BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
// BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
// BBBBBBB                    BBBBBBBBBBBBBBBBBBB
// BBBBBBB                       BBBBBBBBBBBBBBBB
// BBBBBBB                        BBBBBBBBBBBBBBB
// BBBBBBB       BBBBBBBBB        BBBBBBBBBBBBBBB
// BBBBBBB       BBBBBBBBB        BBBBBBBBBBBBBBB
// BBBBBBB       BBBBBBB         BBBBBBBBBBBBBBBB
// BBBBBBB                     BBBBBBBBBBBBBBBBBB
// BBBBBBB                        BBBBBBBBBBBBBBB
// BBBBBBB       BBBBBBBBBB        BBBBBBBBBBBBBB
// BBBBBBB       BBBBBBBBBBB       BBBBBBBBBBBBBB
// BBBBBBB       BBBBBBBBBB        BBBBBBBBBBBBBB
// BBBBBBB       BBBBBBBBB        BBB       BBBBB
// BBBBBBB                       BBBB       BBBBB
// BBBBBBB                    BBBBBBB       BBBBB
// BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
// BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
// BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB

// ==================================================
// Blockfreight™ | The blockchain for global freight.
// ==================================================
<commit_msg>Create Reinitialize function to set the default value to BF_TX attributes<commit_after>// File: ./blockfreight/bft/bf_tx/bf_tx.go
// Summary: Application code for Blockfreight™ | The blockchain of global freight.
// License: MIT License
// Company: Blockfreight, Inc.
// Author: Julian Nunez, Neil Tran, Julian Smith & contributors
// Site: https://blockfreight.com
// Support: <support@blockfreight.com>

// Copyright 2017 Blockfreight, Inc.

// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

// =================================================================================================================================================
// =================================================================================================================================================
//
// BBBBBBBBBBBb     lll                                kkk             ffff                         iii                  hhh            ttt
// BBBB``````BBBB   lll                                kkk            fff                           ```                  hhh            ttt
// BBBB      BBBB   lll      oooooo        ccccccc     kkk    kkkk  fffffff  rrr  rrr    eeeee      iii     gggggg ggg   hhh  hhhhh   tttttttt
// BBBBBBBBBBBB     lll    ooo    oooo    ccc    ccc   kkk   kkk    fffffff  rrrrrrrr eee    eeee   iii   gggg   ggggg   hhhh   hhhh  tttttttt
// BBBBBBBBBBBBBB   lll   ooo      ooo   ccc           kkkkkkk        fff    rrrr    eeeeeeeeeeeee  iii  gggg      ggg   hhh     hhh    ttt
// BBBB       BBB   lll   ooo      ooo   ccc           kkkk kkkk      fff    rrr     eeeeeeeeeeeee  iii   ggg      ggg   hhh     hhh    ttt
// BBBB      BBBB   lll   oooo    oooo   cccc    ccc   kkk   kkkk     fff    rrr      eee      eee  iii    ggg    gggg   hhh     hhh    tttt    ....
// BBBBBBBBBBBBB    lll     oooooooo       ccccccc     kkk     kkkk   fff    rrr       eeeeeeeee    iii     gggggg ggg   hhh     hhh     ttttt  ....
//                                                                                ggg      ggg
//   Blockfreight™ | The blockchain of global freight.                                                      ggggggggg
//
// =================================================================================================================================================
// =================================================================================================================================================

// Package bf_tx is a package that defines the Blockfreight™ Transaction (BF_TX) transaction standard
// and provides some useful functions to work with the BF_TX.
package bf_tx

import (
    // =======================
    // Golang Standard library
    // =======================
    "crypto/ecdsa"      // Implements the Elliptic Curve Digital Signature Algorithm, as defined in FIPS 186-3.
    "encoding/json"     // Implements encoding and decoding of JSON as defined in RFC 4627.

    // ====================
    // Third-party packages
    // ====================
    "github.com/davecgh/go-spew/spew"   // Implements a deep pretty printer for Go data structures to aid in debugging.

    // ======================
    // Blockfreight™ packages
    // ======================
    "github.com/blockfreight/blockfreight-alpha/blockfreight/bft/common"    // Implements common functions for Blockfreight™
)

// SetBF_TX receives the path of a JSON, reads it and returns the BF_TX structure with all attributes.
func SetBF_TX(jsonpath string) (BF_TX, error) {
    var bf_tx BF_TX
    file, err := common.ReadJSON(jsonpath)
    if err != nil {
        return bf_tx, err
    }
    json.Unmarshal(file, &bf_tx)
    return bf_tx, nil
}

// BF_TXContent receives the BF_TX structure, applies it the json.Marshal procedure and return the content of the BF_TX JSON.
func BF_TXContent(bf_tx BF_TX) (string, error) {
    jsonContent, err := json.Marshal(bf_tx)
    return string(jsonContent), err
}

// PrintBF_TX receives a BF_TX and prints it clearly.
func PrintBF_TX(bf_tx BF_TX){
    spew.Dump(bf_tx)
}

// State reports the current state of a BF_TX
func State(bf_tx BF_TX) string {
    if(bf_tx.Transmitted){
        return "Transmitted!"
    } else if(bf_tx.Verified){
        return "Signed!"
    } else {
        return "Constructed!"
    }
}

// Reinitialize set the default values to the Blockfreight attributes of BF_TX
func Reinitialize(bf_tx BF_TX) BF_TX {
    bf_tx.Id = 0
    bf_tx.PrivateKey.Curve = nil
    bf_tx.PrivateKey.X = nil
    bf_tx.PrivateKey.Y = nil
    bf_tx.PrivateKey.D = nil
    bf_tx.Signhash = nil
    bf_tx.Signature = ""
    bf_tx.Verified = false
    bf_tx.Transmitted = false
    bf_tx.Amendment = 0
    return bf_tx
}

// BF_TX structure respresents an logical abstraction of a Blockfreight™ Transaction.
type BF_TX struct {
    // =========================
    // Bill of Lading attributes
    // =========================
    Type       string
    Properties Properties

    // ===================================
    // Blockfreight Transaction attributes
    // ===================================
    Id          int
    PrivateKey  ecdsa.PrivateKey
    Signhash    []uint8
    Signature   string
    Verified    bool
    Transmitted bool
    Amendment   int
}

type Properties struct {
    Shipper              Shipper
    Bol_Num              BolNum
    Ref_Num              RefNum
    Consignee            Consignee
    Vessel               Vessel
    Port_of_Loading      PortLoading
    Port_of_Discharge    PortDischarge
    Notify_Address       NotifyAddress
    Desc_of_Goods        DescGoods
    Gross_Weight         GrossWeight
    Freight_Payable_Amt  FreightPayableAmt
    Freight_Adv_Amt      FreightAdvAmt
    General_Instructions GeneralInstructions
    Date_Shipped         Date
    Issue_Details        IssueDetails
    Num_Bol              NumBol
    Master_Info          MasterInfo
    Agent_for_Master     AgentMaster
    Agent_for_Owner      AgentOwner
}

type Shipper struct {
    Type string
}

type BolNum struct {
    Type int
}

type RefNum struct {
    Type int
}

type Consignee struct {
    Type string //Null
}

type Vessel struct {
    Type int
}

type PortLoading struct {
    Type int
}

type PortDischarge struct {
    Type int
}

type NotifyAddress struct {
    Type string
}

type DescGoods struct {
    Type string
}

type GrossWeight struct {
    Type int
}

type FreightPayableAmt struct {
    Type int
}

type FreightAdvAmt struct {
    Type int
}

type GeneralInstructions struct {
    Type string
}

type Date struct {
    Type   int
    Format string
}

type IssueDetails struct {
    Type       string
    Properties IssueDetailsProperties
}

type IssueDetailsProperties struct {
    Place_of_Issue PlaceIssue
    Date_of_Issue  Date
}

type PlaceIssue struct {
    Type string
}

type NumBol struct {
    Type int
}

type MasterInfo struct {
    Type       string
    Properties MasterInfoProperties
}

type MasterInfoProperties struct {
    First_Name FirstName
    Last_Name  LastName
    Sig        Sig
}

type AgentMaster struct {
    Type       string
    Properties AgentMasterProperties
}

type AgentMasterProperties struct {
    First_Name FirstName
    Last_Name  LastName
    Sig        Sig
}

type AgentOwner struct {
    Type       string
    Properties AgentOwnerProperties
}

type AgentOwnerProperties struct {
    First_Name              FirstName
    Last_Name               LastName
    Sig                     Sig
    Conditions_for_Carriage ConditionsCarriage
}

type FirstName struct {
    Type string
}

type LastName struct {
    Type string
}

type Sig struct {
    Type string
}

type ConditionsCarriage struct {
    Type string
}

// =================================================
// Blockfreight™ | The blockchain of global freight.
// =================================================

// BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
// BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
// BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
// BBBBBBB                    BBBBBBBBBBBBBBBBBBB
// BBBBBBB                       BBBBBBBBBBBBBBBB
// BBBBBBB                        BBBBBBBBBBBBBBB
// BBBBBBB       BBBBBBBBB        BBBBBBBBBBBBBBB
// BBBBBBB       BBBBBBBBB        BBBBBBBBBBBBBBB
// BBBBBBB       BBBBBBB         BBBBBBBBBBBBBBBB
// BBBBBBB                     BBBBBBBBBBBBBBBBBB
// BBBBBBB                        BBBBBBBBBBBBBBB
// BBBBBBB       BBBBBBBBBB        BBBBBBBBBBBBBB
// BBBBBBB       BBBBBBBBBBB       BBBBBBBBBBBBBB
// BBBBBBB       BBBBBBBBBB        BBBBBBBBBBBBBB
// BBBBBBB       BBBBBBBBB        BBB       BBBBB
// BBBBBBB                       BBBB       BBBBB
// BBBBBBB                    BBBBBBB       BBBBB
// BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
// BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
// BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB

// ==================================================
// Blockfreight™ | The blockchain for global freight.
// ==================================================
<|endoftext|>


<commit_before>package config

import (
	"crypto/tls"
	"errors"
	"path/filepath"
)

// ACME is the struct which holds the Let's Encrypt options of the database
type ACME struct {
	Enabled      bool     `json:"enabled"`
	Server       string   `json:"server"`
	PrivateKey   string   `json:"private_key"`
	Registration string   `json:"registration"`
	Domains      []string `json:"domains"`
	TOSAgree     bool     `json:"tos_agree"`
}

// TLS enables TLS support on the server
type TLS struct {
	Enabled bool   `json:"enabled"`
	Key     string `json:"key"`
	Cert    string `json:"cert"`

	ACME ACME `json:"acme"`
}

// Validate ensures that the TLS configuration is OK
func (t *TLS) Validate() (err error) {
	if t.Enabled {
		if t.Key == "" || t.Cert == "" {
			return errors.New("TLS key or cert was not given")
		}
		//Set the file paths to be full paths
		t.Cert, err = filepath.Abs(t.Cert)
		if err != nil {
			return err
		}
		t.Key, err = filepath.Abs(t.Key)
		if err != nil {
			return err
		}

		if t.ACME.Enabled {

			if t.ACME.PrivateKey == "" || t.ACME.Registration != "" {
				return errors.New("ACME registration and private key files not given")
			}

			t.ACME.PrivateKey, err = filepath.Abs(t.ACME.PrivateKey)
			if err != nil {
				return err
			}
			t.ACME.Registration, err = filepath.Abs(t.ACME.Registration)
			if err != nil {
				return err
			}

			if t.ACME.Server == "" {
				return errors.New("ACME server not given")
			}
			if len(t.ACME.Domains) == 0 {
				return errors.New("ACME requires a valid list of domains for certificate")
			}
			if !t.ACME.TOSAgree {
				return errors.New("Must agree to the TOS of your ACME server.")
			}

		} else {
			// If ACME is not on, we require that the key/cert exist already
			_, err = tls.LoadX509KeyPair(t.Cert, t.Key)
			if err != nil {
				return err
			}
		}

		//

	}
	return nil
}
<commit_msg>Fixed acme error bug<commit_after>package config

import (
	"crypto/tls"
	"errors"
	"path/filepath"
)

// ACME is the struct which holds the Let's Encrypt options of the database
type ACME struct {
	Enabled      bool     `json:"enabled"`
	Server       string   `json:"server"`
	PrivateKey   string   `json:"private_key"`
	Registration string   `json:"registration"`
	Domains      []string `json:"domains"`
	TOSAgree     bool     `json:"tos_agree"`
}

// TLS enables TLS support on the server
type TLS struct {
	Enabled bool   `json:"enabled"`
	Key     string `json:"key"`
	Cert    string `json:"cert"`

	ACME ACME `json:"acme"`
}

// Validate ensures that the TLS configuration is OK
func (t *TLS) Validate() (err error) {
	if t.Enabled {
		if t.Key == "" || t.Cert == "" {
			return errors.New("TLS key or cert was not given")
		}
		//Set the file paths to be full paths
		t.Cert, err = filepath.Abs(t.Cert)
		if err != nil {
			return err
		}
		t.Key, err = filepath.Abs(t.Key)
		if err != nil {
			return err
		}

		if t.ACME.Enabled {

			if t.ACME.PrivateKey == "" || t.ACME.Registration == "" {
				return errors.New("ACME registration and private key files not given")
			}

			t.ACME.PrivateKey, err = filepath.Abs(t.ACME.PrivateKey)
			if err != nil {
				return err
			}
			t.ACME.Registration, err = filepath.Abs(t.ACME.Registration)
			if err != nil {
				return err
			}

			if t.ACME.Server == "" {
				return errors.New("ACME server not given")
			}
			if len(t.ACME.Domains) == 0 {
				return errors.New("ACME requires a valid list of domains for certificate")
			}
			if !t.ACME.TOSAgree {
				return errors.New("Must agree to the TOS of your ACME server.")
			}

		} else {
			// If ACME is not on, we require that the key/cert exist already
			_, err = tls.LoadX509KeyPair(t.Cert, t.Key)
			if err != nil {
				return err
			}
		}

		//

	}
	return nil
}
<|endoftext|>