pull/10/head
Hunter Long 2018-06-22 21:17:57 -07:00
parent 8d8f25ea23
commit 68b42dcb8b
16 changed files with 285 additions and 94 deletions

View File

@ -10,6 +10,8 @@ import (
func CheckServices() {
services, _ = SelectAllServices()
core.Communications, _ = SelectAllCommunications()
LoadDefaultCommunications()
for _, v := range services {
obj := v
go obj.StartCheckins()

View File

@ -4,50 +4,74 @@ import (
"bytes"
"crypto/tls"
"fmt"
"github.com/hunterlong/statup/types"
"gopkg.in/gomail.v2"
"html/template"
"log"
"os"
"time"
)
var (
mailer *gomail.Dialer
Emailer *gomail.Dialer
Outgoing []*types.Email
)
func NewMailer() {
mailer = gomail.NewDialer(os.Getenv("HOST"), 587, os.Getenv("USER"), os.Getenv("PASS"))
mailer.TLSConfig = &tls.Config{InsecureSkipVerify: true}
func AddEmail(email *types.Email) {
Outgoing = append(Outgoing, email)
}
source := EmailTemplate("comms/templates/error.html", "this is coooool")
func EmailerQueue() {
defer EmailerQueue()
for _, out := range Outgoing {
fmt.Printf("sending email to: %v \n", out.To)
Send(out)
}
Outgoing = nil
fmt.Println("running emailer queue")
time.Sleep(10 * time.Second)
}
fmt.Println("source: ", source)
func Send(em *types.Email) {
source := EmailTemplate("comms/templates/error.html", nil)
m := gomail.NewMessage()
m.SetHeader("From", "info@betatude.com")
m.SetHeader("To", em.To)
m.SetHeader("Subject", em.Subject)
m.SetBody("text/html", source)
if err := Emailer.DialAndSend(m); err != nil {
fmt.Println(err)
}
}
func SendSample(em *types.Email) {
source := EmailTemplate("comms/templates/error.html", nil)
m := gomail.NewMessage()
m.SetHeader("From", "info@betatude.com")
m.SetHeader("To", em.To)
m.SetHeader("Subject", em.Subject)
m.SetBody("text/html", source)
if err := Emailer.DialAndSend(m); err != nil {
fmt.Println(err)
}
}
func LoadMailer(config *types.Communication) *gomail.Dialer {
Emailer = gomail.NewDialer(config.Host, config.Port, config.Username, config.Password)
Emailer.TLSConfig = &tls.Config{InsecureSkipVerify: true}
return Emailer
}
func EmailTemplate(tmpl string, data interface{}) string {
t := template.New("error.html")
var err error
t, err = t.ParseFiles(tmpl)
if err != nil {
panic(err)
}
var tpl bytes.Buffer
if err := t.Execute(&tpl, data); err != nil {
log.Println(err)
}
result := tpl.String()
return result
}
func SendEmail(to, subject, body string) {
m := gomail.NewMessage()
m.SetHeader("From", "info@email.com")
m.SetHeader("To", to)
m.SetHeader("Subject", subject)
m.SetBody("text/html", body)
if err := mailer.DialAndSend(m); err != nil {
fmt.Println(err)
}
}

View File

@ -1,15 +1,61 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Sample Email</title>
</head>
<body style="-webkit-text-size-adjust: none; box-sizing: border-box; color: #74787E; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; height: 100%; line-height: 1.4; margin: 0; width: 100% !important;" bgcolor="#F2F4F6"><style type="text/css">
body {
width: 100% !important; height: 100%; margin: 0; line-height: 1.4; background-color: #F2F4F6; color: #74787E; -webkit-text-size-adjust: none;
}
@media only screen and (max-width: 600px) {
.email-body_inner {
width: 100% !important;
}
.email-footer {
width: 100% !important;
}
}
@media only screen and (max-width: 500px) {
.button {
width: 100% !important;
}
}
</style>
<table class="email-wrapper" width="100%" cellpadding="0" cellspacing="0" style="box-sizing: border-box; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; margin: 0; padding: 0; width: 100%;" bgcolor="#F2F4F6">
<tr>
<td align="center" style="box-sizing: border-box; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; word-break: break-word;">
<table class="email-content" width="100%" cellpadding="0" cellspacing="0" style="box-sizing: border-box; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; margin: 0; padding: 0; width: 100%;">
<body>
<p>
<strong>Hello {{.}}</strong>
<tr>
<td class="email-body" width="100%" cellpadding="0" cellspacing="0" style="-premailer-cellpadding: 0; -premailer-cellspacing: 0; border-bottom-color: #EDEFF2; border-bottom-style: solid; border-bottom-width: 1px; border-top-color: #EDEFF2; border-top-style: solid; border-top-width: 1px; box-sizing: border-box; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; margin: 0; padding: 0; width: 100%; word-break: break-word;" bgcolor="#FFFFFF">
<table class="email-body_inner" align="center" width="570" cellpadding="0" cellspacing="0" style="box-sizing: border-box; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; margin: 0 auto; padding: 0; width: 570px;" bgcolor="#FFFFFF">
</p>
<tr>
<td class="content-cell" style="box-sizing: border-box; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; padding: 35px; word-break: break-word;">
<h1 style="box-sizing: border-box; color: #2F3133; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; font-size: 19px; font-weight: bold; margin-top: 0;" align="left">Looks like emails work!</h1>
<p style="box-sizing: border-box; color: #74787E; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; font-size: 16px; line-height: 1.5em; margin-top: 0;" align="left">
Since you got this email, it confirms that your Statup Status Page email system is working correctly.
</p>
</p>
<p style="box-sizing: border-box; color: #74787E; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; font-size: 16px; line-height: 1.5em; margin-top: 0;" align="left">
Enjoy using Statup!
<br />Statup.io Team</p>
<table class="body-sub" style="border-top-color: #EDEFF2; border-top-style: solid; border-top-width: 1px; box-sizing: border-box; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; margin-top: 25px; padding-top: 25px;">
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>

View File

@ -1,77 +1,75 @@
package main
import "time"
var (
Communications []*Communication
import (
"github.com/hunterlong/statup/comms"
"github.com/hunterlong/statup/types"
"time"
)
type Communication struct {
Id int64 `db:"id,omitempty" json:"id"`
Method string `db:"method" json:"method"`
Host string `db:"host" json:"host"`
Port int64 `db:"port" json:"port"`
User string `db:"user" json:"user"`
Password string `db:"password" json:"-"`
Var1 string `db:"var1" json:"var1"`
Var2 string `db:"var2" json:"var2"`
ApiKey string `db:"api_key" json:"api_key"`
ApiSecret string `db:"api_secret" json:"api_secret"`
Enabled bool `db:"enabled" json:"enabled"`
Limits int64 `db:"limits" json:"limits"`
Removable bool `db:"removable" json:"removable"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
func LoadDefaultCommunications() {
emailer := SelectCommunication(1)
comms.LoadMailer(emailer)
go comms.EmailerQueue()
}
func OnCommunicate() {
for _, c := range Communications {
func LoadComms() {
for _, c := range core.Communications {
if c.Enabled {
c.Run()
}
}
}
func (c *Communication) Run() {
func Run(c *types.Communication) {
sample := &types.Email{
To: "info@socialeck.com",
Subject: "Test Email from Statup",
}
comms.AddEmail(sample)
}
func SelectAllCommunications() ([]*Communication, error) {
var c []*Communication
func SelectAllCommunications() ([]*types.Communication, error) {
var c []*types.Communication
col := dbSession.Collection("communication").Find()
err := col.All(&c)
Communications = c
core.Communications = c
return c, err
}
func (c *Communication) Create() (int64, error) {
func Create(c *types.Communication) (int64, error) {
c.CreatedAt = time.Now()
uuid, err := dbSession.Collection("communication").Insert(c)
if err != nil {
panic(err)
}
if uuid == nil {
return 0, err
}
c.Id = uuid.(int64)
Communications = append(Communications, c)
core.Communications = append(core.Communications, c)
return uuid.(int64), err
}
func (c *Communication) Disable() {
func Disable(c *types.Communication) {
c.Enabled = false
c.Update()
Update(c)
}
func (c *Communication) Enable() {
func Enable(c *types.Communication) {
c.Enabled = true
c.Update()
Update(c)
}
func (c *Communication) Update() *Communication {
func Update(c *types.Communication) *types.Communication {
col := dbSession.Collection("communication").Find("id", c.Id)
col.Update(c)
return c
}
func SelectCommunication(id int64) *Communication {
for _, c := range Communications {
func SelectCommunication(id int64) *types.Communication {
for _, c := range core.Communications {
if c.Id == id {
return c
}

26
core.go
View File

@ -3,21 +3,23 @@ package main
import (
"github.com/gorilla/sessions"
"github.com/hunterlong/statup/plugin"
"github.com/hunterlong/statup/types"
)
type Core struct {
Name string `db:"name"`
Description string `db:"description"`
Config string `db:"config"`
ApiKey string `db:"api_key"`
ApiSecret string `db:"api_secret"`
Style string `db:"style"`
Footer string `db:"footer"`
Domain string `db:"domain"`
Version string `db:"version"`
Plugins []plugin.Info
Repos []PluginJSON
PluginFields []PluginSelect
Name string `db:"name"`
Description string `db:"description"`
Config string `db:"config"`
ApiKey string `db:"api_key"`
ApiSecret string `db:"api_secret"`
Style string `db:"style"`
Footer string `db:"footer"`
Domain string `db:"domain"`
Version string `db:"version"`
Plugins []plugin.Info
Repos []PluginJSON
PluginFields []PluginSelect
Communications []*types.Communication
}
func (c *Core) Update() (*Core, error) {

View File

@ -61,3 +61,7 @@ func DbConnection(dbType string) error {
OnLoad(dbSession)
return err
}
func Backup() {
}

View File

@ -8,6 +8,7 @@ import (
type Failure struct {
Id int `db:"id,omitempty"`
Issue string `db:"issue"`
Method string `db:"method"`
Service int64 `db:"service"`
CreatedAt time.Time `db:"created_at"`
}

View File

@ -23,7 +23,10 @@
<div class="nav flex-column nav-pills" id="v-pills-tab" role="tablist" aria-orientation="vertical">
<a class="nav-link active" id="v-pills-home-tab" data-toggle="pill" href="#v-pills-home" role="tab" aria-controls="v-pills-home" aria-selected="true">Settings</a>
<a class="nav-link" id="v-pills-home-tab" data-toggle="pill" href="#v-pills-style" role="tab" aria-controls="v-pills-style" aria-selected="false">Styling</a>
<a class="nav-link" id="v-pills-style-tab" data-toggle="pill" href="#v-pills-style" role="tab" aria-controls="v-pills-style" aria-selected="false">Styling</a>
{{ range .Communications }}
<a class="nav-link text-uppercase" id="v-pills-{{ .Method }}-tab" data-toggle="pill" href="#v-pills-{{ .Method }}" role="tab" aria-controls="v-pills-{{ .Method }}" aria-selected="false">{{ .Method }}</a>
{{ end }}
<a class="nav-link" id="v-pills-browse-tab" data-toggle="pill" href="#v-pills-browse" role="tab" aria-controls="v-pills-home" aria-selected="false">Browse Plugins</a>
{{ range .Plugins }}
<a class="nav-link text-capitalize" id="v-pills-{{underscore .Name}}-tab" data-toggle="pill" href="#v-pills-{{underscore .Name}}" role="tab" aria-controls="v-pills-profile" aria-selected="false">{{.Name}}</a>
@ -72,6 +75,48 @@
</form>
</div>
{{ range .Communications }}
<div class="tab-pane fade" id="v-pills-{{ .Method }}" role="tabpanel" aria-labelledby="v-pills-{{ .Method }}-tab">
<form method="POST" action="/settings/{{ .Method }}">
<div class="form-group">
<label for="formGroupExampleInput">SMTP Host</label>
<input type="text" name="host" class="form-control" value="{{ .Host }}" id="formGroupExampleInput" placeholder="Great Uptime">
</div>
<div class="form-group">
<label for="formGroupExampleInput">SMTP Username</label>
<input type="text" name="username" class="form-control" value="{{ .Username }}" id="formGroupExampleInput" placeholder="Great Uptime">
</div>
<div class="form-group">
<label for="formGroupExampleInput">SMTP Password</label>
<input type="password" name="password" class="form-control" value="{{ .Password }}" id="formGroupExampleInput">
</div>
<div class="form-group">
<label for="formGroupExampleInput">SMTP Port</label>
<input type="number" name="port" class="form-control" value="{{ .Port }}" id="formGroupExampleInput" placeholder="587">
</div>
<div class="form-group">
<label for="formGroupExampleInput">Outgoing Email Address</label>
<input type="text" name="address" class="form-control" value="{{ .Var1 }}" id="formGroupExampleInput" placeholder="noreply@domain.com">
</div>
<div class="form-group">
<label for="formGroupExampleInput">Limit Emails per Hour</label>
<input type="number" name="limit" class="form-control" value="30" id="formGroupExampleInput" placeholder="noreply@domain.com">
</div>
<button type="submit" class="btn btn-primary btn-block">Save Email Settings</button>
</form>
</div>
{{ end }}
<div class="tab-pane fade" id="v-pills-browse" role="tabpanel" aria-labelledby="v-pills-browse-tab">
{{ range .Repos }}
<div class="card col-6" style="width: 18rem;">

View File

@ -6,7 +6,6 @@ import (
"github.com/GeertJohan/go.rice"
"github.com/go-yaml/yaml"
"github.com/gorilla/sessions"
"github.com/hunterlong/statup/comms"
"github.com/hunterlong/statup/plugin"
"golang.org/x/crypto/bcrypt"
"io"
@ -126,8 +125,6 @@ func main() {
var err error
fmt.Printf("Starting Statup v%v\n", VERSION)
comms.NewMailer()
RenderBoxes()
configs, err = LoadConfig()
if err != nil {

View File

@ -23,6 +23,7 @@ func TestMySQLMakeConfig(t *testing.T) {
3306,
"Testing MYSQL",
"This is a test of Statup.io!",
"",
"admin",
"admin",
nil,
@ -36,7 +37,7 @@ func TestMySQLMakeConfig(t *testing.T) {
err = DbConnection(configs.Connection)
assert.Nil(t, err)
InsertDefaultComms()
}
func TestInsertMysqlSample(t *testing.T) {
@ -62,6 +63,7 @@ func TestSqliteMakeConfig(t *testing.T) {
5432,
"Testing SQLITE",
"This is a test of Statup.io!",
"",
"admin",
"admin",
nil,
@ -75,6 +77,7 @@ func TestSqliteMakeConfig(t *testing.T) {
err = DbConnection(configs.Connection)
assert.Nil(t, err)
InsertDefaultComms()
}
func TestInsertSqliteSample(t *testing.T) {
@ -92,6 +95,7 @@ func TestPostgresMakeConfig(t *testing.T) {
5432,
"Testing POSTGRES",
"This is a test of Statup.io!",
"",
"admin",
"admin",
nil,
@ -105,6 +109,7 @@ func TestPostgresMakeConfig(t *testing.T) {
err = DbConnection(configs.Connection)
assert.Nil(t, err)
InsertDefaultComms()
}
func TestInsertPostgresSample(t *testing.T) {
@ -130,8 +135,8 @@ func TestSelectCore(t *testing.T) {
func TestUser_Create(t *testing.T) {
user := &User{
Username: "testuserhere",
Password: "password123",
Username: "admin",
Password: "admin",
Email: "info@testuser.com",
}
id, err := user.Create()
@ -215,7 +220,7 @@ func TestService_Hits(t *testing.T) {
assert.NotNil(t, service)
hits, err := service.Hits()
assert.Nil(t, err)
assert.Equal(t, 20, len(hits))
assert.Equal(t, 26, len(hits))
}
func TestService_LimitedHits(t *testing.T) {
@ -223,7 +228,7 @@ func TestService_LimitedHits(t *testing.T) {
assert.NotNil(t, service)
hits, err := service.LimitedHits()
assert.Nil(t, err)
assert.Equal(t, 20, len(hits))
assert.Equal(t, 26, len(hits))
}
func Test(t *testing.T) {

View File

@ -4,6 +4,7 @@ import (
"fmt"
"github.com/go-yaml/yaml"
"github.com/hunterlong/statup/plugin"
"github.com/hunterlong/statup/types"
"net/http"
"os"
"strconv"
@ -20,13 +21,14 @@ type DbConfig struct {
DbPort int `yaml:"port"`
Project string `yaml:"-"`
Description string `yaml:"-"`
Domain string `yaml:"-"`
Username string `yaml:"-"`
Password string `yaml:"-"`
Error error
Error error `yaml:"-"`
}
func ProcessSetupHandler(w http.ResponseWriter, r *http.Request) {
if core.ApiKey != "" {
if core != nil {
http.Redirect(w, r, "/", http.StatusSeeOther)
return
}
@ -42,6 +44,7 @@ func ProcessSetupHandler(w http.ResponseWriter, r *http.Request) {
password := r.PostForm.Get("password")
sample := r.PostForm.Get("sample_data")
description := r.PostForm.Get("description")
domain := r.PostForm.Get("domain")
config := &DbConfig{
dbConn,
@ -52,6 +55,7 @@ func ProcessSetupHandler(w http.ResponseWriter, r *http.Request) {
dbPort,
project,
description,
domain,
username,
password,
nil,
@ -97,12 +101,12 @@ func ProcessSetupHandler(w http.ResponseWriter, r *http.Request) {
}
func InsertDefaultComms() {
emailer := &Communication{
emailer := &types.Communication{
Method: "email",
Removable: false,
Enabled: false,
}
emailer.Create()
Create(emailer)
}
func DeleteConfig() {
@ -166,6 +170,7 @@ func (c *DbConfig) Save() error {
[]plugin.Info{},
[]PluginJSON{},
[]PluginSelect{},
nil,
}
col := dbSession.Collection("core")

View File

@ -46,6 +46,7 @@ CREATE TABLE hits (
CREATE TABLE failures (
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
issue text,
method text,
service INTEGER NOT NULL,
created_at TIMESTAMP,
INDEX (id, service),
@ -65,7 +66,7 @@ CREATE TABLE communication (
method text,
host text,
port integer,
user text,
username text,
password text,
var1 text,
var2 text,

View File

@ -64,7 +64,7 @@ CREATE TABLE communication (
method text,
host text,
port integer,
user text,
username text,
password text,
var1 text,
var2 text,
@ -76,7 +76,6 @@ CREATE TABLE communication (
created_at TIMESTAMP
);
CREATE INDEX idx_hits ON hits(service);
CREATE INDEX idx_failures ON failures(service);
CREATE INDEX idx_checkins ON checkins(service);

View File

@ -46,6 +46,7 @@ CREATE TABLE hits (
CREATE TABLE failures (
id SERIAL PRIMARY KEY,
issue text,
method text,
service INTEGER NOT NULL REFERENCES services(id) ON DELETE CASCADE ON UPDATE CASCADE,
created_at TIMESTAMP
);
@ -63,7 +64,7 @@ CREATE TABLE communication (
method text,
host text,
port integer,
user text,
username text,
password text,
var1 text,
var2 text,

28
types/types.go Normal file
View File

@ -0,0 +1,28 @@
package types
import "time"
type Communication struct {
Id int64 `db:"id,omitempty" json:"id"`
Method string `db:"method" json:"method"`
Host string `db:"host" json:"host"`
Port int `db:"port" json:"port"`
Username string `db:"username" json:"user"`
Password string `db:"password" json:"-"`
Var1 string `db:"var1" json:"var1"`
Var2 string `db:"var2" json:"var2"`
ApiKey string `db:"api_key" json:"api_key"`
ApiSecret string `db:"api_secret" json:"api_secret"`
Enabled bool `db:"enabled" json:"enabled"`
Limits int64 `db:"limits" json:"limits"`
Removable bool `db:"removable" json:"removable"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
}
type Email struct {
To string
Subject string
Template string
Data interface{}
Body string
}

35
web.go
View File

@ -5,6 +5,8 @@ import (
"github.com/fatih/structs"
"github.com/gorilla/mux"
"github.com/gorilla/sessions"
"github.com/hunterlong/statup/comms"
"github.com/hunterlong/statup/types"
"html/template"
"net/http"
"regexp"
@ -45,6 +47,7 @@ func Router() *mux.Router {
r.Handle("/users/{id}/delete", http.HandlerFunc(UsersDeleteHandler)).Methods("GET")
r.Handle("/settings", http.HandlerFunc(PluginsHandler)).Methods("GET")
r.Handle("/settings", http.HandlerFunc(SaveSettingsHandler)).Methods("POST")
r.Handle("/settings/email", http.HandlerFunc(SaveEmailSettingsHandler)).Methods("POST")
r.Handle("/plugins/download/{name}", http.HandlerFunc(PluginsDownloadHandler))
r.Handle("/plugins/{name}/save", http.HandlerFunc(PluginSavedHandler)).Methods("POST")
r.Handle("/help", http.HandlerFunc(HelpHandler))
@ -150,7 +153,7 @@ func CreateServiceHandler(w http.ResponseWriter, r *http.Request) {
}
func SetupHandler(w http.ResponseWriter, r *http.Request) {
if core.ApiKey != "" {
if core != nil {
http.Redirect(w, r, "/", http.StatusSeeOther)
return
}
@ -245,6 +248,34 @@ func IsAuthenticated(r *http.Request) bool {
return session.Values["authenticated"].(bool)
}
func SaveEmailSettingsHandler(w http.ResponseWriter, r *http.Request) {
auth := IsAuthenticated(r)
if !auth {
http.Redirect(w, r, "/", http.StatusSeeOther)
return
}
emailer := SelectCommunication(1)
r.ParseForm()
emailer.Host = r.PostForm.Get("host")
emailer.Username = r.PostForm.Get("username")
emailer.Password = r.PostForm.Get("password")
emailer.Port = int(StringInt(r.PostForm.Get("port")))
emailer.Var1 = r.PostForm.Get("address")
Update(emailer)
sample := &types.Email{
To: "info@socialeck.com",
Subject: "Sample Email",
Template: "templates/error.html",
Body: "okkokkok",
}
comms.AddEmail(sample)
http.Redirect(w, r, "/settings", http.StatusSeeOther)
}
func SaveSettingsHandler(w http.ResponseWriter, r *http.Request) {
auth := IsAuthenticated(r)
if !auth {
@ -290,6 +321,8 @@ func PluginsHandler(w http.ResponseWriter, r *http.Request) {
}
core.PluginFields = pluginFields
fmt.Println(core.Communications)
ExecuteResponse(w, r, "plugins.html", core)
}