Fixed the Cosora Alerts CSS.
Fixed the topic edit input CSS. You can now filter the time range of the routes list. You can now filter the time range of the agents list. Renamed operations.log to ops.log Moved the account routes into the new router. Log suspicious requests. Removed some duplicated code in the control panel routes. Fixed a bug in the User Editor where the Administrator group didn't show up. Users are now force logged out when an admin changes their password.
This commit is contained in:
parent
cce87e2d70
commit
0416b1ed91
|
@ -182,6 +182,7 @@ type PanelAnalyticsRoutesPage struct {
|
||||||
Stats PanelStats
|
Stats PanelStats
|
||||||
Zone string
|
Zone string
|
||||||
ItemList []PanelAnalyticsRoutesItem
|
ItemList []PanelAnalyticsRoutesItem
|
||||||
|
TimeRange string
|
||||||
}
|
}
|
||||||
|
|
||||||
type PanelAnalyticsAgentsItem struct {
|
type PanelAnalyticsAgentsItem struct {
|
||||||
|
@ -196,6 +197,7 @@ type PanelAnalyticsAgentsPage struct {
|
||||||
Stats PanelStats
|
Stats PanelStats
|
||||||
Zone string
|
Zone string
|
||||||
ItemList []PanelAnalyticsAgentsItem
|
ItemList []PanelAnalyticsAgentsItem
|
||||||
|
TimeRange string
|
||||||
}
|
}
|
||||||
|
|
||||||
type PanelAnalyticsRoutePage struct {
|
type PanelAnalyticsRoutePage struct {
|
||||||
|
|
|
@ -97,6 +97,11 @@ var RouteMap = map[string]interface{}{
|
||||||
"routeProfileReplyCreateSubmit": routeProfileReplyCreateSubmit,
|
"routeProfileReplyCreateSubmit": routeProfileReplyCreateSubmit,
|
||||||
"routeProfileReplyEditSubmit": routeProfileReplyEditSubmit,
|
"routeProfileReplyEditSubmit": routeProfileReplyEditSubmit,
|
||||||
"routeProfileReplyDeleteSubmit": routeProfileReplyDeleteSubmit,
|
"routeProfileReplyDeleteSubmit": routeProfileReplyDeleteSubmit,
|
||||||
|
"routeLogin": routeLogin,
|
||||||
|
"routeRegister": routeRegister,
|
||||||
|
"routeLogout": routeLogout,
|
||||||
|
"routeLoginSubmit": routeLoginSubmit,
|
||||||
|
"routeRegisterSubmit": routeRegisterSubmit,
|
||||||
"routeDynamic": routeDynamic,
|
"routeDynamic": routeDynamic,
|
||||||
"routeUploads": routeUploads,
|
"routeUploads": routeUploads,
|
||||||
}
|
}
|
||||||
|
@ -185,8 +190,13 @@ var routeMapEnum = map[string]int{
|
||||||
"routeProfileReplyCreateSubmit": 79,
|
"routeProfileReplyCreateSubmit": 79,
|
||||||
"routeProfileReplyEditSubmit": 80,
|
"routeProfileReplyEditSubmit": 80,
|
||||||
"routeProfileReplyDeleteSubmit": 81,
|
"routeProfileReplyDeleteSubmit": 81,
|
||||||
"routeDynamic": 82,
|
"routeLogin": 82,
|
||||||
"routeUploads": 83,
|
"routeRegister": 83,
|
||||||
|
"routeLogout": 84,
|
||||||
|
"routeLoginSubmit": 85,
|
||||||
|
"routeRegisterSubmit": 86,
|
||||||
|
"routeDynamic": 87,
|
||||||
|
"routeUploads": 88,
|
||||||
}
|
}
|
||||||
var reverseRouteMapEnum = map[int]string{
|
var reverseRouteMapEnum = map[int]string{
|
||||||
0: "routeAPI",
|
0: "routeAPI",
|
||||||
|
@ -271,8 +281,13 @@ var reverseRouteMapEnum = map[int]string{
|
||||||
79: "routeProfileReplyCreateSubmit",
|
79: "routeProfileReplyCreateSubmit",
|
||||||
80: "routeProfileReplyEditSubmit",
|
80: "routeProfileReplyEditSubmit",
|
||||||
81: "routeProfileReplyDeleteSubmit",
|
81: "routeProfileReplyDeleteSubmit",
|
||||||
82: "routeDynamic",
|
82: "routeLogin",
|
||||||
83: "routeUploads",
|
83: "routeRegister",
|
||||||
|
84: "routeLogout",
|
||||||
|
85: "routeLoginSubmit",
|
||||||
|
86: "routeRegisterSubmit",
|
||||||
|
87: "routeDynamic",
|
||||||
|
88: "routeUploads",
|
||||||
}
|
}
|
||||||
var agentMapEnum = map[string]int{
|
var agentMapEnum = map[string]int{
|
||||||
"unknown": 0,
|
"unknown": 0,
|
||||||
|
@ -379,6 +394,19 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
var prefix, extraData string
|
var prefix, extraData string
|
||||||
prefix = req.URL.Path[0:strings.IndexByte(req.URL.Path[1:],'/') + 1]
|
prefix = req.URL.Path[0:strings.IndexByte(req.URL.Path[1:],'/') + 1]
|
||||||
if req.URL.Path[len(req.URL.Path) - 1] != '/' {
|
if req.URL.Path[len(req.URL.Path) - 1] != '/' {
|
||||||
|
// TODO: Cover more suspicious strings and at a lower layer than this and more efficiently
|
||||||
|
if strings.Contains(req.URL.Path,"'") || strings.Contains(req.URL.Path,";") || strings.Contains(req.URL.Path,"\"") || strings.Contains(req.URL.Path,"`") || strings.Contains(req.URL.Path,"%") {
|
||||||
|
log.Print("Suspicious UA: ", req.UserAgent())
|
||||||
|
log.Print("Method: ", req.Method)
|
||||||
|
for key, value := range req.Header {
|
||||||
|
for _, vvalue := range value {
|
||||||
|
log.Print("Header '" + key + "': " + vvalue + "!!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Print("req.URL.Path: ", req.URL.Path)
|
||||||
|
log.Print("req.Referer(): ", req.Referer())
|
||||||
|
log.Print("req.RemoteAddr: ", req.RemoteAddr)
|
||||||
|
}
|
||||||
extraData = req.URL.Path[strings.LastIndexByte(req.URL.Path,'/') + 1:]
|
extraData = req.URL.Path[strings.LastIndexByte(req.URL.Path,'/') + 1:]
|
||||||
req.URL.Path = req.URL.Path[:strings.LastIndexByte(req.URL.Path,'/') + 1]
|
req.URL.Path = req.URL.Path[:strings.LastIndexByte(req.URL.Path,'/') + 1]
|
||||||
}
|
}
|
||||||
|
@ -775,9 +803,21 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
common.RouteViewCounter.Bump(36)
|
common.RouteViewCounter.Bump(36)
|
||||||
err = routePanelAnalyticsViews(w,req,user)
|
err = routePanelAnalyticsViews(w,req,user)
|
||||||
case "/panel/analytics/routes/":
|
case "/panel/analytics/routes/":
|
||||||
|
err = common.ParseForm(w,req,user)
|
||||||
|
if err != nil {
|
||||||
|
router.handleError(err,w,req,user)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
common.RouteViewCounter.Bump(37)
|
common.RouteViewCounter.Bump(37)
|
||||||
err = routePanelAnalyticsRoutes(w,req,user)
|
err = routePanelAnalyticsRoutes(w,req,user)
|
||||||
case "/panel/analytics/agents/":
|
case "/panel/analytics/agents/":
|
||||||
|
err = common.ParseForm(w,req,user)
|
||||||
|
if err != nil {
|
||||||
|
router.handleError(err,w,req,user)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
common.RouteViewCounter.Bump(38)
|
common.RouteViewCounter.Bump(38)
|
||||||
err = routePanelAnalyticsAgents(w,req,user)
|
err = routePanelAnalyticsAgents(w,req,user)
|
||||||
case "/panel/analytics/route/":
|
case "/panel/analytics/route/":
|
||||||
|
@ -1298,6 +1338,51 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
router.handleError(err,w,req,user)
|
router.handleError(err,w,req,user)
|
||||||
}
|
}
|
||||||
|
case "/accounts":
|
||||||
|
switch(req.URL.Path) {
|
||||||
|
case "/accounts/login/":
|
||||||
|
common.RouteViewCounter.Bump(82)
|
||||||
|
err = routeLogin(w,req,user)
|
||||||
|
case "/accounts/create/":
|
||||||
|
common.RouteViewCounter.Bump(83)
|
||||||
|
err = routeRegister(w,req,user)
|
||||||
|
case "/accounts/logout/":
|
||||||
|
err = common.NoSessionMismatch(w,req,user)
|
||||||
|
if err != nil {
|
||||||
|
router.handleError(err,w,req,user)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = common.MemberOnly(w,req,user)
|
||||||
|
if err != nil {
|
||||||
|
router.handleError(err,w,req,user)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
common.RouteViewCounter.Bump(84)
|
||||||
|
err = routeLogout(w,req,user)
|
||||||
|
case "/accounts/login/submit/":
|
||||||
|
err = common.ParseForm(w,req,user)
|
||||||
|
if err != nil {
|
||||||
|
router.handleError(err,w,req,user)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
common.RouteViewCounter.Bump(85)
|
||||||
|
err = routeLoginSubmit(w,req,user)
|
||||||
|
case "/accounts/create/submit/":
|
||||||
|
err = common.ParseForm(w,req,user)
|
||||||
|
if err != nil {
|
||||||
|
router.handleError(err,w,req,user)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
common.RouteViewCounter.Bump(86)
|
||||||
|
err = routeRegisterSubmit(w,req,user)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
router.handleError(err,w,req,user)
|
||||||
|
}
|
||||||
/*case "/sitemaps": // TODO: Count these views
|
/*case "/sitemaps": // TODO: Count these views
|
||||||
req.URL.Path += extraData
|
req.URL.Path += extraData
|
||||||
err = sitemapSwitch(w,req)
|
err = sitemapSwitch(w,req)
|
||||||
|
@ -1309,7 +1394,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
common.NotFound(w,req)
|
common.NotFound(w,req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
common.RouteViewCounter.Bump(83)
|
common.RouteViewCounter.Bump(88)
|
||||||
req.URL.Path += extraData
|
req.URL.Path += extraData
|
||||||
// TODO: Find a way to propagate errors up from this?
|
// TODO: Find a way to propagate errors up from this?
|
||||||
router.UploadHandler(w,req) // TODO: Count these views
|
router.UploadHandler(w,req) // TODO: Count these views
|
||||||
|
@ -1353,7 +1438,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
router.RUnlock()
|
router.RUnlock()
|
||||||
|
|
||||||
if ok {
|
if ok {
|
||||||
common.RouteViewCounter.Bump(82) // TODO: Be more specific about *which* dynamic route it is
|
common.RouteViewCounter.Bump(87) // TODO: Be more specific about *which* dynamic route it is
|
||||||
req.URL.Path += extraData
|
req.URL.Path += extraData
|
||||||
err = handle(w,req,user)
|
err = handle(w,req,user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
11
main.go
11
main.go
|
@ -130,7 +130,7 @@ func main() {
|
||||||
|
|
||||||
// TODO: Have a file for each run with the time/date the server started as the file name?
|
// TODO: Have a file for each run with the time/date the server started as the file name?
|
||||||
// TODO: Log panics with recover()
|
// TODO: Log panics with recover()
|
||||||
f, err := os.OpenFile("./operations.log", os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0755)
|
f, err := os.OpenFile("./ops.log", os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0755)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -303,15 +303,6 @@ func main() {
|
||||||
// TODO: Move these routes into the new routes list
|
// TODO: Move these routes into the new routes list
|
||||||
log.Print("Initialising the router")
|
log.Print("Initialising the router")
|
||||||
router = NewGenRouter(http.FileServer(http.Dir("./uploads")))
|
router = NewGenRouter(http.FileServer(http.Dir("./uploads")))
|
||||||
|
|
||||||
// Accounts
|
|
||||||
router.HandleFunc("/accounts/login/", routeLogin)
|
|
||||||
router.HandleFunc("/accounts/create/", routeRegister)
|
|
||||||
router.HandleFunc("/accounts/logout/", routeLogout)
|
|
||||||
router.HandleFunc("/accounts/login/submit/", routeLoginSubmit)
|
|
||||||
router.HandleFunc("/accounts/create/submit/", routeRegisterSubmit)
|
|
||||||
//router.HandleFunc("/accounts/list/", routeLogin) // Redirect /accounts/ and /user/ to here.. // Get a list of all of the accounts on the forum
|
|
||||||
|
|
||||||
router.HandleFunc("/ws/", routeWebsockets)
|
router.HandleFunc("/ws/", routeWebsockets)
|
||||||
|
|
||||||
log.Print("Initialising the plugins")
|
log.Print("Initialising the plugins")
|
||||||
|
|
|
@ -16,11 +16,25 @@ import (
|
||||||
"./common"
|
"./common"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Experimenting
|
||||||
|
/*func memberRenderTemplate(tmplName string, themeName string, w http.ResponseWriter, r *http.Request, user common.User, pi interface{}) common.RouteError {
|
||||||
|
if common.PreRenderHooks["pre_render_"+tmplName] != nil {
|
||||||
|
if common.RunPreRenderHook("pre_render_"+tmplName, w, r, &user, pi) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err := common.RunThemeTemplate(themeName, tmplName, pi, w)
|
||||||
|
if err != nil {
|
||||||
|
return common.InternalError(err, w, r)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}*/
|
||||||
|
|
||||||
// ? - Should we add a new permission or permission zone (like per-forum permissions) specifically for profile comment creation
|
// ? - Should we add a new permission or permission zone (like per-forum permissions) specifically for profile comment creation
|
||||||
// ? - Should we allow banned users to make reports? How should we handle report abuse?
|
// ? - Should we allow banned users to make reports? How should we handle report abuse?
|
||||||
// TODO: Add a permission to stop certain users from using custom avatars
|
// TODO: Add a permission to stop certain users from using custom avatars
|
||||||
// ? - Log username changes and put restrictions on this?
|
// ? - Log username changes and put restrictions on this?
|
||||||
|
|
||||||
func routeTopicCreate(w http.ResponseWriter, r *http.Request, user common.User, sfid string) common.RouteError {
|
func routeTopicCreate(w http.ResponseWriter, r *http.Request, user common.User, sfid string) common.RouteError {
|
||||||
var fid int
|
var fid int
|
||||||
var err error
|
var err error
|
||||||
|
|
|
@ -834,39 +834,9 @@ func routePanelAnalyticsPosts(w http.ResponseWriter, r *http.Request, user commo
|
||||||
headerVars.Stylesheets = append(headerVars.Stylesheets, "chartist/chartist.min.css")
|
headerVars.Stylesheets = append(headerVars.Stylesheets, "chartist/chartist.min.css")
|
||||||
headerVars.Scripts = append(headerVars.Scripts, "chartist/chartist.min.js")
|
headerVars.Scripts = append(headerVars.Scripts, "chartist/chartist.min.js")
|
||||||
|
|
||||||
var timeQuantity = 6
|
timeRange, err := panelAnalyticsTimeRange(r.FormValue("timeRange"))
|
||||||
var timeUnit = "hour"
|
if err != nil {
|
||||||
var timeSlices = 12
|
return common.LocalError(err.Error(), w, r, user)
|
||||||
var sliceWidth = 60 * 30
|
|
||||||
var timeRange = "six-hours"
|
|
||||||
|
|
||||||
switch r.FormValue("timeRange") {
|
|
||||||
case "one-month":
|
|
||||||
timeQuantity = 30
|
|
||||||
timeUnit = "day"
|
|
||||||
timeSlices = 30
|
|
||||||
sliceWidth = 60 * 60 * 24
|
|
||||||
timeRange = "one-month"
|
|
||||||
case "two-days": // Two days is experimental
|
|
||||||
timeQuantity = 2
|
|
||||||
timeUnit = "day"
|
|
||||||
timeSlices = 24
|
|
||||||
sliceWidth = 60 * 60 * 2
|
|
||||||
timeRange = "two-days"
|
|
||||||
case "one-day":
|
|
||||||
timeQuantity = 1
|
|
||||||
timeUnit = "day"
|
|
||||||
timeSlices = 24
|
|
||||||
sliceWidth = 60 * 60
|
|
||||||
timeRange = "one-day"
|
|
||||||
case "twelve-hours":
|
|
||||||
timeQuantity = 12
|
|
||||||
timeSlices = 24
|
|
||||||
timeRange = "twelve-hours"
|
|
||||||
case "six-hours", "":
|
|
||||||
timeRange = "six-hours"
|
|
||||||
default:
|
|
||||||
return common.LocalError("Unknown time range", w, r, user)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var revLabelList []int64
|
var revLabelList []int64
|
||||||
|
@ -874,8 +844,8 @@ func routePanelAnalyticsPosts(w http.ResponseWriter, r *http.Request, user commo
|
||||||
var viewMap = make(map[int64]int64)
|
var viewMap = make(map[int64]int64)
|
||||||
var currentTime = time.Now().Unix()
|
var currentTime = time.Now().Unix()
|
||||||
|
|
||||||
for i := 1; i <= timeSlices; i++ {
|
for i := 1; i <= timeRange.Slices; i++ {
|
||||||
var label = currentTime - int64(i*sliceWidth)
|
var label = currentTime - int64(i*timeRange.SliceWidth)
|
||||||
revLabelList = append(revLabelList, label)
|
revLabelList = append(revLabelList, label)
|
||||||
viewMap[label] = 0
|
viewMap[label] = 0
|
||||||
}
|
}
|
||||||
|
@ -887,7 +857,7 @@ func routePanelAnalyticsPosts(w http.ResponseWriter, r *http.Request, user commo
|
||||||
log.Print("in routePanelAnalyticsPosts")
|
log.Print("in routePanelAnalyticsPosts")
|
||||||
|
|
||||||
acc := qgen.Builder.Accumulator()
|
acc := qgen.Builder.Accumulator()
|
||||||
rows, err := acc.Select("postchunks").Columns("count, createdAt").DateCutoff("createdAt", timeQuantity, timeUnit).Query()
|
rows, err := acc.Select("postchunks").Columns("count, createdAt").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query()
|
||||||
if err != nil && err != ErrNoRows {
|
if err != nil && err != ErrNoRows {
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
|
@ -925,7 +895,7 @@ func routePanelAnalyticsPosts(w http.ResponseWriter, r *http.Request, user commo
|
||||||
graph := common.PanelTimeGraph{Series: viewList, Labels: labelList}
|
graph := common.PanelTimeGraph{Series: viewList, Labels: labelList}
|
||||||
log.Printf("graph: %+v\n", graph)
|
log.Printf("graph: %+v\n", graph)
|
||||||
|
|
||||||
pi := common.PanelAnalyticsPage{common.GetTitlePhrase("panel_analytics"), user, headerVars, stats, "analytics", graph, viewItems, timeRange}
|
pi := common.PanelAnalyticsPage{common.GetTitlePhrase("panel_analytics"), user, headerVars, stats, "analytics", graph, viewItems, timeRange.Range}
|
||||||
return panelRenderTemplate("panel_analytics_posts", w, r, user, &pi)
|
return panelRenderTemplate("panel_analytics_posts", w, r, user, &pi)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -936,8 +906,13 @@ func routePanelAnalyticsRoutes(w http.ResponseWriter, r *http.Request, user comm
|
||||||
}
|
}
|
||||||
var routeMap = make(map[string]int)
|
var routeMap = make(map[string]int)
|
||||||
|
|
||||||
|
timeRange, err := panelAnalyticsTimeRange(r.FormValue("timeRange"))
|
||||||
|
if err != nil {
|
||||||
|
return common.LocalError(err.Error(), w, r, user)
|
||||||
|
}
|
||||||
|
|
||||||
acc := qgen.Builder.Accumulator()
|
acc := qgen.Builder.Accumulator()
|
||||||
rows, err := acc.Select("viewchunks").Columns("count, route").Where("route != ''").DateCutoff("createdAt", 1, "day").Query()
|
rows, err := acc.Select("viewchunks").Columns("count, route").Where("route != ''").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query()
|
||||||
if err != nil && err != ErrNoRows {
|
if err != nil && err != ErrNoRows {
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
|
@ -969,7 +944,7 @@ func routePanelAnalyticsRoutes(w http.ResponseWriter, r *http.Request, user comm
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pi := common.PanelAnalyticsRoutesPage{common.GetTitlePhrase("panel_analytics"), user, headerVars, stats, "analytics", routeItems}
|
pi := common.PanelAnalyticsRoutesPage{common.GetTitlePhrase("panel_analytics"), user, headerVars, stats, "analytics", routeItems, timeRange.Range}
|
||||||
return panelRenderTemplate("panel_analytics_routes", w, r, user, &pi)
|
return panelRenderTemplate("panel_analytics_routes", w, r, user, &pi)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -980,8 +955,13 @@ func routePanelAnalyticsAgents(w http.ResponseWriter, r *http.Request, user comm
|
||||||
}
|
}
|
||||||
var agentMap = make(map[string]int)
|
var agentMap = make(map[string]int)
|
||||||
|
|
||||||
|
timeRange, err := panelAnalyticsTimeRange(r.FormValue("timeRange"))
|
||||||
|
if err != nil {
|
||||||
|
return common.LocalError(err.Error(), w, r, user)
|
||||||
|
}
|
||||||
|
|
||||||
acc := qgen.Builder.Accumulator()
|
acc := qgen.Builder.Accumulator()
|
||||||
rows, err := acc.Select("viewchunks_agents").Columns("count, browser").DateCutoff("createdAt", 1, "day").Query()
|
rows, err := acc.Select("viewchunks_agents").Columns("count, browser").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query()
|
||||||
if err != nil && err != ErrNoRows {
|
if err != nil && err != ErrNoRows {
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
|
@ -1013,7 +993,7 @@ func routePanelAnalyticsAgents(w http.ResponseWriter, r *http.Request, user comm
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pi := common.PanelAnalyticsAgentsPage{common.GetTitlePhrase("panel_analytics"), user, headerVars, stats, "analytics", agentItems}
|
pi := common.PanelAnalyticsAgentsPage{common.GetTitlePhrase("panel_analytics"), user, headerVars, stats, "analytics", agentItems, timeRange.Range}
|
||||||
return panelRenderTemplate("panel_analytics_agents", w, r, user, &pi)
|
return panelRenderTemplate("panel_analytics_agents", w, r, user, &pi)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1505,7 +1485,7 @@ func routePanelUsersEdit(w http.ResponseWriter, r *http.Request, user common.Use
|
||||||
}
|
}
|
||||||
|
|
||||||
var groupList []interface{}
|
var groupList []interface{}
|
||||||
for _, group := range groups[1:] {
|
for _, group := range groups {
|
||||||
if !user.Perms.EditUserGroupAdmin && group.IsAdmin {
|
if !user.Perms.EditUserGroupAdmin && group.IsAdmin {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -1600,6 +1580,8 @@ func routePanelUsersEditSubmit(w http.ResponseWriter, r *http.Request, user comm
|
||||||
|
|
||||||
if newpassword != "" {
|
if newpassword != "" {
|
||||||
common.SetPassword(targetUser.ID, newpassword)
|
common.SetPassword(targetUser.ID, newpassword)
|
||||||
|
// Log the user out as a safety precaution
|
||||||
|
common.Auth.ForceLogout(targetUser.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
targetUser.CacheRemove()
|
targetUser.CacheRemove()
|
||||||
|
|
|
@ -181,6 +181,7 @@ func main() {
|
||||||
for id, agent := range tmplVars.AllAgentNames {
|
for id, agent := range tmplVars.AllAgentNames {
|
||||||
tmplVars.AllAgentMap[agent] = id
|
tmplVars.AllAgentMap[agent] = id
|
||||||
}
|
}
|
||||||
|
var graveSym = "`"
|
||||||
|
|
||||||
var fileData = `// Code generated by. DO NOT EDIT.
|
var fileData = `// Code generated by. DO NOT EDIT.
|
||||||
/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */
|
/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */
|
||||||
|
@ -284,6 +285,19 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
var prefix, extraData string
|
var prefix, extraData string
|
||||||
prefix = req.URL.Path[0:strings.IndexByte(req.URL.Path[1:],'/') + 1]
|
prefix = req.URL.Path[0:strings.IndexByte(req.URL.Path[1:],'/') + 1]
|
||||||
if req.URL.Path[len(req.URL.Path) - 1] != '/' {
|
if req.URL.Path[len(req.URL.Path) - 1] != '/' {
|
||||||
|
// TODO: Cover more suspicious strings and at a lower layer than this and more efficiently
|
||||||
|
if strings.Contains(req.URL.Path,"'") || strings.Contains(req.URL.Path,";") || strings.Contains(req.URL.Path,"\"") || strings.Contains(req.URL.Path,"` + graveSym + `") || strings.Contains(req.URL.Path,"%") {
|
||||||
|
log.Print("Suspicious UA: ", req.UserAgent())
|
||||||
|
log.Print("Method: ", req.Method)
|
||||||
|
for key, value := range req.Header {
|
||||||
|
for _, vvalue := range value {
|
||||||
|
log.Print("Header '" + key + "': " + vvalue + "!!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Print("req.URL.Path: ", req.URL.Path)
|
||||||
|
log.Print("req.Referer(): ", req.Referer())
|
||||||
|
log.Print("req.RemoteAddr: ", req.RemoteAddr)
|
||||||
|
}
|
||||||
extraData = req.URL.Path[strings.LastIndexByte(req.URL.Path,'/') + 1:]
|
extraData = req.URL.Path[strings.LastIndexByte(req.URL.Path,'/') + 1:]
|
||||||
req.URL.Path = req.URL.Path[:strings.LastIndexByte(req.URL.Path,'/') + 1]
|
req.URL.Path = req.URL.Path[:strings.LastIndexByte(req.URL.Path,'/') + 1]
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ func routes() {
|
||||||
buildTopicRoutes()
|
buildTopicRoutes()
|
||||||
buildReplyRoutes()
|
buildReplyRoutes()
|
||||||
buildProfileReplyRoutes()
|
buildProfileReplyRoutes()
|
||||||
|
buildAccountRoutes()
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Test the email token route
|
// TODO: Test the email token route
|
||||||
|
@ -101,6 +102,19 @@ func buildProfileReplyRoutes() {
|
||||||
addRouteGroup(pReplyGroup)
|
addRouteGroup(pReplyGroup)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func buildAccountRoutes() {
|
||||||
|
//router.HandleFunc("/accounts/list/", routeLogin) // Redirect /accounts/ and /user/ to here.. // Get a list of all of the accounts on the forum
|
||||||
|
accReplyGroup := newRouteGroup("/accounts/")
|
||||||
|
accReplyGroup.Routes(
|
||||||
|
View("routeLogin", "/accounts/login/"),
|
||||||
|
View("routeRegister", "/accounts/create/"),
|
||||||
|
Action("routeLogout", "/accounts/logout/"),
|
||||||
|
AnonAction("routeLoginSubmit", "/accounts/login/submit/"), // TODO: Guard this with a token, maybe the IP hashed with a rotated key?
|
||||||
|
AnonAction("routeRegisterSubmit", "/accounts/create/submit/"),
|
||||||
|
)
|
||||||
|
addRouteGroup(accReplyGroup)
|
||||||
|
}
|
||||||
|
|
||||||
func buildPanelRoutes() {
|
func buildPanelRoutes() {
|
||||||
panelGroup := newRouteGroup("/panel/").Before("SuperModOnly")
|
panelGroup := newRouteGroup("/panel/").Before("SuperModOnly")
|
||||||
panelGroup.Routes(
|
panelGroup.Routes(
|
||||||
|
@ -138,8 +152,8 @@ func buildPanelRoutes() {
|
||||||
Action("routePanelUsersEditSubmit", "/panel/users/edit/submit/", "extraData"),
|
Action("routePanelUsersEditSubmit", "/panel/users/edit/submit/", "extraData"),
|
||||||
|
|
||||||
View("routePanelAnalyticsViews", "/panel/analytics/views/").Before("ParseForm"),
|
View("routePanelAnalyticsViews", "/panel/analytics/views/").Before("ParseForm"),
|
||||||
View("routePanelAnalyticsRoutes", "/panel/analytics/routes/"),
|
View("routePanelAnalyticsRoutes", "/panel/analytics/routes/").Before("ParseForm"),
|
||||||
View("routePanelAnalyticsAgents", "/panel/analytics/agents/"),
|
View("routePanelAnalyticsAgents", "/panel/analytics/agents/").Before("ParseForm"),
|
||||||
View("routePanelAnalyticsRouteViews", "/panel/analytics/route/", "extraData"),
|
View("routePanelAnalyticsRouteViews", "/panel/analytics/route/", "extraData"),
|
||||||
View("routePanelAnalyticsAgentViews", "/panel/analytics/agent/", "extraData"),
|
View("routePanelAnalyticsAgentViews", "/panel/analytics/agent/", "extraData"),
|
||||||
View("routePanelAnalyticsPosts", "/panel/analytics/posts/").Before("ParseForm"),
|
View("routePanelAnalyticsPosts", "/panel/analytics/posts/").Before("ParseForm"),
|
||||||
|
|
14
routes.go
14
routes.go
|
@ -746,10 +746,6 @@ func routeLoginSubmit(w http.ResponseWriter, r *http.Request, user common.User)
|
||||||
if user.Loggedin {
|
if user.Loggedin {
|
||||||
return common.LocalError("You're already logged in.", w, r, user)
|
return common.LocalError("You're already logged in.", w, r, user)
|
||||||
}
|
}
|
||||||
err := r.ParseForm()
|
|
||||||
if err != nil {
|
|
||||||
return common.LocalError("Bad Form", w, r, user)
|
|
||||||
}
|
|
||||||
|
|
||||||
username := html.EscapeString(strings.Replace(r.PostFormValue("username"), "\n", "", -1))
|
username := html.EscapeString(strings.Replace(r.PostFormValue("username"), "\n", "", -1))
|
||||||
uid, err := common.Auth.Authenticate(username, r.PostFormValue("password"))
|
uid, err := common.Auth.Authenticate(username, r.PostFormValue("password"))
|
||||||
|
@ -807,10 +803,6 @@ func routeRegister(w http.ResponseWriter, r *http.Request, user common.User) com
|
||||||
func routeRegisterSubmit(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
func routeRegisterSubmit(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||||
headerLite, _ := common.SimpleUserCheck(w, r, &user)
|
headerLite, _ := common.SimpleUserCheck(w, r, &user)
|
||||||
|
|
||||||
err := r.ParseForm()
|
|
||||||
if err != nil {
|
|
||||||
return common.LocalError("Bad Form", w, r, user)
|
|
||||||
}
|
|
||||||
username := html.EscapeString(strings.Replace(r.PostFormValue("username"), "\n", "", -1))
|
username := html.EscapeString(strings.Replace(r.PostFormValue("username"), "\n", "", -1))
|
||||||
if username == "" {
|
if username == "" {
|
||||||
return common.LocalError("You didn't put in a username.", w, r, user)
|
return common.LocalError("You didn't put in a username.", w, r, user)
|
||||||
|
@ -831,13 +823,15 @@ func routeRegisterSubmit(w http.ResponseWriter, r *http.Request, user common.Use
|
||||||
}
|
}
|
||||||
|
|
||||||
// ? Move this into Create()? What if we want to programatically set weak passwords for tests?
|
// ? Move this into Create()? What if we want to programatically set weak passwords for tests?
|
||||||
err = common.WeakPassword(password)
|
err := common.WeakPassword(password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.LocalError(err.Error(), w, r, user)
|
return common.LocalError(err.Error(), w, r, user)
|
||||||
}
|
}
|
||||||
|
|
||||||
confirmPassword := r.PostFormValue("confirm_password")
|
confirmPassword := r.PostFormValue("confirm_password")
|
||||||
log.Print("Registration Attempt! common.Username: " + username) // TODO: Add more controls over what is logged when?
|
if common.Dev.DebugMode {
|
||||||
|
log.Print("Registration Attempt! Username: " + username) // TODO: Add more controls over what is logged when?
|
||||||
|
}
|
||||||
|
|
||||||
// Do the two inputted passwords match..?
|
// Do the two inputted passwords match..?
|
||||||
if password != confirmPassword {
|
if password != confirmPassword {
|
||||||
|
|
2
run.bat
2
run.bat
|
@ -34,5 +34,5 @@ if %errorlevel% neq 0 (
|
||||||
echo Running Gosora
|
echo Running Gosora
|
||||||
gosora.exe
|
gosora.exe
|
||||||
rem Or you could redirect the output to a file
|
rem Or you could redirect the output to a file
|
||||||
rem gosora.exe > operations.log 2>&1
|
rem gosora.exe > ops.log 2>&1
|
||||||
pause
|
pause
|
|
@ -2,9 +2,20 @@
|
||||||
<div class="colstack panel_stack">
|
<div class="colstack panel_stack">
|
||||||
{{template "panel-menu.html" . }}
|
{{template "panel-menu.html" . }}
|
||||||
<main id="panel_dashboard_right" class="colstack_right">
|
<main id="panel_dashboard_right" class="colstack_right">
|
||||||
<div class="colstack_item colstack_head">
|
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/agents/" method="get">
|
||||||
<div class="rowitem"><a>User Agents (24 hours)</a></div>
|
<div class="colstack_item colstack_head">
|
||||||
</div>
|
<div class="rowitem">
|
||||||
|
<a>User Agents</a>
|
||||||
|
<select class="timeRangeSelector to_right" name="timeRange">
|
||||||
|
<option val="one-month"{{if eq .TimeRange "one-month"}} selected{{end}}>1 month</option>
|
||||||
|
<option val="two-days"{{if eq .TimeRange "two-days"}} selected{{end}}>2 days</option>
|
||||||
|
<option val="one-day"{{if eq .TimeRange "one-day"}} selected{{end}}>1 day</option>
|
||||||
|
<option val="twelve-hours"{{if eq .TimeRange "twelve-hours"}} selected{{end}}>12 hours</option>
|
||||||
|
<option val="six-hours"{{if eq .TimeRange "six-hours"}} selected{{end}}>6 hours</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
<div id="panel_analytics_agents" class="colstack_item rowlist">
|
<div id="panel_analytics_agents" class="colstack_item rowlist">
|
||||||
{{range .ItemList}}
|
{{range .ItemList}}
|
||||||
<div class="rowitem panel_compactrow editable_parent">
|
<div class="rowitem panel_compactrow editable_parent">
|
||||||
|
|
|
@ -2,9 +2,20 @@
|
||||||
<div class="colstack panel_stack">
|
<div class="colstack panel_stack">
|
||||||
{{template "panel-menu.html" . }}
|
{{template "panel-menu.html" . }}
|
||||||
<main id="panel_dashboard_right" class="colstack_right">
|
<main id="panel_dashboard_right" class="colstack_right">
|
||||||
<div class="colstack_item colstack_head">
|
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/routes/" method="get">
|
||||||
<div class="rowitem"><a>Routes (24 hours)</a></div>
|
<div class="colstack_item colstack_head">
|
||||||
</div>
|
<div class="rowitem">
|
||||||
|
<a>Routes</a>
|
||||||
|
<select class="timeRangeSelector to_right" name="timeRange">
|
||||||
|
<option val="one-month"{{if eq .TimeRange "one-month"}} selected{{end}}>1 month</option>
|
||||||
|
<option val="two-days"{{if eq .TimeRange "two-days"}} selected{{end}}>2 days</option>
|
||||||
|
<option val="one-day"{{if eq .TimeRange "one-day"}} selected{{end}}>1 day</option>
|
||||||
|
<option val="twelve-hours"{{if eq .TimeRange "twelve-hours"}} selected{{end}}>12 hours</option>
|
||||||
|
<option val="six-hours"{{if eq .TimeRange "six-hours"}} selected{{end}}>6 hours</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
<div id="panel_analytics_routes" class="colstack_item rowlist">
|
<div id="panel_analytics_routes" class="colstack_item rowlist">
|
||||||
{{range .ItemList}}
|
{{range .ItemList}}
|
||||||
<div class="rowitem panel_compactrow editable_parent">
|
<div class="rowitem panel_compactrow editable_parent">
|
||||||
|
|
|
@ -207,9 +207,12 @@ ul {
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
.alertItem.withAvatar .text {
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
.alertItem.withAvatar:not(:last-child) .text {
|
.alertItem.withAvatar:not(:last-child) .text {
|
||||||
border-bottom: 1px solid var(--element-border-color);
|
border-bottom: 1px solid var(--element-border-color);
|
||||||
margin-top: 8px;
|
padding-bottom: 16px;
|
||||||
}
|
}
|
||||||
.alertItem .bgsub {
|
.alertItem .bgsub {
|
||||||
width: 32px;
|
width: 32px;
|
||||||
|
@ -217,6 +220,9 @@ ul {
|
||||||
border-radius: 30px;
|
border-radius: 30px;
|
||||||
margin-right: 12px;
|
margin-right: 12px;
|
||||||
}
|
}
|
||||||
|
.alertItem.withAvatar:not(:first-child) {
|
||||||
|
padding-top: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
.rowblock, .colstack_head {
|
.rowblock, .colstack_head {
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
|
@ -820,6 +826,18 @@ textarea {
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.topic_item {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.topic_item .topic_name_input {
|
||||||
|
width: 100%;
|
||||||
|
padding-left: 12px;
|
||||||
|
margin-right: 12px;
|
||||||
|
}
|
||||||
|
.topic_item .formbutton {
|
||||||
|
margin-top: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
.post_item {
|
.post_item {
|
||||||
display: flex;
|
display: flex;
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
|
|
Loading…
Reference in New Issue