Improved the admin's interface
This commit is contained in:
parent
4138c85639
commit
6e1800aa5f
64
Admin/HandlerConfiguration.go
Normal file
64
Admin/HandlerConfiguration.go
Normal file
@ -0,0 +1,64 @@
|
||||
package Admin
|
||||
|
||||
import (
|
||||
"github.com/SommerEngineering/Ocean/ConfigurationDB"
|
||||
"github.com/SommerEngineering/Ocean/Log"
|
||||
LM "github.com/SommerEngineering/Ocean/Log/Meta"
|
||||
"github.com/SommerEngineering/Ocean/MimeTypes"
|
||||
"github.com/SommerEngineering/Ocean/Shutdown"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Handler for accessing the file upload function.
|
||||
func HandlerConfiguration(response http.ResponseWriter, request *http.Request) {
|
||||
|
||||
// Case: The system goes down now.
|
||||
if Shutdown.IsDown() {
|
||||
http.NotFound(response, request)
|
||||
return
|
||||
}
|
||||
|
||||
if strings.ToLower(request.Method) == `get` {
|
||||
//
|
||||
// Case: Send the website to the client
|
||||
//
|
||||
|
||||
// Read all configuration values:
|
||||
values := ConfigurationDB.ReadAll()
|
||||
|
||||
// Build the data type for the template:
|
||||
data := AdminWebConfiguration{}
|
||||
data.Configuration = values
|
||||
|
||||
// Write the MIME type and execute the template:
|
||||
MimeTypes.Write2HTTP(response, MimeTypes.TypeWebHTML)
|
||||
if executeError := AdminTemplates.ExecuteTemplate(response, `Configuration`, data); executeError != nil {
|
||||
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactCritical, LM.MessageNameEXECUTE, `Was not able to execute the configuration template.`, executeError.Error())
|
||||
}
|
||||
} else {
|
||||
//
|
||||
// Case: Receive the changed configuration
|
||||
//
|
||||
|
||||
// Read all configuration values:
|
||||
values := ConfigurationDB.ReadAll()
|
||||
|
||||
// Loop over all current known values:
|
||||
for _, value := range values {
|
||||
|
||||
// Read the new value from the client side:
|
||||
newValue := request.FormValue(value.Name)
|
||||
|
||||
// Store the new value:
|
||||
value.Value = newValue
|
||||
|
||||
// Update the database:
|
||||
ConfigurationDB.UpdateValue(value.Name, value)
|
||||
}
|
||||
|
||||
// Redirect the client to the admin's overview:
|
||||
defer http.Redirect(response, request, "/configuration", 302)
|
||||
}
|
||||
|
||||
}
|
78
Admin/HandlerFileUpload.go
Normal file
78
Admin/HandlerFileUpload.go
Normal file
@ -0,0 +1,78 @@
|
||||
package Admin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/SommerEngineering/Ocean/CustomerDB"
|
||||
"github.com/SommerEngineering/Ocean/Log"
|
||||
LM "github.com/SommerEngineering/Ocean/Log/Meta"
|
||||
"github.com/SommerEngineering/Ocean/MimeTypes"
|
||||
"github.com/SommerEngineering/Ocean/Shutdown"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Handler for accessing the file upload function.
|
||||
func HandlerFileUpload(response http.ResponseWriter, request *http.Request) {
|
||||
|
||||
// Case: The system goes down now.
|
||||
if Shutdown.IsDown() {
|
||||
http.NotFound(response, request)
|
||||
return
|
||||
}
|
||||
|
||||
if strings.ToLower(request.Method) == `get` {
|
||||
//
|
||||
// Case: Send the website to the client
|
||||
//
|
||||
|
||||
// Write the MIME type and execute the template:
|
||||
MimeTypes.Write2HTTP(response, MimeTypes.TypeWebHTML)
|
||||
if executeError := AdminTemplates.ExecuteTemplate(response, `FileUpload`, nil); executeError != nil {
|
||||
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactCritical, LM.MessageNameEXECUTE, `Was not able to execute the file upload template.`, executeError.Error())
|
||||
}
|
||||
} else {
|
||||
//
|
||||
// Case: Receive the file to upload
|
||||
//
|
||||
|
||||
if file, fileHeader, fileError := request.FormFile(`file`); fileError != nil {
|
||||
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactCritical, LM.MessageNameBROWSER, `Was not able to access the file uploaded.`, fileError.Error())
|
||||
} else {
|
||||
//
|
||||
// Case: Access was possible.
|
||||
//
|
||||
|
||||
// Get the GridFS from the database:
|
||||
dbSession, gridFS := CustomerDB.GridFS()
|
||||
defer dbSession.Close()
|
||||
|
||||
// Try to create the desired file at the grid file system:
|
||||
if newFile, errNewFile := gridFS.Create(fileHeader.Filename); errNewFile != nil {
|
||||
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactCritical, LM.MessageNameDATABASE, `Was not able to create the desired file at the grid file system.`, errNewFile.Error(), fmt.Sprintf("filename='%s'", fileHeader.Filename))
|
||||
} else {
|
||||
|
||||
// Close the files afterwards:
|
||||
defer file.Close()
|
||||
defer newFile.Close()
|
||||
|
||||
// Try to copy the file's content to the database:
|
||||
if _, errCopy := io.Copy(newFile, file); errCopy != nil {
|
||||
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactCritical, LM.MessageNameNETWORK, `Was not able to copy the desired file's content to the grid file system.`, errNewFile.Error(), fmt.Sprintf("filename='%s'", fileHeader.Filename))
|
||||
} else {
|
||||
// Try to determine the MIME type:
|
||||
if mimeType, errMime := MimeTypes.DetectType(fileHeader.Filename); errMime != nil {
|
||||
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityLow, LM.ImpactLow, LM.MessageNamePARSE, `Was not able to parse the desired file's MIME type.`, errMime.Error(), fmt.Sprintf("filename='%s'", fileHeader.Filename))
|
||||
} else {
|
||||
// Set also the MIME type in the database:
|
||||
newFile.SetContentType(mimeType.MimeType)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Redirect the client to the admin's overview:
|
||||
defer http.Redirect(response, request, "/", 302)
|
||||
}
|
||||
|
||||
}
|
@ -22,4 +22,12 @@ func init() {
|
||||
if _, err := AdminTemplates.Parse(Templates.Overview); err != nil {
|
||||
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactUnknown, LM.MessageNamePARSE, `Was not able to parse the template for the admin overview.`, err.Error())
|
||||
}
|
||||
|
||||
if _, err := AdminTemplates.Parse(Templates.FileUpload); err != nil {
|
||||
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactUnknown, LM.MessageNamePARSE, `Was not able to parse the template for the file upload.`, err.Error())
|
||||
}
|
||||
|
||||
if _, err := AdminTemplates.Parse(Templates.Configuration); err != nil {
|
||||
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactUnknown, LM.MessageNamePARSE, `Was not able to parse the template for the configuration.`, err.Error())
|
||||
}
|
||||
}
|
||||
|
9
Admin/Scheme.go
Normal file
9
Admin/Scheme.go
Normal file
@ -0,0 +1,9 @@
|
||||
package Admin
|
||||
|
||||
import (
|
||||
"github.com/SommerEngineering/Ocean/ConfigurationDB"
|
||||
)
|
||||
|
||||
type AdminWebConfiguration struct {
|
||||
Configuration []ConfigurationDB.ConfigurationDBEntry
|
||||
}
|
47
Admin/Templates/Configuration.go
Normal file
47
Admin/Templates/Configuration.go
Normal file
@ -0,0 +1,47 @@
|
||||
package Templates
|
||||
|
||||
var Configuration = `
|
||||
{{define "Configuration"}}
|
||||
<!DOCTYPE html>
|
||||
<!-- This site was created in Webflow. http://www.webflow.com-->
|
||||
<!-- Last Published: Fri Jun 26 2015 05:50:41 GMT+0000 (UTC) -->
|
||||
<html data-wf-site="547b44aa3e9ac2216ec5d048" data-wf-page="558ce60e0939295c77a362db">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Configuration</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="generator" content="Webflow">
|
||||
<link rel="stylesheet" type="text/css" href="/admin/css/normalize.css">
|
||||
<link rel="stylesheet" type="text/css" href="/admin/css/webflow.css">
|
||||
<link rel="stylesheet" type="text/css" href="/admin/css/admin.css">
|
||||
<script type="text/javascript" src="/admin/js/modernizr.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="w-section headercontainer">
|
||||
<h1>Cluster Configuration</h1>
|
||||
</div>
|
||||
<div class="w-container adminsection">
|
||||
<p class="introtext"><strong>Attention:</strong> This configuration applies to the whole cluster of your Ocean servers. Therefore, <strong>this is not</strong> an individual configuration for any single server! Thus, please consider beforehand if the desired change matches all of your servers.
|
||||
</p>
|
||||
<div class="w-form">
|
||||
<form id="configuration" name="configuration" data-name="configuration" method="post" action="/configuration">
|
||||
{{range .Configuration}}
|
||||
<label for="{{.Name}}">Configuration parameter: {{.Name}}</label>
|
||||
<input class="w-input" id="{{.Name}}" type="text" name="{{.Name}}" data-name="{{.Name}}" required="required" value="{{.Value}}" placeholder="{{.Value}}">
|
||||
{{end}}
|
||||
<input class="w-button button optionbuttons" type="submit" value="Apply all changes" data-wait="Please wait...">
|
||||
</form>
|
||||
<div class="w-form-done">
|
||||
<p>Thank you! Your submission has been received!</p>
|
||||
</div>
|
||||
<div class="w-form-fail">
|
||||
<p>Oops! Something went wrong while submitting the form</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="/admin/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="/admin/js/webflow.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
{{end}}
|
||||
`
|
44
Admin/Templates/FileUpload.go
Normal file
44
Admin/Templates/FileUpload.go
Normal file
@ -0,0 +1,44 @@
|
||||
package Templates
|
||||
|
||||
var FileUpload = `
|
||||
{{define "FileUpload"}}
|
||||
<!DOCTYPE html>
|
||||
<!-- This site was created in Webflow. http://www.webflow.com-->
|
||||
<!-- Last Published: Fri Jun 26 2015 05:50:41 GMT+0000 (UTC) -->
|
||||
<html data-wf-site="547b44aa3e9ac2216ec5d048" data-wf-page="558ce167e20f1f4d31c64577">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Upload a file</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="generator" content="Webflow">
|
||||
<link rel="stylesheet" type="text/css" href="/admin/css/normalize.css">
|
||||
<link rel="stylesheet" type="text/css" href="/admin/css/webflow.css">
|
||||
<link rel="stylesheet" type="text/css" href="/admin/css/admin.css">
|
||||
<script type="text/javascript" src="/admin/js/modernizr.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="w-section headercontainer">
|
||||
<h1>Upload a file</h1>
|
||||
</div>
|
||||
<div class="w-container adminsection">
|
||||
<p class="introtext">This function enables you to upload a file to the distributed file system of the MongoDB database. If the desired file is already present, a new revision of this file is created. Therefore, an already existing file gets never overwritten! Please consider, that the configured maximum size of the header of the admin web server (see configuration) forces the maximum file size. Thus, please check the current maximum. The default maximum is approx. 10 MB!</p>
|
||||
<div class="w-form">
|
||||
<form id="upload" name="upload" data-name="upload" method="post" action="/upload" enctype="multipart/form-data">
|
||||
<label for="file">Please select a file to upload:</label>
|
||||
<input class="w-input" id="file" type="file" name="file" data-name="file">
|
||||
<input class="w-button button optionbuttons" type="submit" value="Upload this file" data-wait="Please wait...">
|
||||
</form>
|
||||
<div class="w-form-done">
|
||||
<p>Thank you! Your submission has been received!</p>
|
||||
</div>
|
||||
<div class="w-form-fail">
|
||||
<p>Oops! Something went wrong while submitting the form</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="/admin/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="/admin/js/webflow.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
{{end}}
|
||||
`
|
@ -24,8 +24,10 @@ var Overview = `
|
||||
<div class="w-row">
|
||||
<div class="w-col w-col-4"><a class="button adminbutton" href="/log">Logging Viewer</a>
|
||||
</div>
|
||||
<div class="w-col w-col-4"></div>
|
||||
<div class="w-col w-col-4"></div>
|
||||
<div class="w-col w-col-4"><a class="button adminbutton" href="/upload">Upload a file</a>
|
||||
</div>
|
||||
<div class="w-col w-col-4"><a class="button adminbutton" href="/configuration">Configuration</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="/admin/js/jquery.min.js"></script>
|
||||
|
@ -16,7 +16,7 @@ func checkConfiguration() {
|
||||
CheckSingleConfigurationPresentsAndAddIfMissing(`AdminWebServerEnabled`, `True`)
|
||||
CheckSingleConfigurationPresentsAndAddIfMissing(`AdminWebServerReadTimeoutSeconds`, `10`)
|
||||
CheckSingleConfigurationPresentsAndAddIfMissing(`AdminWebServerWriteTimeoutSeconds`, `10`)
|
||||
CheckSingleConfigurationPresentsAndAddIfMissing(`AdminWebServerMaxHeaderLenBytes`, `1048576`)
|
||||
CheckSingleConfigurationPresentsAndAddIfMissing(`AdminWebServerMaxHeaderLenBytes`, `10485760`)
|
||||
CheckSingleConfigurationPresentsAndAddIfMissing(`PublicWebServerPort`, `50000`)
|
||||
CheckSingleConfigurationPresentsAndAddIfMissing(`PublicWebServerReadTimeoutSeconds`, `10`)
|
||||
CheckSingleConfigurationPresentsAndAddIfMissing(`PublicWebServerWriteTimeoutSeconds`, `10`)
|
||||
|
16
ConfigurationDB/ReadAll.go
Normal file
16
ConfigurationDB/ReadAll.go
Normal file
@ -0,0 +1,16 @@
|
||||
package ConfigurationDB
|
||||
|
||||
import (
|
||||
"github.com/SommerEngineering/Ocean/Log"
|
||||
LM "github.com/SommerEngineering/Ocean/Log/Meta"
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
)
|
||||
|
||||
// This function reads all configuration values e.g. for the admin's configuration web interface.
|
||||
func ReadAll() (values []ConfigurationDBEntry) {
|
||||
if errFind := collection.Find(bson.D{}).All(&values); errFind != nil {
|
||||
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityUnknown, LM.ImpactUnknown, LM.MessageNameDATABASE, `Was not able to read all configuration values out of the database.`, `Error while find.`, errFind.Error())
|
||||
}
|
||||
|
||||
return
|
||||
}
|
47
ConfigurationDB/UpdateValue.go
Normal file
47
ConfigurationDB/UpdateValue.go
Normal file
@ -0,0 +1,47 @@
|
||||
package ConfigurationDB
|
||||
|
||||
import (
|
||||
"github.com/SommerEngineering/Ocean/Log"
|
||||
LM "github.com/SommerEngineering/Ocean/Log/Meta"
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
)
|
||||
|
||||
// This function updates a configuration value e.g. from the admin's configuration web interface.
|
||||
func UpdateValue(name string, configuration ConfigurationDBEntry) {
|
||||
|
||||
// Check the configuration's name:
|
||||
if name == `` {
|
||||
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityUnknown, LM.ImpactUnknown, LM.MessageNameSTATE, `Was not able to update a configuration value.`, `The given name was nil!`)
|
||||
return
|
||||
}
|
||||
|
||||
// Ensure, that the configuration is already present:
|
||||
if count, errFind := collection.Find(bson.D{{"Name", name}}).Count(); errFind != nil {
|
||||
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityUnknown, LM.ImpactUnknown, LM.MessageNameDATABASE, `Was not able to update a configuration value.`, `Error while find the old value.`, errFind.Error())
|
||||
return
|
||||
} else {
|
||||
// Is the configuration already present?
|
||||
if count == 0 {
|
||||
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityUnknown, LM.ImpactUnknown, LM.MessageNameSTATE, `Was not able to update a configuration value.`, `The desired configuration is not present.`)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure, that the configuration value also uses the same name:
|
||||
if name != configuration.Name {
|
||||
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityUnknown, LM.ImpactUnknown, LM.MessageNameSTATE, `Was not able to update a configuration value.`, `The given name was different with the name of the desired configuration value.`)
|
||||
return
|
||||
}
|
||||
|
||||
//
|
||||
// Case: Any precondition is fulfilled
|
||||
//
|
||||
|
||||
// Selection of the correct configuration (the name is a unique value):
|
||||
selector := bson.D{{"Name", name}}
|
||||
if errUpdate := collection.Update(selector, configuration); errUpdate != nil {
|
||||
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityUnknown, LM.ImpactUnknown, LM.MessageNameDATABASE, `Was not able to update a configuration value.`, `Error while updating the database.`, errUpdate.Error())
|
||||
}
|
||||
|
||||
return
|
||||
}
|
@ -37,7 +37,7 @@ func InitHandlers() {
|
||||
Handlers.AddPublicHandler(`/ICCC`, ICCC.ICCCHandler)
|
||||
|
||||
//
|
||||
// Private Handlers:
|
||||
// Private/Admin Handlers:
|
||||
//
|
||||
|
||||
// Handler for the web frameworks like e.g. jQuery, Bootstrap, etc.
|
||||
@ -58,7 +58,13 @@ func InitHandlers() {
|
||||
// Handler for the web logging:
|
||||
Handlers.AddAdminHandler(`/log`, Web.HandlerWebLog)
|
||||
|
||||
// Handler for the web logging's CSS and JS:
|
||||
// Handler for the file upload:
|
||||
Handlers.AddAdminHandler(`/upload`, Admin.HandlerFileUpload)
|
||||
|
||||
// Handler for the configuration view:
|
||||
Handlers.AddAdminHandler(`/configuration`, Admin.HandlerConfiguration)
|
||||
|
||||
// Handler for the admin area:
|
||||
Handlers.AddAdminHandler(`/admin/css/normalize.css`, Admin.HandlerCSSNormalize)
|
||||
Handlers.AddAdminHandler(`/admin/css/webflow.css`, Admin.HandlerCSSWebflow)
|
||||
Handlers.AddAdminHandler(`/admin/css/admin.css`, Admin.HandlerCSSAdmin)
|
||||
|
Loading…
Reference in New Issue
Block a user