5 Commits
Author SHA1 Message Date
Thorsten Sommer 598f9b0ec0 Updated README 2015-06-27 20:03:09 +02:00
Thorsten Sommer 749625f399 Updated README 2015-06-27 19:59:03 +02:00
Thorsten Sommer e3d4f53eb4 Improved the README 2015-06-27 19:57:32 +02:00
Thorsten Sommer 6e1800aa5f Improved the admin's interface 2015-06-27 19:57:14 +02:00
Thorsten Sommer 4138c85639 Updated the README 2015-06-24 08:08:24 +02:00
12 changed files with 367 additions and 21 deletions

No files matched your search

+64
View 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
View 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)
}
}
+8
View File
@@ -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
View File
@@ -0,0 +1,9 @@
package Admin
import (
"github.com/SommerEngineering/Ocean/ConfigurationDB"
)
type AdminWebConfiguration struct {
Configuration []ConfigurationDB.ConfigurationDBEntry
}
+47
View 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:&nbsp;{{.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
View 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&nbsp;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}}
`
+4 -2
View File
@@ -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>
+1 -1
View File
@@ -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
View 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
View 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
}
+41 -16
View File
@@ -2,14 +2,15 @@ Ocean
=====
## Overview
Ocean is a smart and powerful application framework and server which uses the KISS principle ("Keep it simple, stupid"). It enables you to develop (distributed) (web or Internet of Things) applications. Therefore, Ocean contains several components to support you:
Ocean is a smart and powerful application framework and server which uses the KISS principle ("Keep it simple, stupid"). It enables you to develop (distributed) e.g. web, Internet of Things, distributed data / text mining applications. Therefore, Ocean contains several components to support you:
* A distributed logging component with different loggers (log to console, to a database cluster, admin's web view)
* A public facing webserver
* A private facing webserver (e.g. for administration issues)
* A distributed messaging component called ICCC to communicate with all of your servers (or even with other parts at different programming languages at different servers)
* A distributed template engine for web applications
* A distributed and half-automated configuration management
* A distributed and half-automated configuration management with HTML5 admin's interface
* A distributed unique number generator which produces e.g. customer IDs
* A distributed logging service with HTML5 admin's logging viewer
* A simple I18N support
* A simple database abstraction which MongoDB as database back-end
@@ -26,15 +27,27 @@ You have to setup a MongoDB (http://www.mongodb.org) database or database cluste
Ocean uses a very small memory footprint which is usually between 5 and 20 MB, thus, the memory requirements are very low. Further, Ocean also tries to avoid any disk I/O after start-up and utilise for e.g. HTML templates and small static files an in-memory cache.
One assumption to the environment is the usage of static IP addresses. If you start Ocean with a different IP address as last time, you will receive the "Was not able to register this host" error. If you want to use e.g. a DHCP environment with changing addresses, you have to delete the corresponding out-dated entries from the `ICCCHosts` and `ICCCListener` collections.
One assumption to the environment is the usage of static IP addresses. If you start Ocean with a different IP address as last time, you will receive the "*Was not able to register this host*" error. If you want to use e.g. a DHCP environment with changing addresses, you have to delete the corresponding out-dated entries from the `ICCCHosts` and `ICCCListener` collections.
## Setup
### MongoDB steps
After you have installed MongoDB and it runs as service, you have to add a database and provide a user for Ocean. Here is how to do this without any GUI tool. Assumption: The database is installed and no user was added and the authentication is not activated til now.
```
mongo # opens the command-line interface of the MongoDB database
use admin # Change to admin database
db.createUser({user: "root", pwd: "PASSWORD", roles: [ "root" ]}) # Adds an root user for all databases
use Ocean # Change to the non-existing Ocean database
db.createUser({ user: "Ocean", pwd: "PASSWORD", roles: [{ role: "dbOwner", db: "Ocean" }]}) # Create an admin user for the Ocean database
exit
```
You must activate the authentication for the MongoDB installtion now in order to allow Ocean to start.
### Common steps
To enable the Ocean's startup, two small configuration files at the working directory are required. The first file is **project.name**: This file contains one line with the project name. If the project name is longer than 10 characters, the name will be cutted after 10 characters.
The second configuration file is **configuration.json**. It contains the configuration database's information, such as user's name, password, etc. Here is an example:
```json
```JSON
{
"ConfigDBHostname" : "127.0.0.1:27017",
"ConfigDBDatabase" : "MyOcean",
@@ -43,27 +56,39 @@ The second configuration file is **configuration.json**. It contains the configu
"ConfigDBConfigurationCollectionPassword" : "PWD"
}
```
The ConfigDBConfigurationCollection collection gets automatically created. After both files are present and the MongoDB (cluster) is running, Ocean is able to start. The database can and should be empty. Ocean does the configuration automatically. Due to this minimal local configuration and the distributed configuration database, the deployment of additional Ocean servers can be achieved by using e.g. data center scripts, etc.
The `ConfigDBConfigurationCollection` collection gets automatically created. After both files are present and the MongoDB database or database cluster is running, Ocean is able to start. The database can and should be empty. Ocean does the configuration automatically. Due to this minimal local configuration and the distributed configuration database, the deployment of additional Ocean servers can be achieved by using e.g. data center scripts, etc.
In case you setting up an additional Ocean server, you are done. After a few minutes, the ICCC components are connected to each others. The further setup steps in case of a new environment:
* Start Ocean the first time. Ocean will connect to the configuration database. After the setup of all necessary database indexes and after the creation of the configuration collection (table), Ocean stops working. This is fine: Ocean cannot connect to the customer and logging database.
* Therefore, the second step is to use your MongoDB (GUI) tool(s) and edit the configuration collection's values. The most important values are:
* Start Ocean the first time. Ocean will connect to the configuration database. After the setup of all necessary database indexes and after the creation of the configuration collection (i.e. table), Ocean stops working. This is fine: Ocean cannot connect to the customer and logging database.
* Therefore, the second step is to edit the configuration collection's values.
* To update these values, you can use the built-in MongoDB command-line shell, instead of using a GUI tool. Please see http://docs.mongodb.org/manual/reference/method/db.collection.update/ about how to update single values. To identify the documents which you want to update it is fine to use the names.
* Example, about how to update the `CustomerDBHost` field:
```
mongo # opens the command-line interface of the MongoDB database
use Ocean # Change to the Ocean database for the authentication
db.auth("root", "PASSWORD") # Your user name and password
db.Configuration.update({Name: "CustomerDBHost"},{$set: {Value: "127.0.0.1:27017"}})
exit
```
* The most important values and the corresponding MongoDB update-codes are:
* `CustomerDBHost`: Please provide the hostname and port for the customer database. You can use the same as for the configuration database. `db.Configuration.update({Name: "CustomerDBHost"},{$set: {Value: "127.0.0.1:27017"}})`
* `CustomerDBDatabase`: Please provide the database name for the customer database. You can use the same as for the configuration database. `db.Configuration.update({Name: "CustomerDBDatabase"},{$set: {Value: "Ocean"}})`
* `CustomerDBUsername`: Please provide the customer database's username. `db.Configuration.update({Name: "CustomerDBUsername"},{$set: {Value: "Ocean"}})`
* `CustomerDBPassword`: Please provide the customer database's password. `db.Configuration.update({Name: "CustomerDBPassword"},{$set: {Value: "PASSWORD"}})`
* `LogDBHost`: Please provide the hostname and port for the logging database. You can use the same as for the configuration database. `db.Configuration.update({Name: "LogDBHost"},{$set: {Value: "127.0.0.1:27017"}})`
* `LogDBDatabase`: Please provide the database name for the logging database. You can use the same as for the configuration database. `db.Configuration.update({Name: "LogDBDatabase"},{$set: {Value: "Ocean"}})`
* `LogDBUsername`: Please provide the logging database's username. `db.Configuration.update({Name: "LogDBUsername"},{$set: {Value: "Ocean"}})`
* `LogDBPassword`: Please provide the logging database's password. `db.Configuration.update({Name: "LogDBPassword"},{$set: {Value: "PASSWORD"}})`
* Start Ocean the second time. Ocean should start-up now and provides you the admin's web interface at http://127.0.0.1:60000/. With this interface, you can now conveniently adjust additional configuration values. May you consider these values:
* `AdminWebServerBinding`: Where should the admin/private web server connected? You should use this only at local and trusted networks.
* `AdminWebServerEnabled`: Is the admin/private web server enabled?
* `PublicWebServerPort`: Which port should the public web server use? If you use another web server at the front (e.g. a hardware load balancer, nginx, etc.) you can use the default. If you want to use Ocean directly to the public, use port 80.
* `InternalCommPassword`: Replase this value with e.g. a UUID or a good password. It will be used e.g. for the ICCC communication. Therefore, it will be used for the communication between the Ocean's servers.
* `CustomerDBHost`: Please provide the hostname and port for the customer database. You can use the same as for the configuration database.
* `CustomerDBDatabase`: Please provide the database name for the customer database. You can use the same as for the configuration database.
* `CustomerDBUsername`: Please provide the customer database's username.
* `CustomerDBPassword`: Please provide the customer database's password.
* `LogDBHost`: Please provide the hostname and port for the logging database. You can use the same as for the configuration database.
* `LogDBDatabase`: Please provide the database name for the logging database. You can use the same as for the configuration database.
* `LogDBUsername`: Please provide the logging database's username.
* `LogDBPassword`: Please provide the logging database's password.
* `OceanUtilizeCPUs`: How many CPUs should Ocean utilise? At the moment, Ocean uses one value for all servers. Later on, this will be replaced by a per-machine configuration table/collection to enable the usage of heterogeneous servers.
* `LogUseConsoleLogging`: Do you wan to use the console logging? Use it for your first steps and disable it for the production usage. Use the distributed database logging instead!
* `LogUseDatabaseLogging`: Is the database logging enabled? Yes, you should enable it.
* As third step, start Ocean again. The system should now run fine. Please have a look at the ICCC startup messages.
* In case of using Ocean to provide some websites: Use the admin's web interface (http://127.0.0.1:60000/) to upload the necessary files, e.g. `templates.zip` (your HTML templates, see http://golang.org/pkg/html/template/), `staticFiles.zip` (e.g. to provide static images, CSS, JavaScript, XML, JSON data) and a `web.zip` (to separately manage web frameworks like e.g. jQuery, Bootstrap, etc.) See also https://github.com/SommerEngineering/Example003 for an example.
* Finnaly, start Ocean again. The system should now run fine. Please have a look at the ICCC startup logging messages.
### Use Ocean as distributed messaging broker
For this case, you can now start your other ICCC components. This are e.g. some Java, Python or C# programs which are using the Ocean ICCC drivers. These drivers currently under development and they will be available soon.
+8 -2
View File
@@ -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)