Added the Control Panel Dashboard.

Bumped the version requirements upto Go 1.8
Added gopsutil as a dependency.

Added the minify_templates and super_debug config.go options.
Cleaned up the console.
config.go is a little more organised now.
This commit is contained in:
Azareal 2017-05-07 09:31:41 +01:00
parent cac3ffe982
commit fab2db0936
23 changed files with 592 additions and 162 deletions

View File

@ -110,13 +110,17 @@ We're looking for ways to clean-up the plugin system so that all of them (except
# Dependencies
* Go 1.7
* Go 1.8
* MariaDB
* MariaDB (or any other MySQL compatible database engine)
* github.com/go-sql-driver/mysql
* github.com/go-sql-driver/mysql For interfacing with MariaDB.
* golang.org/x/crypto/bcrypt
* golang.org/x/crypto/bcrypt For hashing passwords.
* github.com/shirou/gopsutil For pulling information on CPU and memory usage.
* github.com/StackExchange/wmi Dependency for gopsutil on Windows.
# Bundled Plugins
@ -127,3 +131,9 @@ There are several plugins which are bundled with the software by default. These
* BBCode - A plugin in early development for converting BBCode Tags into HTML.
* Markdown - An extremely simple plugin for converting Markdown into HTML.
# Developers
There are a few things you'll need to know before running the more developer oriented features like the tests or the benchmarks.
The benchmarks are currently being rewritten as they're currently extremely serial which can lead to severe slow-downs when run on a home computer due to the benchmarks being run on the one core everything else is being run on (Browser, OS, etc.) and the tests not taking parallelism into account.

View File

@ -1,5 +1,13 @@
package main
// Site Info
var site_name = "Test Install" // Should be a setting in the database
var site_url = "localhost:8080"
var server_port = "8080"
var enable_ssl = false
var ssl_privkey = ""
var ssl_fullchain = ""
// Database details
var dbhost = "localhost"
var dbuser = "root"
@ -10,35 +18,32 @@ var dbport = "3306" // You probably won't need to change this
// Limiters
var max_request_size = 5 * megabyte
// Misc
// Caching
var cache_topicuser = CACHE_STATIC
var user_cache_capacity = 100 // The max number of users held in memory
var topic_cache_capacity = 100 // The max number of topics held in memory
var default_route = route_topics
var default_group = 3 // Should be a setting
var activation_group = 5 // Should be a setting
var staff_css = " background-color: #ffeaff;"
var uncategorised_forum_visible = true
var enable_emails = false
var site_name = "Test Install" // Should be a setting
var site_email = "" // Should be a setting
// Email
var site_email = "" // Should be a setting in the database
var smtp_server = ""
var smtp_username = ""
var smtp_password = ""
var smtp_port = "25"
var enable_emails = false
// Misc
var default_route = route_topics
var default_group = 3 // Should be a setting in the database
var activation_group = 5 // Should be a setting in the database
var staff_css = " background-color: #ffeaff;"
var uncategorised_forum_visible = true
var minify_templates = true
//var noavatar = "https://api.adorable.io/avatars/{width}/{id}@{site_url}.png"
var noavatar = "https://api.adorable.io/avatars/285/{id}@" + site_url + ".png"
var items_per_page = 25
var site_url = "localhost:8080"
var server_port = "8080"
var enable_ssl = false
var ssl_privkey = ""
var ssl_fullchain = ""
// Developer flags
var debug = false
var super_debug = false
var profiling = false

View File

@ -75,9 +75,10 @@ func add_static_file(path string, prefix string) error {
return err
}
log.Print("Adding the '" + path + "' static file")
path = strings.TrimPrefix(path, prefix)
if debug {
log.Print("Added the '" + path + "' static file")
}
gzip_data := compress_bytes_gzip(data)
static_files["/static" + path] = SFile{data,gzip_data,0,int64(len(data)),int64(len(gzip_data)),mime.TypeByExtension(filepath.Ext(prefix + path)),f,f.ModTime().UTC().Format(http.TimeFormat)}

View File

@ -2,6 +2,8 @@ echo "Installing the MySQL Driver"
go get -u github.com/go-sql-driver/mysql
echo "Installing bcrypt"
go get -u golang.org/x/crypto/bcrypt
echo "Installing gopsutil"
go get -u github.com/shirou/gopsutil
echo "Preparing the installer"
go generate

View File

@ -1,5 +1,5 @@
@echo off
echo Installing the dependencies
echo Installing dependencies
go get -u github.com/go-sql-driver/mysql
if %errorlevel% neq 0 (
pause
@ -10,6 +10,16 @@ if %errorlevel% neq 0 (
pause
exit /b %errorlevel%
)
go get -u github.com/StackExchange/wmi
if %errorlevel% neq 0 (
pause
exit /b %errorlevel%
)
go get -u github.com/shirou/gopsutil
if %errorlevel% neq 0 (
pause
exit /b %errorlevel%
)
echo Preparing the installer
go generate

View File

@ -138,6 +138,14 @@ func main() {
configContents := []byte(`package main
// Site Info
var site_name = "` + site_name + `" // Should be a setting in the database
var site_url = "` + site_url + `"
var server_port = "` + server_port + `"
var enable_ssl = false
var ssl_privkey = ""
var ssl_fullchain = ""
// Database details
var dbhost = "` + db_host + `"
var dbuser = "` + db_username + `"
@ -148,36 +156,34 @@ var dbport = "` + db_port + `" // You probably won't need to change this
// Limiters
var max_request_size = 5 * megabyte
// Misc
// Caching
var cache_topicuser = CACHE_STATIC
var user_cache_capacity = 100 // The max number of users held in memory
var topic_cache_capacity = 100 // The max number of topics held in memory
var default_route = route_topics
var default_group = 3 // Should be a setting
var activation_group = 5 // Should be a setting
var staff_css = " background-color: #ffeaff;"
var uncategorised_forum_visible = true
var enable_emails = false
var site_name = "` + site_name + `" // Should be a setting
var site_email = "" // Should be a setting
// Email
var site_email = "" // Should be a setting in the database
var smtp_server = ""
var smtp_username = ""
var smtp_password = ""
var smtp_port = "25"
var enable_emails = false
// Misc
var default_route = route_topics
var default_group = 3 // Should be a setting in the database
var activation_group = 5 // Should be a setting in the database
var staff_css = " background-color: #ffeaff;"
var uncategorised_forum_visible = true
var minify_templates = true
//var noavatar = "https://api.adorable.io/avatars/{width}/{id}@{site_url}.png"
var noavatar = "https://api.adorable.io/avatars/285/{id}@" + site_url + ".png"
var items_per_page = 25
var site_url = "` + site_url + `"
var server_port = "` + server_port + `"
var enable_ssl = false
var ssl_privkey = ""
var ssl_fullchain = ""
// Developer flag
var debug = false
var debug = true
var super_debug = false
var profiling = false
`)

32
main.go
View File

@ -1,8 +1,9 @@
/* Copyright Azareal 2016 - 2017 */
/* Copyright Azareal 2016 - 2018 */
package main
import (
"net/http"
"fmt"
"log"
"mime"
"strings"
@ -14,15 +15,18 @@ import (
//"runtime/pprof"
)
var version Version = Version{Major:0,Minor:1,Patch:0,Tag:"dev"}
const hour int = 60 * 60
const day int = hour * 24
const month int = day * 30
const year int = day * 365
const kilobyte int = 1024
const megabyte int = kilobyte * 1024
const gigabyte int = megabyte * 1024
const terabyte int = gigabyte * 1024
const saltLength int = 32
const sessionLength int = 80
var nogrouplog bool = false // This is mainly for benchmarks, as we don't want a lot of information getting in the way of the results
var templates = template.New("")
var no_css_tmpl = template.CSS("")
@ -106,19 +110,14 @@ func init_templates() {
compile_templates()
// Filler functions for now...
filler_func := func(in interface{}, in2 interface{})interface{} {
return 1
}
fmap := make(map[string]interface{})
fmap["add"] = func(in interface{}, in2 interface{})interface{} {
return 1
}
fmap["subtract"] = func(in interface{}, in2 interface{})interface{} {
return 1
}
fmap["multiply"] = func(in interface{}, in2 interface{})interface{} {
return 1
}
fmap["divide"] = func(in interface{}, in2 interface{})interface{} {
return 1
}
fmap["add"] = filler_func
fmap["subtract"] = filler_func
fmap["multiply"] = filler_func
fmap["divide"] = filler_func
// The interpreted templates...
templates.Funcs(fmap)
@ -140,7 +139,9 @@ func init_static_files() {
}
path = strings.TrimPrefix(path,"public/")
if debug {
log.Print("Added the '" + path + "' static file.")
}
gzip_data := compress_bytes_gzip(data)
static_files["/static/" + path] = SFile{data,gzip_data,0,int64(len(data)),int64(len(gzip_data)),mime.TypeByExtension(filepath.Ext("/public/" + path)),f,f.ModTime().UTC().Format(http.TimeFormat)}
@ -160,6 +161,9 @@ func main(){
// pprof.StartCPUProfile(f)
//}
log.Print("Running Gosora v" + version.String())
fmt.Println("")
init_themes()
err := init_database()
if err != nil {

View File

@ -633,7 +633,7 @@ func init_database() (err error) {
// Ugh, you really shouldn't physically delete these items, it makes a big mess of things
if group.ID != i {
fmt.Println("Stop physically deleting groups. You are messing up the IDs. Use the Group Manager or delete_group() instead x.x")
log.Print("Stop physically deleting groups. You are messing up the IDs. Use the Group Manager or delete_group() instead x.x")
fill_group_id_gap(i, group.ID)
}
@ -641,8 +641,8 @@ func init_database() (err error) {
if err != nil {
return err
}
if !nogrouplog {
fmt.Println(group.Name + ": ")
if debug {
log.Print(group.Name + ": ")
fmt.Printf("%+v\n", group.Perms)
}
@ -679,7 +679,7 @@ func init_database() (err error) {
// Ugh, you really shouldn't physically delete these items, it makes a big mess of things
if forum.ID != i {
fmt.Println("Stop physically deleting forums. You are messing up the IDs. Use the Forum Manager or delete_forum() instead x.x")
log.Print("Stop physically deleting forums. You are messing up the IDs. Use the Forum Manager or delete_forum() instead x.x")
fill_forum_id_gap(i, forum.ID)
}
@ -693,7 +693,13 @@ func init_database() (err error) {
forum.LastTopicTime = ""
}*/
if forum.Name == "" {
if debug {
log.Print("Adding a placeholder forum")
}
} else {
log.Print("Adding the " + forum.Name + " forum")
}
forums = append(forums,forum)
}
err = rows.Err()
@ -712,12 +718,13 @@ func init_database() (err error) {
}
defer rows.Close()
// Temporarily store the forum perms in a map before transferring it to a much faster slice
if debug {
log.Print("Adding the forum permissions")
}
// Temporarily store the forum perms in a map before transferring it to a much faster slice
forum_perms = make(map[int]map[int]ForumPerms)
for rows.Next() {
var gid int
var fid int
var gid, fid int
var perms []byte
var pperms ForumPerms
err := rows.Scan(&gid, &fid, &perms)
@ -737,7 +744,9 @@ func init_database() (err error) {
forum_perms[gid][fid] = pperms
}
for gid, _ := range groups {
if debug {
log.Print("Adding the forum permissions for Group #" + strconv.Itoa(gid) + " - " + groups[gid].Name)
}
//groups[gid].Forums = append(groups[gid].Forums,BlankForumPerms) // GID 0. I sometimes wish MySQL's AUTO_INCREMENT would start at zero
for fid, _ := range forums {
forum_perm, ok := forum_perms[gid][fid]

View File

@ -77,6 +77,25 @@ type CreateTopicPage struct
ExtData interface{}
}
type GridElement struct
{
Body string
Order int // For future use
Class string
Background string
TextColour string
Note string
}
type PanelDashboardPage struct
{
Title string
CurrentUser User
NoticeList []string
GridItems []GridElement
ExtData interface{}
}
type ThemesPage struct
{
Title string

View File

@ -1,15 +1,23 @@
package main
import "log"
import "fmt"
import "strings"
import "strconv"
import "html"
import "encoding/json"
import "net/http"
import "html/template"
import "database/sql"
import (
"log"
"fmt"
"errors"
"strings"
"strconv"
"html"
"time"
"runtime"
"encoding/json"
"net/http"
"html/template"
"database/sql"
)
import _ "github.com/go-sql-driver/mysql"
import "github.com/shirou/gopsutil/cpu"
import "github.com/shirou/gopsutil/mem"
func route_panel(w http.ResponseWriter, r *http.Request){
user, noticeList, ok := SessionCheck(w,r)
@ -20,7 +28,137 @@ func route_panel(w http.ResponseWriter, r *http.Request){
NoPermissions(w,r,user)
return
}
pi := Page{"Control Panel Dashboard",user,noticeList,tList,nil}
var cpustr, cpuColour string
perc2, err := cpu.Percent(time.Duration(time.Second),true)
if err != nil {
cpustr = "Unknown"
} else {
/*cpures, _ := cpu.Times(true)
totcpu := cpures[0].Idle + cpures[0].System + cpures[0].User
fmt.Println("System",cpures[0].System)
fmt.Println("User",cpures[0].User)
fmt.Println("Usage",cpures[0].System + cpures[0].User)
fmt.Println("Idle",cpures[0].Idle)
fmt.Println("Gap",totcpu - (cpures[0].System + cpures[0].User))
perc := ((cpures[0].System + + cpures[0].User) * 100) / totcpu
fmt.Println("Perc",perc)
fmt.Println("Perc2",perc2)*/
calcperc := int(perc2[0]) / runtime.NumCPU()
cpustr = strconv.Itoa(calcperc)
if calcperc < 25 {
cpuColour = "stat_green"
} else if calcperc < 75 {
cpuColour = "stat_orange"
} else {
cpuColour = "stat_red"
}
}
var ramstr, ramColour string
memres, err := mem.VirtualMemory()
if err != nil {
ramstr = "Unknown"
} else {
total_count, total_unit := convert_byte_unit(float64(memres.Total))
used_count := convert_byte_in_unit(float64(memres.Total - memres.Available),total_unit)
// Round totals with .9s up, it's how most people see it anyway. Floats are notoriously imprecise, so do it off 0.85
//fmt.Println(used_count)
var totstr string
if (total_count - float64(int(total_count))) > 0.85 {
used_count += 1.0 - (total_count - float64(int(total_count)))
totstr = strconv.Itoa(int(total_count) + 1)
} else {
totstr = fmt.Sprintf("%.1f",total_count)
}
//fmt.Println(used_count)
if used_count > total_count {
used_count = total_count
}
ramstr = fmt.Sprintf("%.1f",used_count) + " / " + totstr + total_unit
ramperc := ((memres.Total - memres.Available) * 100) / memres.Total
//fmt.Println(ramperc)
if ramperc < 50 {
ramColour = "stat_green"
} else if ramperc < 75 {
ramColour = "stat_orange"
} else {
ramColour = "stat_red"
}
}
var postCount int
err = db.QueryRow("select count(*) from replies where createdAt BETWEEN (now() - interval 1 day) and now()").Scan(&postCount)
if err != nil && err != sql.ErrNoRows {
InternalError(err,w,r)
return
}
var postInterval string = "day"
var postColour string
if postCount > 10 {
postColour = "stat_green"
} else if postCount > 0 {
postColour = "stat_orange"
} else {
postColour = "stat_red"
}
var topicCount int
err = db.QueryRow("select count(*) from topics where createdAt BETWEEN (now() - interval 1 day) and now()").Scan(&topicCount)
if err != nil && err != sql.ErrNoRows {
InternalError(err,w,r)
return
}
var topicInterval string = "day"
var topicColour string
if topicCount > 10 {
topicColour = "stat_green"
} else if topicCount > 0 {
topicColour = "stat_orange"
} else {
topicColour = "stat_red"
}
var reportCount int
err = db.QueryRow("select count(*) from topics where createdAt BETWEEN (now() - interval 1 day) and now() and parentID = 1").Scan(&reportCount)
if err != nil && err != sql.ErrNoRows {
InternalError(err,w,r)
return
}
var reportInterval string = "week"
var newUserCount int
err = db.QueryRow("select count(*) from users where createdAt BETWEEN (now() - interval 1 day) and now()").Scan(&newUserCount)
if err != nil && err != sql.ErrNoRows {
InternalError(err,w,r)
return
}
var newUserInterval string = "week"
var gridElements []GridElement = []GridElement{
GridElement{"v" + version.String(),0,"grid_istat stat_green","","","Gosora is up-to-date :)"},
GridElement{"CPU: " + cpustr + "%",1,"grid_istat " + cpuColour,"","","The global CPU usage of this server"},
GridElement{"RAM: " + ramstr,2,"grid_istat " + ramColour,"","","The global RAM usage of this server"},
GridElement{strconv.Itoa(postCount) + " posts / " + postInterval,3,"grid_stat " + postColour,"","","The number of new posts over the last 24 hours"},
GridElement{strconv.Itoa(topicCount) + " topics / " + topicInterval,4,"grid_stat " + topicColour,"","","The number of new topics over the last 24 hours"},
GridElement{"20 online / day",5,"grid_stat stat_disabled","","","Coming Soon!"/*"The people online over the last 24 hours"*/},
GridElement{"8 searches / week",6,"grid_stat stat_disabled","","","Coming Soon!"/*"The number of searches over the last 7 days"*/},
GridElement{strconv.Itoa(newUserCount) + " new users / " + newUserInterval,7,"grid_stat","","","The number of new users over the last 7 days"},
GridElement{strconv.Itoa(reportCount) + " reports / " + reportInterval,8,"grid_stat","","","The number of reports over the last 7 days"},
GridElement{"2 minutes / user / week",9,"grid_stat stat_disabled","","","Coming Soon!"/*"The average number of number of minutes spent by each active user over the last 7 days"*/},
GridElement{"2 visitors / week",10,"grid_stat stat_disabled","","","Coming Soon!"/*"The number of unique visitors we've had over the last 7 days"*/},
GridElement{"5 posts / user / week",11,"grid_stat stat_disabled","","","Coming Soon!"/*"The average number of posts made by each active user over the past week"*/},
}
pi := PanelDashboardPage{"Control Panel Dashboard",user,noticeList,gridElements,nil}
templates.ExecuteTemplate(w,"panel-dashboard.html",pi)
}
@ -1261,7 +1399,7 @@ func route_panel_themes_default(w http.ResponseWriter, r *http.Request, uname st
dTheme, ok := themes[defaultTheme]
if !ok {
log.Fatal("The default theme is missing")
InternalError(errors.New("The default theme is missing"),w,r)
return
}
dTheme.Active = false

View File

@ -27,11 +27,7 @@ func init() {
func parseSetting(sname string, scontent string, stype string, constraint string) string {
var err error
if stype == "bool" {
if scontent == "1" {
settings[sname] = true
} else {
settings[sname] = false
}
settings[sname] = (scontent == "1")
} else if stype == "int" {
settings[sname], err = strconv.Atoi(scontent)
if err != nil {

View File

@ -1,4 +1,5 @@
package main
import "log"
import "fmt"
import "bytes"
@ -90,7 +91,7 @@ func (c *CTemplateSet) compile_template(name string, dir string, expects string,
}
content := string(res)
if !debug {
if minify_templates {
content = minify(content)
}
@ -100,7 +101,7 @@ func (c *CTemplateSet) compile_template(name string, dir string, expects string,
if err != nil {
log.Fatal(err)
}
if debug {
if super_debug {
fmt.Println(name)
}
@ -110,7 +111,7 @@ func (c *CTemplateSet) compile_template(name string, dir string, expects string,
c.tlist[fname] = tree
varholder := "tmpl_" + fname + "_vars"
if debug {
if super_debug {
fmt.Println(c.tlist)
}
c.localVars = make(map[string]map[string]VarItemReflect)
@ -123,13 +124,13 @@ func (c *CTemplateSet) compile_template(name string, dir string, expects string,
c.FragmentCursor[fname] = 0
subtree := c.tlist[fname]
if debug {
if super_debug {
fmt.Println(subtree.Root)
}
treeLength := len(subtree.Root.Nodes)
for index, node := range subtree.Root.Nodes {
if debug {
if super_debug {
fmt.Println("Node: " + node.String())
}
@ -165,12 +166,14 @@ w.Write([]byte(`," + ",-1)
//whitespace_writes := regexp.MustCompile(`(?s)w.Write\(\[\]byte\(`+spstr+`\)\)`)
//fout = whitespace_writes.ReplaceAllString(fout,"")
if debug {
for index, count := range c.stats {
fmt.Println(index + ": " + strconv.Itoa(count))
}
fmt.Println(" ")
}
if debug {
if super_debug {
fmt.Println("Output!")
fmt.Println(fout)
}
@ -180,7 +183,7 @@ w.Write([]byte(`," + ",-1)
func (c *CTemplateSet) compile_switch(varholder string, holdreflect reflect.Value, template_name string, node interface{}) (out string) {
switch node := node.(type) {
case *parse.ActionNode:
if debug {
if super_debug {
fmt.Println("Action Node")
}
@ -192,14 +195,14 @@ func (c *CTemplateSet) compile_switch(varholder string, holdreflect reflect.Valu
}
return out
case *parse.IfNode:
if debug {
if super_debug {
fmt.Println("If Node: ")
fmt.Println(node.Pipe)
}
var expr string
for _, cmd := range node.Pipe.Cmds {
if debug {
if super_debug {
fmt.Println("If Node Bit: ")
fmt.Println(cmd)
fmt.Println(reflect.ValueOf(cmd).Type().Name())
@ -211,18 +214,18 @@ func (c *CTemplateSet) compile_switch(varholder string, holdreflect reflect.Valu
c.currentNode = parse.NodeList
c.nextNode = -1
if node.ElseList == nil {
if debug {
if super_debug {
fmt.Println("Branch 1")
}
return "if " + expr + " {\n" + c.compile_switch(varholder, holdreflect, template_name, node.List) + "}\n"
} else {
if debug {
if super_debug {
fmt.Println("Branch 2")
}
return "if " + expr + " {\n" + c.compile_switch(varholder, holdreflect, template_name, node.List) + "} else {\n" + c.compile_switch(varholder, holdreflect, template_name, node.ElseList) + "}\n"
}
case *parse.ListNode:
if debug {
if super_debug {
fmt.Println("List Node")
}
for _, subnode := range node.Nodes {
@ -230,21 +233,21 @@ func (c *CTemplateSet) compile_switch(varholder string, holdreflect reflect.Valu
}
return out
case *parse.RangeNode:
if debug {
if super_debug {
fmt.Println("Range Node!")
fmt.Println(node.Pipe)
}
var outVal reflect.Value
for _, cmd := range node.Pipe.Cmds {
if debug {
if super_debug {
fmt.Println("Range Bit: ")
fmt.Println(cmd)
}
out, outVal = c.compile_reflectswitch(varholder, holdreflect, template_name, cmd)
}
if debug {
if super_debug {
fmt.Println("Returned: ")
fmt.Println(out)
fmt.Println("Range Kind Switch!")
@ -277,7 +280,7 @@ func (c *CTemplateSet) compile_switch(varholder string, holdreflect reflect.Valu
}
return out
case *parse.TemplateNode:
if debug {
if super_debug {
fmt.Println("Template Node")
}
return c.compile_subtemplate(varholder, holdreflect, node)
@ -309,7 +312,7 @@ func (c *CTemplateSet) compile_subswitch(varholder string, holdreflect reflect.V
firstWord := node.Args[0]
switch n := firstWord.(type) {
case *parse.FieldNode:
if debug {
if super_debug {
fmt.Println("Field Node: ")
fmt.Println(n.Ident)
}
@ -324,7 +327,7 @@ func (c *CTemplateSet) compile_subswitch(varholder string, holdreflect reflect.V
}
for _, id := range n.Ident {
if debug {
if super_debug {
fmt.Println("Data Kind: ")
fmt.Println(cur.Kind().String())
fmt.Println("Field Bit: ")
@ -346,7 +349,7 @@ func (c *CTemplateSet) compile_subswitch(varholder string, holdreflect reflect.V
} else {
varbit += "." + id
}
if debug {
if super_debug {
fmt.Println("End Cycle")
}
}
@ -359,7 +362,7 @@ func (c *CTemplateSet) compile_subswitch(varholder string, holdreflect reflect.V
}
return out
case *parse.DotNode:
if debug {
if super_debug {
fmt.Println("Dot Node")
fmt.Println(node.String())
}
@ -367,7 +370,7 @@ func (c *CTemplateSet) compile_subswitch(varholder string, holdreflect reflect.V
case *parse.NilNode:
panic("Nil is not a command x.x")
case *parse.VariableNode:
if debug {
if super_debug {
fmt.Println("Variable Node")
fmt.Println(n.String())
fmt.Println(n.Ident)
@ -377,7 +380,7 @@ func (c *CTemplateSet) compile_subswitch(varholder string, holdreflect reflect.V
case *parse.StringNode:
return n.Quoted
case *parse.IdentifierNode:
if debug {
if super_debug {
fmt.Println("Identifier Node: ")
fmt.Println(node)
fmt.Println(node.Args)
@ -397,7 +400,7 @@ func (c *CTemplateSet) compile_varswitch(varholder string, holdreflect reflect.V
firstWord := node.Args[0]
switch n := firstWord.(type) {
case *parse.FieldNode:
if debug {
if super_debug {
fmt.Println("Field Node: ")
fmt.Println(n.Ident)
@ -410,14 +413,14 @@ func (c *CTemplateSet) compile_varswitch(varholder string, holdreflect reflect.V
/* Use reflect to determine if the field is for a method, otherwise assume it's a variable. Coming Soon. */
return c.compile_boolsub(n.String(), varholder, template_name, holdreflect)
case *parse.ChainNode:
if debug {
if super_debug {
fmt.Println("Chain Node: ")
fmt.Println(n.Node)
fmt.Println(node.Args)
}
break
case *parse.IdentifierNode:
if debug {
if super_debug {
fmt.Println("Identifier Node: ")
fmt.Println(node)
fmt.Println(node.Args)
@ -426,7 +429,7 @@ func (c *CTemplateSet) compile_varswitch(varholder string, holdreflect reflect.V
case *parse.DotNode:
return varholder
case *parse.VariableNode:
if debug {
if super_debug {
fmt.Println("Variable Node")
fmt.Println(n.String())
fmt.Println(n.Ident)
@ -436,7 +439,7 @@ func (c *CTemplateSet) compile_varswitch(varholder string, holdreflect reflect.V
case *parse.NilNode:
panic("Nil is not a command x.x")
case *parse.PipeNode:
if debug {
if super_debug {
fmt.Println("Pipe Node!")
fmt.Println(n)
fmt.Println("Args: ")
@ -444,7 +447,7 @@ func (c *CTemplateSet) compile_varswitch(varholder string, holdreflect reflect.V
}
out += c.compile_identswitch_n(varholder, holdreflect, template_name, node)
if debug {
if super_debug {
fmt.Println("Out: ")
fmt.Println(out)
}
@ -467,7 +470,7 @@ func (c *CTemplateSet) compile_identswitch_n(varholder string, holdreflect refle
func (c *CTemplateSet) compile_identswitch(varholder string, holdreflect reflect.Value, template_name string, node *parse.CommandNode) (out string, val reflect.Value) {
ArgLoop:
for pos, id := range node.Args {
if debug {
if super_debug {
fmt.Println(id)
}
@ -480,42 +483,42 @@ func (c *CTemplateSet) compile_identswitch(varholder string, holdreflect reflect
out += " && "
case "le":
out += c.compile_if_varsub_n(node.Args[pos + 1].String(), varholder, template_name, holdreflect) + " <= " + c.compile_if_varsub_n(node.Args[pos + 2].String(), varholder, template_name, holdreflect)
if debug {
if super_debug {
fmt.Println(node.Args[pos + 1])
fmt.Println(node.Args[pos + 2])
}
break ArgLoop
case "lt":
out += c.compile_if_varsub_n(node.Args[pos + 1].String(), varholder, template_name, holdreflect) + " < " + c.compile_if_varsub_n(node.Args[pos + 2].String(), varholder, template_name, holdreflect)
if debug {
if super_debug {
fmt.Println(node.Args[pos + 1])
fmt.Println(node.Args[pos + 2])
}
break ArgLoop
case "gt":
out += c.compile_if_varsub_n(node.Args[pos + 1].String(), varholder, template_name, holdreflect) + " > " + c.compile_if_varsub_n(node.Args[pos + 2].String(), varholder, template_name, holdreflect)
if debug {
if super_debug {
fmt.Println(node.Args[pos + 1])
fmt.Println(node.Args[pos + 2])
}
break ArgLoop
case "ge":
out += c.compile_if_varsub_n(node.Args[pos + 1].String(), varholder, template_name, holdreflect) + " >= " + c.compile_if_varsub_n(node.Args[pos + 2].String(), varholder, template_name, holdreflect)
if debug {
if super_debug {
fmt.Println(node.Args[pos + 1])
fmt.Println(node.Args[pos + 2])
}
break ArgLoop
case "eq":
out += c.compile_if_varsub_n(node.Args[pos + 1].String(), varholder, template_name, holdreflect) + " == " + c.compile_if_varsub_n(node.Args[pos + 2].String(), varholder, template_name, holdreflect)
if debug {
if super_debug {
fmt.Println(node.Args[pos + 1])
fmt.Println(node.Args[pos + 2])
}
break ArgLoop
case "ne":
out += c.compile_if_varsub_n(node.Args[pos + 1].String(), varholder, template_name, holdreflect) + " != " + c.compile_if_varsub_n(node.Args[pos + 2].String(), varholder, template_name, holdreflect)
if debug {
if super_debug {
fmt.Println(node.Args[pos + 1])
fmt.Println(node.Args[pos + 2])
}
@ -534,7 +537,7 @@ func (c *CTemplateSet) compile_identswitch(varholder string, holdreflect reflect
}
out += param1 + " + " + param2
if debug {
if super_debug {
fmt.Println("add")
fmt.Println(node.Args[pos + 1])
fmt.Println(node.Args[pos + 2])
@ -554,7 +557,7 @@ func (c *CTemplateSet) compile_identswitch(varholder string, holdreflect reflect
}
out += param1 + " - " + param2
if debug {
if super_debug {
fmt.Println("subtract")
fmt.Println(node.Args[pos + 1])
fmt.Println(node.Args[pos + 2])
@ -574,7 +577,7 @@ func (c *CTemplateSet) compile_identswitch(varholder string, holdreflect reflect
}
out += param1 + " / " + param2
if debug {
if super_debug {
fmt.Println("divide")
fmt.Println(node.Args[pos + 1])
fmt.Println(node.Args[pos + 2])
@ -594,14 +597,14 @@ func (c *CTemplateSet) compile_identswitch(varholder string, holdreflect reflect
}
out += param1 + " * " + param2
if debug {
if super_debug {
fmt.Println("multiply")
fmt.Println(node.Args[pos + 1])
fmt.Println(node.Args[pos + 2])
}
break ArgLoop
default:
if debug {
if super_debug {
fmt.Println("Variable!")
}
out += c.compile_if_varsub_n(id.String(), varholder, template_name, holdreflect)
@ -614,7 +617,7 @@ func (c *CTemplateSet) compile_reflectswitch(varholder string, holdreflect refle
firstWord := node.Args[0]
switch n := firstWord.(type) {
case *parse.FieldNode:
if debug {
if super_debug {
fmt.Println("Field Node: ")
fmt.Println(n.Ident)
@ -626,7 +629,7 @@ func (c *CTemplateSet) compile_reflectswitch(varholder string, holdreflect refle
/* Use reflect to determine if the field is for a method, otherwise assume it's a variable. Coming Soon. */
return c.compile_if_varsub(n.String(), varholder, template_name, holdreflect)
case *parse.ChainNode:
if debug {
if super_debug {
fmt.Println("Chain Node: ")
fmt.Println(n.Node)
fmt.Println(node.Args)
@ -676,7 +679,7 @@ func (c *CTemplateSet) compile_if_varsub(varname string, varholder string, templ
}
bits[0] = strings.TrimPrefix(bits[0],"$")
if debug {
if super_debug {
fmt.Println("Cur Kind: ")
fmt.Println(cur.Kind())
fmt.Println("Cur Type: ")
@ -684,7 +687,7 @@ func (c *CTemplateSet) compile_if_varsub(varname string, varholder string, templ
}
for _, bit := range bits {
if debug {
if super_debug {
fmt.Println("Variable Field!")
fmt.Println(bit)
}
@ -701,7 +704,7 @@ func (c *CTemplateSet) compile_if_varsub(varname string, varholder string, templ
out += "." + bit
}
if debug {
if super_debug {
fmt.Println("Data Kind: ")
fmt.Println(cur.Kind())
fmt.Println("Data Type: ")
@ -709,7 +712,7 @@ func (c *CTemplateSet) compile_if_varsub(varname string, varholder string, templ
}
}
if debug {
if super_debug {
fmt.Println("Out Value: ")
fmt.Println(out)
fmt.Println("Out Kind: ")
@ -724,7 +727,7 @@ func (c *CTemplateSet) compile_if_varsub(varname string, varholder string, templ
}
}
if debug {
if super_debug {
fmt.Println("Out Value: ")
fmt.Println(out)
fmt.Println("Out Kind: ")
@ -802,7 +805,7 @@ func (c *CTemplateSet) compile_varsub(varname string, val reflect.Value) string
}
func (c *CTemplateSet) compile_subtemplate(pvarholder string, pholdreflect reflect.Value, node *parse.TemplateNode) (out string) {
if debug {
if super_debug {
fmt.Println("Template Node: " + node.Name)
}
@ -832,7 +835,7 @@ func (c *CTemplateSet) compile_subtemplate(pvarholder string, pholdreflect refle
}
content := string(res)
if !debug {
if minify_templates {
content = minify(content)
}
@ -845,7 +848,7 @@ func (c *CTemplateSet) compile_subtemplate(pvarholder string, pholdreflect refle
c.tlist[fname] = tree
subtree := c.tlist[fname]
if debug {
if super_debug {
fmt.Println(subtree.Root)
}
@ -855,7 +858,7 @@ func (c *CTemplateSet) compile_subtemplate(pvarholder string, pholdreflect refle
treeLength := len(subtree.Root.Nodes)
for index, node := range subtree.Root.Nodes {
if debug {
if super_debug {
fmt.Println("Node: " + node.String())
}

View File

@ -1,11 +1,11 @@
{{template "header.html" . }}
{{template "panel-menu.html" . }}
<div class="colstack_right">
<div class="colstack_item colstack_head">
<div class="rowitem rowhead"><a>Dashboard</a></div>
</div>
<div class="colstack_item">
<div class="rowitem passive">Coming Soon...</div>
<div class="colstack_grid">
{{range .GridItems}}
<div class="grid_item {{.Class}}" title="{{.Note}}" style="{{if .TextColour}}color: {{.TextColour}};{{end}}
{{if .Background}}background-color: {{.Background}};{{end}}">{{.Body}}</div>
{{end}}
</div>
</div>
{{template "footer.html" . }}

View File

@ -20,7 +20,7 @@
<a style="font-size: 17px;">{{.Action}}</a><br />
<small style="margin-left: 2px;">IP: {{.IPAddress}}</small>
<span style="float: right;">
<span style="font-size: 16px;position:relative;top: -2px;">{{.DoneAt}}</span>
<span style="font-size: 16px;">{{.DoneAt}}</span>
</span>
</div>
{{end}}

View File

@ -77,7 +77,9 @@ func init_themes() {
theme.Active = false // Set this to false, just in case someone explicitly overrode this value in the JSON file
if theme.FullImage != "" {
if debug {
log.Print("Adding theme image")
}
err = add_static_file("./themes/" + themeName + "/" + theme.FullImage, "./themes/" + themeName)
if err != nil {
log.Fatal(err)
@ -98,14 +100,19 @@ func add_theme_static_files(themeName string) {
}
path = strings.Replace(path,"\\","/",-1)
if debug {
log.Print("Attempting to add static file '" + path + "' for default theme '" + themeName + "'")
}
data, err := ioutil.ReadFile(path)
if err != nil {
return err
}
path = strings.TrimPrefix(path,"themes/" + themeName + "/public")
if debug {
log.Print("Added the '" + path + "' static file for default theme " + themeName + ".")
}
gzip_data := compress_bytes_gzip(data)
static_files["/static" + path] = SFile{data,gzip_data,0,int64(len(data)),int64(len(gzip_data)),mime.TypeByExtension(filepath.Ext("/themes/" + themeName + "/public" + path)),f,f.ModTime().UTC().Format(http.TimeFormat)}

View File

@ -1,4 +1,4 @@
/* AtomBB Cosmo Port. Copyright Azareal 2017 */
/* AtomBB Cosmo Port. Copyright Azareal 2017 - 2018 */
/* I'm currently converting the CSS over. Don't use this yet! */
* {
@ -261,7 +261,7 @@ hr { color: silver; border: 1px solid silver; }
.colblock_left:first-of-type { margin-top: 8px; }
.colblock_right:first-of-type { margin-top: 8px; }
/* The new method of doing columns layouts, colblock is now deprecated */
/* The new method of doing columns layouts, colblock is now deprecated :( */
.colstack_left
{
float: left;
@ -289,6 +289,39 @@ hr { color: silver; border: 1px solid silver; }
.colstack_left:empty { display: none; }
.colstack_right:empty { display: none; }
.colstack_grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
/*grid-gap: 15px;*/
grid-gap: 12px;
margin-left: 5px;
}
.grid_item {
border: 1px solid #ccc;
word-wrap: break-word;
background-color: white;
width: 100%;
overflow: hidden;
}
.grid_stat, .grid_istat {
/*padding-top: 15px;*/
text-align: center;
/*padding-bottom: 15px;
font-size: 20px;*/
padding-top: 12px;
padding-bottom: 12px;
font-size: 16px;
}
.grid_istat {
/*margin-bottom: 10px;*/
margin-bottom: 5px;
}
.stat_green { background-color: lightgreen; border-color: green; }
.stat_orange { background-color: #ffe4b3; border-color: orange; }
.stat_red { background-color: #ffb2b2; border-color: red; }
.stat_disabled { background-color: lightgray; border-color: gray; }
.colitem
{
padding-left: 8px;

View File

@ -196,13 +196,8 @@ hr { color: silver; border: 1px solid silver; }
border-left: 1px solid black;
border-right: 1px solid black;
}
.rowblock:first-of-type {
margin-top: 8px;
}
.rowitem:not(:last-child)
{
border-bottom: 1px dotted #ccc;
}
.rowblock:first-of-type { margin-top: 8px; }
.rowitem:not(:last-child) { border-bottom: 1px dotted #ccc; }
.rowhead, .colhead {
background: #ce2424;
@ -283,6 +278,39 @@ hr { color: silver; border: 1px solid silver; }
.colstack_left:empty { display: none; }
.colstack_right:empty { display: none; }
.colstack_grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
/*grid-gap: 15px;*/
grid-gap: 12px;
margin-left: 5px;
}
.grid_item {
border: 1px solid #ccc;
word-wrap: break-word;
background-color: white;
width: 100%;
overflow: hidden;
}
.grid_stat, .grid_istat {
/*padding-top: 15px;*/
text-align: center;
/*padding-bottom: 15px;
font-size: 20px;*/
padding-top: 12px;
padding-bottom: 12px;
font-size: 16px;
}
.grid_istat {
/*margin-bottom: 10px;*/
margin-bottom: 5px;
}
.stat_green { background-color: lightgreen; border-color: green; }
.stat_orange { background-color: #ffe4b3; border-color: orange; }
.stat_red { background-color: #ffb2b2; border-color: red; }
.stat_disabled { background-color: lightgray; border-color: gray; }
.colitem
{
padding-left: 8px;
@ -302,10 +330,7 @@ hr { color: silver; border: 1px solid silver; }
text-decoration: none;
color: black;
}
.colitem a:hover
{
color: silver;
}
.colitem a:hover { color: silver; }
.col_left
{

View File

@ -178,7 +178,7 @@ li a
.colblock_left:empty { display: none; }
.colblock_right:empty { display: none; }
/* The new method of doing columns layouts, colblock is now deprecated */
/* The new method of doing columns layouts, colblock is now deprecated :( */
.colstack_left
{
float: left;
@ -205,6 +205,39 @@ li a
.colstack_left:empty { display: none; }
.colstack_right:empty { display: none; }
.colstack_grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
/*grid-gap: 15px;*/
grid-gap: 12px;
margin-left: 5px;
}
.grid_item {
border: 1px solid #ccc;
word-wrap: break-word;
background-color: white;
width: 100%;
overflow: hidden;
}
.grid_stat, .grid_istat {
/*padding-top: 15px;*/
text-align: center;
/*padding-bottom: 15px;
font-size: 20px;*/
padding-top: 12px;
padding-bottom: 12px;
font-size: 16px;
}
.grid_istat {
/*margin-bottom: 10px;*/
margin-bottom: 5px;
}
.stat_green { background-color: lightgreen; border-color: green; }
.stat_orange { background-color: #ffe4b3; border-color: orange; }
.stat_red { background-color: #ffb2b2; border-color: red; }
.stat_disabled { background-color: lightgray; border-color: gray; }
.rowitem
{
width: 100%;

View File

@ -167,7 +167,7 @@ li a
.colblock_left:empty { display: none; }
.colblock_right:empty { display: none; }
/* The new method of doing columns layouts, colblock is now deprecated */
/* The new method of doing columns layouts, colblock is now deprecated :( */
.colstack_left
{
float: left;
@ -194,6 +194,39 @@ li a
.colstack_left:empty { display: none; }
.colstack_right:empty { display: none; }
.colstack_grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
/*grid-gap: 15px;*/
grid-gap: 12px;
margin-left: 5px;
}
.grid_item {
border: 1px solid #ccc;
word-wrap: break-word;
background-color: white;
width: 100%;
overflow: hidden;
}
.grid_stat, .grid_istat {
/*padding-top: 15px;*/
text-align: center;
/*padding-bottom: 15px;
font-size: 20px;*/
padding-top: 12px;
padding-bottom: 12px;
font-size: 16px;
}
.grid_istat {
/*margin-bottom: 10px;*/
margin-bottom: 5px;
}
.stat_green { background-color: lightgreen; border-color: lightgreen; }
.stat_orange { background-color: #ffe4b3; border-color: #ffe4b3; }
.stat_red { background-color: #ffb2b2; border-color: #ffb2b2; }
.stat_disabled { background-color: lightgray; border-color: lightgray; }
.rowhead { font-family: cursive; }
.rowitem
{

View File

@ -167,7 +167,7 @@ li a
.colblock_left:empty { display: none; }
.colblock_right:empty { display: none; }
/* The new method of doing columns layouts, colblock is now deprecated */
/* The new method of doing columns layouts, colblock is now deprecated :( */
.colstack_left
{
float: left;
@ -194,6 +194,39 @@ li a
.colstack_left:empty { display: none; }
.colstack_right:empty { display: none; }
.colstack_grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
/*grid-gap: 15px;*/
grid-gap: 12px;
margin-left: 5px;
}
.grid_item {
border: 1px solid #ccc;
word-wrap: break-word;
background-color: white;
width: 100%;
overflow: hidden;
}
.grid_stat, .grid_istat {
/*padding-top: 15px;*/
text-align: center;
/*padding-bottom: 15px;
font-size: 20px;*/
padding-top: 12px;
padding-bottom: 12px;
font-size: 16px;
}
.grid_istat {
/*margin-bottom: 10px;*/
margin-bottom: 5px;
}
.stat_green { background-color: lightgreen; border-color: lightgreen; }
.stat_orange { background-color: #ffe4b3; border-color: #ffe4b3; }
.stat_red { background-color: #ffb2b2; border-color: #ffb2b2; }
.stat_disabled { background-color: lightgray; border-color: lightgray; }
.rowitem
{
width: 100%;

View File

@ -2,3 +2,5 @@ echo "Updating the MySQL Driver"
go get -u github.com/go-sql-driver/mysql
echo "Updating bcrypt"
go get -u golang.org/x/crypto/bcrypt
echo "Updating gopsutil"
go get -u github.com/shirou/gopsutil

View File

@ -13,5 +13,19 @@ if %errorlevel% neq 0 (
exit /b %errorlevel%
)
echo Updating wmi (dependency for gopsutil)
go get -u github.com/StackExchange/wmi
if %errorlevel% neq 0 (
pause
exit /b %errorlevel%
)
echo Updating gopsutil
go get -u github.com/shirou/gopsutil
if %errorlevel% neq 0 (
pause
exit /b %errorlevel%
)
echo The dependencies were successfully updated
pause

View File

@ -6,10 +6,31 @@ import "os"
import "math"
import "strings"
import "unicode"
import "strconv"
import "encoding/base64"
import "crypto/rand"
import "net/smtp"
type Version struct
{
Major int
Minor int
Patch int
Tag string
TagID int
}
func (version *Version) String() (out string) {
out = strconv.Itoa(version.Major) + "." + strconv.Itoa(version.Minor) + "." + strconv.Itoa(version.Patch)
if version.Tag != "" {
out += "-" + version.Tag
if version.TagID != 0 {
out += strconv.Itoa(version.TagID)
}
}
return
}
// Generate a cryptographically secure set of random bytes..
func GenerateSafeString(length int) (string, error) {
rb := make([]byte,length)
@ -66,6 +87,32 @@ func relative_time(in string) (string, error) {
}
}
func convert_byte_unit(bytes float64) (float64,string) {
switch
{
case bytes >= float64(terabyte): return bytes / float64(terabyte), "TB"
case bytes >= float64(gigabyte): return bytes / float64(gigabyte), "GB"
case bytes >= float64(megabyte): return bytes / float64(megabyte), "MB"
case bytes >= float64(kilobyte): return bytes / float64(kilobyte), "KB"
default: return bytes, " bytes"
}
}
func convert_byte_in_unit(bytes float64,unit string) (count float64) {
switch(unit) {
case "TB": count = bytes / float64(terabyte)
case "GB": count = bytes / float64(gigabyte)
case "MB": count = bytes / float64(megabyte)
case "KB": count = bytes / float64(kilobyte)
default: count = 0.1
}
if count < 0.1 {
count = 0.1
}
return
}
func SendEmail(email string, subject string, msg string) (res bool) {
// This hook is useful for plugin_sendmail or for testing tools. Possibly to hook it into some sort of mail server?
if vhooks["email_send_intercept"] != nil {