diff --git a/LICENSE b/LICENSE
index 4e81bbe..d424397 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
The MIT License (MIT)
-Copyright (c) 2014 FeedLabs
+Copyright (c) 2015 FeedLabs
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..f03b010
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,20 @@
+TEST?=./...
+
+default: test
+
+bin:
+ @sh -c "$(CURDIR)/scripts/build.sh"
+
+dev:
+ @TF_DEV=1 sh -c "$(CURDIR)/scripts/build.sh"
+
+test:
+ go test $(TEST) $(TESTARGS) -timeout=10s
+
+testrace:
+ go test -race $(TEST) $(TESTARGS)
+
+updatedeps:
+ go get -d -v -p 2 ./...
+
+.PHONY: bin default test updatedeps
diff --git a/README.md b/README.md
index 466b630..b56aa99 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@ elasticfeed
#### Internal workflow

-#### Development
+#### Development environment
Create symbolic link from you directory to `GOPATH`
```
cd to-your-project
@@ -14,6 +14,50 @@ mkdir -p $GOPATH/src/github.com/feedlabs
ln -s $(pwd) $GOPATH/src/github.com/feedlabs/elasticfeed
```
+#### Developing Elasticfeed
+If you wish to work on Elasticfeed itself or any of its built-in providers,
+you'll first need [Go](http://www.golang.org) installed (version 1.2+ is
+_required_). Make sure Go is properly installed, including setting up
+a [GOPATH](http://golang.org/doc/code.html#GOPATH).
+
+Next, install the following software packages, which are needed for some dependencies:
+
+- [Bazaar](http://bazaar.canonical.com/en/)
+- [Git](http://git-scm.com/)
+- [Mercurial](http://mercurial.selenic.com/)
+
+Then, install [Gox](https://github.com/mitchellh/gox), which is used
+as a compilation tool on top of Go:
+
+ $ go get -u github.com/mitchellh/gox
+
+Next, clone this repository into `$GOPATH/src/github.com/elasticfeed/elasticfeed`.
+Install the necessary dependencies by running `make updatedeps` and then just
+type `make`. This will compile some more dependencies and then run the tests. If
+this exits with exit status 0, then everything is working!
+
+ $ make updatedeps
+ ...
+ $ make
+ ...
+
+To compile a development version of Elasticfeed and the built-in plugins,
+run `make dev`. This will put Elasticfeed binaries in the `bin` folder:
+
+ $ make dev
+ ...
+ $ bin/elasticfeed
+ ...
+
+
+If you're developing a specific package, you can run tests for just that
+package by specifying the `TEST` variable. For example below, only
+`elasticfeed` package tests will be run.
+
+ $ make test TEST=./elasticfeed
+ ...
+
+
#### Run
`go run api.go`
@@ -31,7 +75,7 @@ $ npm install apidoc -g
##### Generate
```
-$ apidoc -i public/ -o docs/api
+$ apidoc -i service/db -o docs/api
```
##### View
diff --git a/api.go b/api.go
index a283a28..afed378 100644
--- a/api.go
+++ b/api.go
@@ -1,10 +1,21 @@
package main
import (
- _ "github.com/feedlabs/elasticfeed/service"
- "github.com/feedlabs/feedify"
+ "github.com/feedlabs/elasticfeed/service"
+ "github.com/feedlabs/elasticfeed/plugin"
+ "github.com/feedlabs/elasticfeed/workflow"
+ "github.com/feedlabs/elasticfeed/event"
+ "github.com/feedlabs/elasticfeed/resource"
+ "github.com/feedlabs/elasticfeed/elasticfeed"
)
func main() {
- feedify.Run()
+ rm := resource.NewResourceManager()
+ em := event.NewEventManager()
+ pm := plugin.NewPluginManager(rm)
+ wm := workflow.NewWorkflowManager(nil, pm, em)
+ sm := service.NewServiceManager()
+
+ ServerEngine := elasticfeed.NewElasticfeed(rm, em, sm, pm, wm)
+ ServerEngine.Run()
}
diff --git a/apidoc.json b/apidoc.json
index 31913ae..50e6b96 100644
--- a/apidoc.json
+++ b/apidoc.json
@@ -6,11 +6,11 @@
"url": "https://api.feedlabs.com/v1/",
"header": {
"title": "Principles",
- "filename": "docs/apidoc-header.md"
+ "filename": "docs/apidoc/apidoc-header.md"
},
"footer": {
"title": "Appendix",
- "filename": "docs/apidoc-footer.md"
+ "filename": "docs/apidoc/apidoc-footer.md"
},
"order": [
"Application",
diff --git a/helper/auth.go b/common/auth.go
similarity index 96%
rename from helper/auth.go
rename to common/auth.go
index 3e73119..aa016e9 100644
--- a/helper/auth.go
+++ b/common/auth.go
@@ -1,10 +1,10 @@
-package helper
+package common
import (
auth "github.com/abbot/go-http-auth"
"github.com/astaxie/beego/context"
"github.com/feedlabs/elasticfeed/resource"
- "github.com/feedlabs/elasticfeed/helper/config"
+ "github.com/feedlabs/elasticfeed/common/config"
)
var (
diff --git a/helper/config/general.go b/common/config/general.go
similarity index 100%
rename from helper/config/general.go
rename to common/config/general.go
diff --git a/helper/crypt.go b/common/crypt.go
similarity index 95%
rename from helper/crypt.go
rename to common/crypt.go
index a9d84a0..dc14e5d 100644
--- a/helper/crypt.go
+++ b/common/crypt.go
@@ -1,4 +1,4 @@
-package helper
+package common
import (
"crypto/md5"
diff --git a/helper/ids.go b/common/ids.go
similarity index 90%
rename from helper/ids.go
rename to common/ids.go
index 40f0510..9da85b9 100644
--- a/helper/ids.go
+++ b/common/ids.go
@@ -1,4 +1,4 @@
-package helper
+package common
import (
"github.com/feedlabs/elasticfeed/resource"
diff --git a/helper/net.go b/common/net.go
similarity index 93%
rename from helper/net.go
rename to common/net.go
index a8171d2..dc78838 100644
--- a/helper/net.go
+++ b/common/net.go
@@ -1,4 +1,4 @@
-package helper
+package common
import (
"net"
diff --git a/common/uuid/uuid.go b/common/uuid/uuid.go
new file mode 100644
index 0000000..d8b9830
--- /dev/null
+++ b/common/uuid/uuid.go
@@ -0,0 +1,24 @@
+package uuid
+
+import (
+ "crypto/rand"
+ "fmt"
+ "time"
+)
+
+// Generates a time ordered UUID. Top 32 bits are a timestamp,
+// bottom 96 are random.
+func TimeOrderedUUID() string {
+ unix := uint32(time.Now().UTC().Unix())
+
+ b := make([]byte, 12)
+ n, err := rand.Read(b)
+ if n != len(b) {
+ err = fmt.Errorf("Not enough entropy available")
+ }
+ if err != nil {
+ panic(err)
+ }
+ return fmt.Sprintf("%08x-%04x-%04x-%04x-%04x%08x",
+ unix, b[0:2], b[2:4], b[4:6], b[6:8], b[8:])
+}
diff --git a/common/uuid/uuid_test.go b/common/uuid/uuid_test.go
new file mode 100644
index 0000000..8a853f1
--- /dev/null
+++ b/common/uuid/uuid_test.go
@@ -0,0 +1,12 @@
+package uuid
+
+import (
+ "testing"
+)
+
+func TestTimeOrderedUuid(t *testing.T) {
+ uuid := TimeOrderedUUID()
+ if len(uuid) != 36 {
+ t.Fatalf("bad: %s", uuid)
+ }
+}
diff --git a/config.go b/config.go
new file mode 100644
index 0000000..76f0ae8
--- /dev/null
+++ b/config.go
@@ -0,0 +1,224 @@
+package main
+
+import (
+ "encoding/json"
+ "io"
+ "log"
+ "os/exec"
+ "path/filepath"
+ "strings"
+
+ "github.com/mitchellh/osext"
+ "github.com/mitchellh/elasticfeed/elasticfeed"
+ "github.com/mitchellh/elasticfeed/elasticfeed/plugin"
+)
+
+// EnvConfig is the global EnvironmentConfig we use to initialize the CLI.
+var EnvConfig elasticfeed.EnvironmentConfig
+
+type config struct {
+ PluginMinPort uint
+ PluginMaxPort uint
+
+ Pipelines map[string]string
+ Indexers map[string]string
+ Crawlers map[string]string
+ Scenarios map[string]string
+}
+
+// ConfigFile returns the default path to the configuration file. On
+// Unix-like systems this is the ".elasticfeedconfig" file in the home directory.
+// On Windows, this is the "elasticfeed.config" file in the application data
+// directory.
+func ConfigFile() (string, error) {
+ return configFile()
+}
+
+// ConfigDir returns the configuration directory for Elasticfeed.
+func ConfigDir() (string, error) {
+ return configDir()
+}
+
+// Decodes configuration in JSON format from the given io.Reader into
+// the config object pointed to.
+func decodeConfig(r io.Reader, c *config) error {
+ decoder := json.NewDecoder(r)
+ return decoder.Decode(c)
+}
+
+// Discover discovers plugins.
+//
+// This looks in the directory of the executable and the CWD, in that
+// order for priority.
+func (c *config) Discover() error {
+ // Next, look in the same directory as the executable. Any conflicts
+ // will overwrite those found in our current directory.
+ exePath, err := osext.Executable()
+ if err != nil {
+ log.Printf("[ERR] Error loading exe directory: %s", err)
+ } else {
+ if err := c.discover(filepath.Dir(exePath)); err != nil {
+ return err
+ }
+ }
+
+ // Look in the plugins directory
+ dir, err := ConfigDir()
+ if err != nil {
+ log.Printf("[ERR] Error loading config directory: %s", err)
+ } else {
+ if err := c.discover(filepath.Join(dir, "plugins")); err != nil {
+ return err
+ }
+ }
+
+ // Look in the cwd.
+ if err := c.discover("."); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (c *config) LoadPiplines(name string) (elasticfeed.Pipeline, error) {
+ log.Printf("Loading provisioner: %s\n", name)
+ bin, ok := c.Pipelines[name]
+ if !ok {
+ log.Printf("Pipelines not found: %s\n", name)
+ return nil, nil
+ }
+
+ return c.pluginClient(bin).Pipeline()
+}
+
+func (c *config) LoadIndexers(name string) (elasticfeed.Indexer, error) {
+ log.Printf("Loading provisioner: %s\n", name)
+ bin, ok := c.Indexers[name]
+ if !ok {
+ log.Printf("Indexers not found: %s\n", name)
+ return nil, nil
+ }
+
+ return c.pluginClient(bin).Indexer()
+}
+
+func (c *config) LoadCrawlers(name string) (elasticfeed.Crawler, error) {
+ log.Printf("Loading provisioner: %s\n", name)
+ bin, ok := c.Crawlers[name]
+ if !ok {
+ log.Printf("Crawlers not found: %s\n", name)
+ return nil, nil
+ }
+
+ return c.pluginClient(bin).Crawler()
+}
+
+func (c *config) LoadScenarios(name string) (elasticfeed.Scenario, error) {
+ log.Printf("Loading provisioner: %s\n", name)
+ bin, ok := c.Scenarios[name]
+ if !ok {
+ log.Printf("Scenarios not found: %s\n", name)
+ return nil, nil
+ }
+
+ return c.pluginClient(bin).Scenario()
+}
+
+func (c *config) discover(path string) error {
+ var err error
+
+ if !filepath.IsAbs(path) {
+ path, err = filepath.Abs(path)
+ if err != nil {
+ return err
+ }
+ }
+
+ err = c.discoverSingle(
+ filepath.Join(path, "elasticfeed-pipeline-*"), &c.Pipelines)
+ if err != nil {
+ return err
+ }
+
+ err = c.discoverSingle(
+ filepath.Join(path, "elasticfeed-indexer-*"), &c.Indexers)
+ if err != nil {
+ return err
+ }
+
+ err = c.discoverSingle(
+ filepath.Join(path, "elasticfeed-crawler-*"), &c.Crawlers)
+ if err != nil {
+ return err
+ }
+
+ err = c.discoverSingle(
+ filepath.Join(path, "elasticfeed-scenario-*"), &c.Scenarios)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (c *config) discoverSingle(glob string, m *map[string]string) error {
+ matches, err := filepath.Glob(glob)
+ if err != nil {
+ return err
+ }
+
+ if *m == nil {
+ *m = make(map[string]string)
+ }
+
+ prefix := filepath.Base(glob)
+ prefix = prefix[:strings.Index(prefix, "*")]
+ for _, match := range matches {
+ file := filepath.Base(match)
+
+ // If the filename has a ".", trim up to there
+ if idx := strings.Index(file, "."); idx >= 0 {
+ file = file[:idx]
+ }
+
+ // Look for foo-bar-baz. The plugin name is "baz"
+ plugin := file[len(prefix):]
+ log.Printf("[DEBUG] Discoverd plugin: %s = %s", plugin, match)
+ (*m)[plugin] = match
+ }
+
+ return nil
+}
+
+func (c *config) pluginClient(path string) *plugin.Client {
+ originalPath := path
+
+ // First attempt to find the executable by consulting the PATH.
+ path, err := exec.LookPath(path)
+ if err != nil {
+ // If that doesn't work, look for it in the same directory
+ // as the `packer` executable (us).
+ log.Printf("Plugin could not be found. Checking same directory as executable.")
+ exePath, err := osext.Executable()
+ if err != nil {
+ log.Printf("Couldn't get current exe path: %s", err)
+ } else {
+ log.Printf("Current exe path: %s", exePath)
+ path = filepath.Join(filepath.Dir(exePath), filepath.Base(originalPath))
+ }
+ }
+
+ // If everything failed, just use the original path and let the error
+ // bubble through.
+ if path == "" {
+ path = originalPath
+ }
+
+ log.Printf("Creating plugin client for path: %s", path)
+ var config plugin.ClientConfig
+ config.Cmd = exec.Command(path)
+ config.Managed = true
+ config.MinPort = c.PluginMinPort
+ config.MaxPort = c.PluginMaxPort
+ return plugin.NewClient(&config)
+}
diff --git a/config_unix.go b/config_unix.go
new file mode 100644
index 0000000..2c8a7a3
--- /dev/null
+++ b/config_unix.go
@@ -0,0 +1,54 @@
+// +build darwin freebsd linux netbsd openbsd
+
+package main
+
+import (
+ "bytes"
+ "errors"
+ "log"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "strings"
+)
+
+func configFile() (string, error) {
+ dir, err := homeDir()
+ if err != nil {
+ return "", err
+ }
+
+ return filepath.Join(dir, ".packerconfig"), nil
+}
+
+func configDir() (string, error) {
+ dir, err := homeDir()
+ if err != nil {
+ return "", err
+ }
+
+ return filepath.Join(dir, ".packer.d"), nil
+}
+
+func homeDir() (string, error) {
+ // First prefer the HOME environmental variable
+ if home := os.Getenv("HOME"); home != "" {
+ log.Printf("Detected home directory from env var: %s", home)
+ return home, nil
+ }
+
+ // If that fails, try the shell
+ var stdout bytes.Buffer
+ cmd := exec.Command("sh", "-c", "eval echo ~$USER")
+ cmd.Stdout = &stdout
+ if err := cmd.Run(); err != nil {
+ return "", err
+ }
+
+ result := strings.TrimSpace(stdout.String())
+ if result == "" {
+ return "", errors.New("blank output")
+ }
+
+ return result, nil
+}
diff --git a/config_windows.go b/config_windows.go
new file mode 100644
index 0000000..fa3ab94
--- /dev/null
+++ b/config_windows.go
@@ -0,0 +1,46 @@
+// +build windows
+
+package main
+
+import (
+ "path/filepath"
+ "syscall"
+ "unsafe"
+)
+
+var (
+ shell = syscall.MustLoadDLL("Shell32.dll")
+ getFolderPath = shell.MustFindProc("SHGetFolderPathW")
+)
+
+const CSIDL_APPDATA = 26
+
+func configFile() (string, error) {
+ dir, err := homeDir()
+ if err != nil {
+ return "", err
+ }
+
+ return filepath.Join(dir, "packer.config"), nil
+}
+
+func configDir() (string, error) {
+ dir, err := homeDir()
+ if err != nil {
+ return "", err
+ }
+
+ return filepath.Join(dir, "packer.d"), nil
+}
+
+func homeDir() (string, error) {
+ b := make([]uint16, syscall.MAX_PATH)
+
+ // See: http://msdn.microsoft.com/en-us/library/windows/desktop/bb762181(v=vs.85).aspx
+ r, _, err := getFolderPath.Call(0, CSIDL_APPDATA, 0, 0, uintptr(unsafe.Pointer(&b[0])))
+ if uint32(r) != 0 {
+ return "", err
+ }
+
+ return syscall.UTF16ToString(b), nil
+}
diff --git a/docs/examples/javascript.md b/docs/examples/javascript.md
deleted file mode 100644
index 9055496..0000000
--- a/docs/examples/javascript.md
+++ /dev/null
@@ -1,39 +0,0 @@
-### JS client
-
-#### Usage
-```
- window.onload = function() {
- elasticfeed.init({
- channel: {
- url: 'ws://localhost:80',
- transport: 'ws'
- }
- });
-
- feed = elasticfeed.initFeed('000001', {
- outputContainerId: 'my-elastic-feed-1',
- stylerFunction: function(data) {
- return '
' + data + '
';
- }
- });
-
- feed.channel.on('join', function(chid, ts) {
- feed1.addEntry(chid + " joined the chat room");
- });
-
- feed.channel.on('leave', function(chid, ts) {
- feed1.addEntry(chid + " left the chat room");
- });
-
- window['socket'] = feed1.socket;
- }
-```
-
-#### Test data broadcast
-```
- // single feed
- socket.send({Type:1, Timestamp:1111111, Content: {Type:3, Timestamp:22222, Id: "000001", Content: "data-examples"}})
-
- // all feeds in the view on the chanel
- socket.send({Type:1, Timestamp:1111111, Content: {Type:3, Timestamp:22222, Id: "*", Content: "data-examples"}})
-```
diff --git a/elasticfeed/elasticfeed.go b/elasticfeed/elasticfeed.go
new file mode 100644
index 0000000..b7c51a4
--- /dev/null
+++ b/elasticfeed/elasticfeed.go
@@ -0,0 +1,51 @@
+package elasticfeed
+
+import (
+ "github.com/feedlabs/elasticfeed/plugin"
+ "github.com/feedlabs/elasticfeed/workflow"
+ "github.com/feedlabs/elasticfeed/service"
+ "github.com/feedlabs/elasticfeed/event"
+ "github.com/feedlabs/elasticfeed/resource"
+
+ "github.com/feedlabs/feedify"
+)
+
+type Elasticfeed struct {
+ rm *resource.ResourceManager
+ em *event.EventManager
+ sm *service.ServiceManager
+ pm *plugin.PluginManager
+ wm *workflow.WorkflowManager
+}
+
+func (this *Elasticfeed) GetEventManager() *event.EventManager {
+ return this.em
+}
+
+func (this *Elasticfeed) GetResourceManager() *resource.ResourceManager {
+ return this.rm
+}
+
+func (this *Elasticfeed) GetServiceManager() *service.ServiceManager {
+ return this.sm
+}
+
+func (this *Elasticfeed) GetPluginManager() *plugin.PluginManager {
+ return this.pm
+}
+
+func (this *Elasticfeed) GetWorkflowManager() *workflow.WorkflowManager {
+ return this.wm
+}
+
+func (this *Elasticfeed) Run() {
+ this.GetResourceManager().Init()
+ this.GetServiceManager().Init()
+
+ feedify.SetStaticPath("/static", "public")
+ feedify.Run()
+}
+
+func NewElasticfeed(rm *resource.ResourceManager, em *event.EventManager, sm *service.ServiceManager, pm *plugin.PluginManager, wm *workflow.WorkflowManager) *Elasticfeed {
+ return &Elasticfeed{rm, em, sm, pm, wm}
+}
diff --git a/event/event.go b/event/event.go
new file mode 100644
index 0000000..6831249
--- /dev/null
+++ b/event/event.go
@@ -0,0 +1,12 @@
+package event
+
+type Event struct {
+ data interface{}
+
+ eventGroup string
+ eventName string
+}
+
+func NewEvent(data interface{}) *Event {
+ return &Event{data, "", ""}
+}
diff --git a/event/manager.go b/event/manager.go
new file mode 100644
index 0000000..5e8fd45
--- /dev/null
+++ b/event/manager.go
@@ -0,0 +1,46 @@
+package event
+
+const (
+ EVENT_STORING = "storing"
+ EVENT_PROCESSING = "processing"
+ EVENT_DISTRIBUTING = "distributing"
+ EVENT_LEARNING = "learning"
+
+ EVENT_STORING_CREATE_ENTRY = "create-entry"
+ EVENT_PROCESSING_FEED_MAINTAINER = "feed-maintainer"
+ EVENT_DISTRIBUTING_PUSH_ENTRY = "push-entry"
+ EVENT_LEARNING_CREATE_METRIC = "create-metric"
+)
+
+type EventManager struct {
+ events map[string]interface {}
+}
+
+func (this *EventManager) On(name string, callback func(event *Event)) {
+ this.events[name] = callback
+}
+
+func (this *EventManager) Off(event string) {
+ delete(this.events, event)
+}
+
+func (this *EventManager) Trigger(name string, data interface{}) {
+ e := NewEvent(data)
+ for _, i := range (this.events) {
+ i.(func(*Event))(e)
+ }
+}
+
+func (this *EventManager) GetEventsMap() map[string]interface{} {
+ return map[string]interface{}{
+ EVENT_STORING: []string{EVENT_STORING_CREATE_ENTRY},
+ EVENT_PROCESSING: []string{EVENT_PROCESSING_FEED_MAINTAINER},
+ EVENT_DISTRIBUTING: []string{EVENT_DISTRIBUTING_PUSH_ENTRY},
+ EVENT_LEARNING: []string{EVENT_LEARNING_CREATE_METRIC},
+ }
+}
+
+func NewEventManager() *EventManager {
+ e := make(map[string]interface{})
+ return &EventManager{e}
+}
diff --git a/install.sh b/install.sh
index f2bc913..5d53538 100755
--- a/install.sh
+++ b/install.sh
@@ -40,3 +40,6 @@ go get github.com/bradfitz/gomemcache/memcache
# websoscket
go get github.com/gorilla/websocket
+
+# sseserver
+go get github.com/mroth/sseserver
diff --git a/plugin/compiler/js.go b/plugin/compiler/js.go
new file mode 100644
index 0000000..f266cec
--- /dev/null
+++ b/plugin/compiler/js.go
@@ -0,0 +1,5 @@
+package compiler
+
+func getJsVM() {}
+
+func init() {}
diff --git a/plugin/crawler.go b/plugin/crawler.go
new file mode 100644
index 0000000..0de1c04
--- /dev/null
+++ b/plugin/crawler.go
@@ -0,0 +1,5 @@
+package plugin
+
+type Crawler interface {
+
+}
diff --git a/plugin/indexer.go b/plugin/indexer.go
new file mode 100644
index 0000000..136adec
--- /dev/null
+++ b/plugin/indexer.go
@@ -0,0 +1,5 @@
+package plugin
+
+type Indexer interface {
+
+}
diff --git a/plugin/manager.go b/plugin/manager.go
new file mode 100644
index 0000000..61eb6f1
--- /dev/null
+++ b/plugin/manager.go
@@ -0,0 +1,41 @@
+package plugin
+
+type PluginManager struct {
+ Indexers map[string]interface{}
+ Crawlers map[string]interface{}
+ Sensors map[string]interface{}
+ Pipelines map[string]interface{}
+ Scenarios map[string]interface{}
+ Helpers map[string]interface{}
+
+ api *ResourceApi
+}
+
+func (this *PluginManager) GetResourceApi() interface{} {
+ return this.api
+}
+
+func (this *PluginManager) InitIndexer(name string, profiler *Profiler) *Plugin {
+ p := NewPlugin(this, this.api, profiler)
+
+ p.Init()
+ this.Indexers[name] = p
+
+ return p
+}
+
+func (this *PluginManager) FindPlugin(name string, profiler *Profiler) *interface{} {
+ return nil
+}
+
+func (this *PluginManager) ExecPlugin(p Plugin) {
+ // profiler := p.profiler
+}
+
+func NewPluginManager(resourceManager interface{}) *PluginManager {
+ pm := &PluginManager{}
+
+ pm.api = NewResourceApi(resourceManager)
+
+ return pm
+}
diff --git a/plugin/math/rpc_client.go b/plugin/math/rpc_client.go
new file mode 100644
index 0000000..418e15b
--- /dev/null
+++ b/plugin/math/rpc_client.go
@@ -0,0 +1,27 @@
+package main
+
+import (
+ "fmt"
+ "net/rpc"
+ "log"
+)
+
+type Args struct {
+ X, Y int
+}
+
+func main(){
+
+ client, err := rpc.Dial("tcp", "127.0.0.1:1234")
+ if err != nil {
+ log.Fatal("dialing:", err)
+ }
+ // Synchronous call
+ args := &Args{1,2}
+ var reply int
+ err = client.Call("bbb.Add", args, &reply)
+ if err != nil {
+ log.Fatal("arith error:", err)
+ }
+ fmt.Printf("Result: %d+%d=%d", args.X, args.Y, reply)
+}
diff --git a/plugin/pipeline.go b/plugin/pipeline.go
new file mode 100644
index 0000000..50fd273
--- /dev/null
+++ b/plugin/pipeline.go
@@ -0,0 +1,5 @@
+package plugin
+
+type Pipeline interface {
+
+}
diff --git a/plugin/pipeline/ai_neural_network.go b/plugin/pipeline/ai_neural_network.go
new file mode 100644
index 0000000..0fc612c
--- /dev/null
+++ b/plugin/pipeline/ai_neural_network.go
@@ -0,0 +1,11 @@
+package pipeline
+
+func AINeuralNetworkAnimator(data interface{}, viewerBrain interface{}) interface{} {
+
+ // PIPELINE data through the Neural Netowrk
+ // of current connected Viewer/Audience
+
+ return data
+}
+
+func init() {}
diff --git a/plugin/pipeline/pipeline.go b/plugin/pipeline/pipeline.go
new file mode 100644
index 0000000..b4ef5ec
--- /dev/null
+++ b/plugin/pipeline/pipeline.go
@@ -0,0 +1,8 @@
+package pipeline
+
+func Filter(data interface{}) interface{} {
+ // should call plugins of type PIPELINE
+ return RandomAnimator(data)
+}
+
+func init() {}
diff --git a/plugin/pipeline/random_animator.go b/plugin/pipeline/random_animator.go
new file mode 100644
index 0000000..0b0a892
--- /dev/null
+++ b/plugin/pipeline/random_animator.go
@@ -0,0 +1,18 @@
+package pipeline
+
+import (
+ "time"
+ "math/rand"
+)
+
+func RandomAnimator(data interface {}) interface {} {
+
+ // PIPE DELAY SIMULATION
+
+ amt := time.Duration(rand.Intn(200))
+ time.Sleep(amt * time.Millisecond)
+
+ return data
+}
+
+func init() {}
diff --git a/plugin/plugin.go b/plugin/plugin.go
new file mode 100644
index 0000000..7edbbbe
--- /dev/null
+++ b/plugin/plugin.go
@@ -0,0 +1,15 @@
+package plugin
+
+type Plugin struct {
+ pluginManager *PluginManager
+ resourceApi *ResourceApi
+
+ profiler *Profiler
+ rpcAddress interface{}
+}
+
+func (this *Plugin) Init() {}
+
+func NewPlugin(pm *PluginManager, api *ResourceApi, profiler *Profiler) *Plugin {
+ return &Plugin{pm, api, profiler, ""}
+}
diff --git a/plugin/profiler.go b/plugin/profiler.go
new file mode 100644
index 0000000..fe2014a
--- /dev/null
+++ b/plugin/profiler.go
@@ -0,0 +1,9 @@
+package plugin
+
+type Profiler struct {
+ data map[string]string
+}
+
+func NewProfiler(data map[string]string) *Profiler {
+ return &Profiler{data}
+}
diff --git a/plugin/provider/sensors.go b/plugin/provider/sensors.go
new file mode 100644
index 0000000..3a235ab
--- /dev/null
+++ b/plugin/provider/sensors.go
@@ -0,0 +1,5 @@
+package provider
+
+func Weather() {}
+func StockIndices() {}
+func SunActivity() {}
diff --git a/plugin/resource_api.go b/plugin/resource_api.go
new file mode 100644
index 0000000..42edc67
--- /dev/null
+++ b/plugin/resource_api.go
@@ -0,0 +1,19 @@
+package plugin
+
+type ResourceApi struct {
+ resourceManager interface{}
+}
+
+func (this *ResourceApi) CreateEntryMetric() {}
+func (this *ResourceApi) DeleteEntryMetric() {}
+func (this *ResourceApi) UpdateEntryMetric() {}
+
+func (this *ResourceApi) ClearFeed() {}
+func (this *ResourceApi) ReorderFeed() {}
+func (this *ResourceApi) CreateFeedEntry() {}
+func (this *ResourceApi) DeleteFeedEntry() {}
+func (this *ResourceApi) UpdateFeedEntry() {}
+
+func NewResourceApi(resourceManager interface{}) *ResourceApi {
+ return &ResourceApi{resourceManager}
+}
diff --git a/plugin/scenario.go b/plugin/scenario.go
new file mode 100644
index 0000000..8a1e75d
--- /dev/null
+++ b/plugin/scenario.go
@@ -0,0 +1,5 @@
+package plugin
+
+type Scenario interface {
+
+}
diff --git a/plugin/scenario/ai_neural_network_trainer.go b/plugin/scenario/ai_neural_network_trainer.go
new file mode 100644
index 0000000..e1b1d3f
--- /dev/null
+++ b/plugin/scenario/ai_neural_network_trainer.go
@@ -0,0 +1,17 @@
+package scenario
+
+import (
+ _ "github.com/feedlabs/elasticfeed/elasticfeed/ai"
+)
+
+func AINeuralNetworkTrainer(data interface{}, viewerBrain interface{}) interface{} {
+
+ //-----------------------------------------------------
+ // Train ViewerBrain using Viewer specific metrics from
+ // front-end behaviours
+ //-----------------------------------------------------
+
+ return viewerBrain
+}
+
+func init() {}
diff --git a/plugin/sensor.go b/plugin/sensor.go
new file mode 100644
index 0000000..d53da75
--- /dev/null
+++ b/plugin/sensor.go
@@ -0,0 +1,5 @@
+package plugin
+
+type Sensor interface {
+
+}
diff --git a/public/lp.html b/public/lp.html
new file mode 100644
index 0000000..7c6fa97
--- /dev/null
+++ b/public/lp.html
@@ -0,0 +1,81 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/public/ws.html b/public/ws.html
new file mode 100644
index 0000000..cec0522
--- /dev/null
+++ b/public/ws.html
@@ -0,0 +1,85 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/resource/admin.go b/resource/admin.go
index c3f2f1b..7c68e66 100644
--- a/resource/admin.go
+++ b/resource/admin.go
@@ -5,7 +5,7 @@ import (
"strconv"
"github.com/feedlabs/feedify/graph"
- "github.com/feedlabs/elasticfeed/helper/config"
+ "github.com/feedlabs/elasticfeed/common/config"
)
func (this *Admin) IsSuperUser() bool {
@@ -136,8 +136,14 @@ func DeleteAdmin(id string) (error) {
}
func FindAdminByUsername(username string) (admin *Admin, err error) {
- org := &Org{"26", "", "", 0, 0, 0}
- whitelist := []string{"127.0.0.1", "192.168.1.51"}
+
+ /* -----------------------------------------------
+ * Hardcoded admin ORG ID
+ * ------------------------------------------------
+ */
+
+ org := &Org{"0", "", "", 0, 0, 0}
+ whitelist := []string{"127.0.0.1", "192.168.1.51", "localhost"}
password := "hello"
if username == config.GetApiSuperuser() {
@@ -146,7 +152,3 @@ func FindAdminByUsername(username string) (admin *Admin, err error) {
return &Admin{"0", org, username, true, whitelist, password, 0}, nil
}
-
-func init() {
- Admins = make(map[string]*Admin)
-}
diff --git a/resource/application.go b/resource/application.go
index 9561e30..80ff12a 100644
--- a/resource/application.go
+++ b/resource/application.go
@@ -107,7 +107,3 @@ func DeleteApplication(id string) (error) {
_id, _ := strconv.Atoi(id)
return storage.DeleteNode(_id)
}
-
-func init() {
- Applications = make(map[string]*Application)
-}
diff --git a/resource/feed.go b/resource/feed.go
index 632b00d..bad14cd 100644
--- a/resource/feed.go
+++ b/resource/feed.go
@@ -7,6 +7,7 @@ import (
"github.com/feedlabs/feedify/graph"
"github.com/feedlabs/elasticfeed/service/stream/controller/room"
+ "github.com/feedlabs/elasticfeed/workflow"
)
func (this *Feed) AddEntry(entry Entry) (EntryId string, err error) {
@@ -17,6 +18,22 @@ func (this *Feed) GetEntryList() (entries []*Entry, err error) {
return GetEntryList(this.Id, this.Application.Id, this.Application.Org.Id)
}
+func (this *Feed) SetWorkflowfile(data map[string]interface{}) {
+ this.Workflowfile = data
+}
+
+func (this *Feed) GetWorkflowfile() map[string]interface{} {
+ return this.Workflowfile
+}
+
+func (this *Feed) InitWorkflow(wm *workflow.WorkflowManager) {
+ this.Workflow = wm.CreateFeedWorkflow(this, this.GetWorkflowfile())
+}
+
+func (this *Feed) GetWorkflow() *workflow.Workflow {
+ return this.Workflow
+}
+
func GetFeedList(ApplicationId string, OrgId string) (feedList []*Feed, err error) {
app, err := GetApplication(ApplicationId, OrgId)
if err != nil {
@@ -33,7 +50,7 @@ func GetFeedList(ApplicationId string, OrgId string) (feedList []*Feed, err erro
id := strconv.Itoa(rel.EndNode.Id)
rels, _ := storage.RelationshipsNode(rel.EndNode.Id, "entry")
- feed := &Feed{id , app, data, len(rels)}
+ feed := NewFeed(id , app, data, len(rels))
feeds = append(feeds, feed)
}
@@ -60,7 +77,7 @@ func GetFeed(id string, applicationId string, orgId string) (feed *Feed, err err
if node != nil && Contains(node.Labels, RESOURCE_FEED_LABEL) && app.Id == node.Data["applicationId"].(string) {
data := node.Data["data"].(string)
rels, _ := storage.RelationshipsNode(node.Id, "entry")
- return &Feed{strconv.Itoa(node.Id), app, data, len(rels)}, nil
+ return NewFeed(strconv.Itoa(node.Id), app, data, len(rels)), nil
}
return nil, errors.New("FeedId not exist for ApplicationId `"+applicationId+"`")
@@ -112,6 +129,6 @@ func ActionEmptyFeed(id string) {
room.Publish <- room.NewFeedEvent(room.FEED_EMPTY, id, "empty")
}
-func init() {
- Feeds = make(map[string]*Feed)
+func NewFeed(id string, app *Application, data string, entries int) *Feed {
+ return &Feed{id, app, data, entries, nil, nil}
}
diff --git a/resource/manager.go b/resource/manager.go
new file mode 100644
index 0000000..236ef2f
--- /dev/null
+++ b/resource/manager.go
@@ -0,0 +1,13 @@
+package resource
+
+type ResourceManager struct {}
+
+func (this * ResourceManager) Init() {
+ InitStorage()
+ InitResources()
+ InitStreamCommunicator()
+}
+
+func NewResourceManager() *ResourceManager {
+ return &ResourceManager{}
+}
diff --git a/resource/metric.go b/resource/metric.go
new file mode 100644
index 0000000..8f6efbf
--- /dev/null
+++ b/resource/metric.go
@@ -0,0 +1,5 @@
+package resource
+
+func (this *Metric) NewMetric() *Metric {
+ return &Metric{}
+}
diff --git a/resource/resource.go b/resource/resource.go
index d2adb3d..f075123 100644
--- a/resource/resource.go
+++ b/resource/resource.go
@@ -2,26 +2,41 @@ package resource
import (
"errors"
+ "encoding/json"
+ "time"
+ "math/rand"
"github.com/feedlabs/feedify/service"
"github.com/feedlabs/feedify/graph"
"github.com/feedlabs/feedify/stream"
+
+ "github.com/feedlabs/elasticfeed/service/stream/controller/room"
+ "github.com/feedlabs/elasticfeed/service/stream/model"
+ "github.com/feedlabs/elasticfeed/plugin/pipeline"
+
+ "github.com/feedlabs/elasticfeed/workflow"
)
-const RESOURCE_ORG_LABEL = "org"
-const RESOURCE_ADMIN_LABEL = "admin"
-const RESOURCE_TOKEN_LABEL = "token"
-const RESOURCE_APPLICATION_LABEL = "application"
-const RESOURCE_FEED_LABEL = "feed"
-const RESOURCE_ENTRY_LABEL = "entry"
+const (
+ RESOURCE_ORG_LABEL = "org"
+ RESOURCE_ADMIN_LABEL = "admin"
+ RESOURCE_TOKEN_LABEL = "token"
+ RESOURCE_APPLICATION_LABEL = "application"
+ RESOURCE_FEED_LABEL = "feed"
+ RESOURCE_ENTRY_LABEL = "entry"
+ RESOURCE_METRIC_LABEL = "metric"
+ RESOURCE_VIEWER_LABEL = "viewer"
+)
var (
Orgs map[string]*Org
- Admins map[string]*Admin
- Tokens map[string]*Token
+ Admins map[string]*Admin
+ Tokens map[string]*Token
Applications map[string]*Application
- Feeds map[string]*Feed
- Entries map[string]*Entry
+ Feeds map[string]*Feed
+ Entries map[string]*Entry
+ Metrics map[string]*Metric
+ Viewers map[string]*Viewer
message *stream.StreamMessage
storage *graph.GraphStorage
@@ -68,6 +83,9 @@ type Feed struct {
Data string
Entries int
+
+ Workflow *workflow.Workflow
+ Workflowfile map[string]interface{}
}
type Entry struct {
@@ -76,18 +94,104 @@ type Entry struct {
Data string
}
-func init() {
- stream_service, _ := service.NewStream()
- if stream_service == nil {
- panic(errors.New("Cannot create stream service"))
+type Viewer struct {}
+
+type Metric struct {}
+
+func ResourceStreamManager() {
+ for {
+ select {
+ case socketEvent := <-room.ResourceEvent:
+
+ go ResourceStreamRequest(socketEvent)
+ }
}
- message = stream_service.Message
+}
+
+func ResourceStreamRequest(socketEvent model.SocketEvent) {
+
+ // *******************************************************************
+ // here should be implemented REAL CONTENT IMPROVEMENT
+ // based on connected user (viewer) or users (audience)!: habits, behaviours, stats etc.
+ // PIPE: filtering, customization
+ // SCENARIO-ENGINE: scenarios
+ // *******************************************************************
+
+ // *******************************************************************
+ // SCENARIO AND RULES/METRICS
+ // should use go routine with time limit to query filter rules
+ // if in specific time there is no rules the results should be sent
+ // client feed. After this the next package should be sent with
+ // rules which entries should be remove/hidden from the view!
+ // *******************************************************************
+
+ timeout := make(chan bool, 1)
+ results := make(chan []*Entry, 1)
+
+ list, _ := GetEntryList(socketEvent.FeedId, socketEvent.AppId, socketEvent.OrgId)
+
+ // PIPE TIMEOUT
+ go func() {
+ amt := time.Duration(rand.Intn(100))
+ time.Sleep(amt * time.Millisecond)
+ timeout <- true
+ }()
+
+ // SHOULD BE A FILTER IMPLEMENTATION
+ go func(list []*Entry, socketEvent model.SocketEvent) {
+ list = pipeline.Filter(list).([]*Entry)
+ results <- list
+ }(list, socketEvent)
+
+ select {
+
+ // IF PIPE TAKES TOO MUCH TIME, DATA DELAYED
+ case <-timeout:
+
+ event := room.NewFeedEvent(room.FEED_ENTRY_NEW, socketEvent.FeedId, "{Content:\"tiemout\"}")
+ data, _ := json.Marshal(event)
+
+ if socketEvent.Ws != nil {
+ amt := time.Duration(rand.Intn(500)) * 1000
+ time.Sleep(amt * time.Microsecond)
+ socketEvent.Ws.WriteMessage(1, data)
+ }
+
+ if socketEvent.Ch != nil {
+ socketEvent.Ch <- data
+ }
+
+ // IF DATA ARRIVES WITHOUT DELAY
+ case list := <-results:
+
+ // *********************************************************************
+ // register socket handler
+ // needs to send notiffication to long pooling + ws
+ // join should generate uniqe ID and client should use it
+ // maybe sessionID could be as uniqeID ?
+ // room.FeedSubscribers[socketEvent.FeedId][channelID] = socketEvent
+ // *********************************************************************
+ //
+ // timeout with channels and routines?
+ // http://blog.golang.org/go-concurrency-patterns-timing-out-and
+
+ // amt := time.Duration(rand.Intn(500)) * 10000
+ // time.Sleep(amt * time.Microsecond)
+
+ d, _ := json.Marshal(list)
+ event := room.NewFeedEvent(room.FEED_ENTRY_INIT, socketEvent.FeedId, string(d))
+ data, _ := json.Marshal(event)
+
+ if socketEvent.Ws != nil {
+ socketEvent.Ws.WriteMessage(1, data)
+ }
+
+ if socketEvent.Ch != nil {
+ socketEvent.Ch <- data
+ }
- graph_service, _ := service.NewGraph()
- if graph_service == nil {
- panic(errors.New("Cannot create graph service"))
}
- storage = graph_service.Storage
+
}
func Contains(s []string, e string) bool {
@@ -103,3 +207,26 @@ func ConvertInterfaceToStringArray(d interface{}) []string {
}
return output
}
+
+func InitResources() {
+ Admins = make(map[string]*Admin)
+ Applications = make(map[string]*Application)
+ Feeds = make(map[string]*Feed)
+ Entries = make(map[string]*Entry)
+ Orgs = make(map[string]*Org)
+ Tokens = make(map[string]*Token)
+ Metrics = make(map[string]*Metric)
+ Viewers = make(map[string]*Viewer)
+}
+
+func InitStorage() {
+ graph_service, _ := service.NewGraph()
+ if graph_service == nil {
+ panic(errors.New("Cannot create graph service"))
+ }
+ storage = graph_service.Storage
+}
+
+func InitStreamCommunicator() {
+ go ResourceStreamManager()
+}
diff --git a/resource/token.go b/resource/token.go
index 4345e84..135d3ed 100644
--- a/resource/token.go
+++ b/resource/token.go
@@ -7,10 +7,6 @@ import (
"github.com/feedlabs/feedify/graph"
)
-func init() {
- Tokens = make(map[string]*Token)
-}
-
func GetTokenList(AdminId string, OrgId string) (tokenLinst []*Token, err error) {
admin, err := GetAdmin(AdminId, OrgId)
if err != nil {
diff --git a/resource/viewer.go b/resource/viewer.go
new file mode 100644
index 0000000..958e354
--- /dev/null
+++ b/resource/viewer.go
@@ -0,0 +1 @@
+package resource
diff --git a/scripts/build.sh b/scripts/build.sh
new file mode 100755
index 0000000..c1d3587
--- /dev/null
+++ b/scripts/build.sh
@@ -0,0 +1,80 @@
+#!/bin/bash
+#
+# This script builds the application from source for multiple platforms.
+set -e
+
+# Get the parent directory of where this script is.
+SOURCE="${BASH_SOURCE[0]}"
+while [ -h "$SOURCE" ] ; do SOURCE="$(readlink "$SOURCE")"; done
+DIR="$( cd -P "$( dirname "$SOURCE" )/.." && pwd )"
+
+# Change into that directory
+cd $DIR
+
+# Get the git commit
+GIT_COMMIT=$(git rev-parse HEAD)
+GIT_DIRTY=$(test -n "`git status --porcelain`" && echo "+CHANGES" || true)
+
+# If its dev mode, only build for ourself
+if [ "${TF_DEV}x" != "x" ]; then
+ XC_OS=${XC_OS:-$(go env GOOS)}
+ XC_ARCH=${XC_ARCH:-$(go env GOARCH)}
+fi
+
+# Determine the arch/os combos we're building for
+XC_ARCH=${XC_ARCH:-"386 amd64 arm"}
+XC_OS=${XC_OS:-linux darwin windows freebsd openbsd}
+
+# Install dependencies
+echo "==> Getting dependencies..."
+
+#go get ./...
+
+# Delete the old dir
+echo "==> Removing old directory..."
+rm -f bin/*
+rm -rf pkg/*
+mkdir -p bin/
+
+# Build!
+echo "==> Building..."
+set +e
+gox \
+ -os="${XC_OS}" \
+ -arch="${XC_ARCH}" \
+ -ldflags "-X main.GitCommit ${GIT_COMMIT}${GIT_DIRTY}" \
+ -output "pkg/{{.OS}}_{{.Arch}}/elasticfeed-{{.Dir}}" \
+ ./...
+set -e
+
+# Make sure "elasticfeed-elasticfeed" is renamed properly
+for PLATFORM in $(find ./pkg -mindepth 1 -maxdepth 1 -type d); do
+ set +e
+ mv ${PLATFORM}/elasticfeed-elasticfeed.exe ${PLATFORM}/elasticfeed.exe 2>/dev/null
+ mv ${PLATFORM}/elasticfeed-elasticfeed ${PLATFORM}/elasticfeed 2>/dev/null
+ set -e
+done
+
+# Move all the compiled things to the $GOPATH/bin
+GOPATH=${GOPATH:-$(go env GOPATH)}
+case $(uname) in
+ CYGWIN*)
+ GOPATH="$(cygpath $GOPATH)"
+ ;;
+esac
+OLDIFS=$IFS
+IFS=: MAIN_GOPATH=($GOPATH)
+IFS=$OLDIFS
+
+# Copy our OS/Arch to the bin/ directory
+echo "==> Copying binaries for this platform..."
+DEV_PLATFORM="./pkg/$(go env GOOS)_$(go env GOARCH)"
+for F in $(find ${DEV_PLATFORM} -mindepth 1 -maxdepth 1 -type f); do
+ cp ${F} bin/
+ cp ${F} ${MAIN_GOPATH}/bin/
+done
+
+# Done!
+echo
+echo "==> Results:"
+ls -hl bin/
diff --git a/scripts/dist.sh b/scripts/dist.sh
new file mode 100755
index 0000000..2e76ed0
--- /dev/null
+++ b/scripts/dist.sh
@@ -0,0 +1,57 @@
+#!/bin/bash
+set -e
+
+# Get the parent directory of where this script is.
+SOURCE="${BASH_SOURCE[0]}"
+while [ -h "$SOURCE" ] ; do SOURCE="$(readlink "$SOURCE")"; done
+DIR="$( cd -P "$( dirname "$SOURCE" )/.." && pwd )"
+
+# Change into that dir because we expect that
+cd $DIR
+
+# Get the version from the command line
+VERSION=$1
+if [ -z $VERSION ]; then
+ echo "Please specify a version."
+ exit 1
+fi
+
+# Make sure we have a bintray API key
+if [ -z $BINTRAY_API_KEY ]; then
+ echo "Please set your bintray API key in the BINTRAY_API_KEY env var."
+ exit 1
+fi
+
+# Zip and copy to the dist dir
+echo "==> Packaging..."
+rm -rf ./pkg/dist
+mkdir -p ./pkg/dist
+for PLATFORM in $(find ./pkg -mindepth 1 -maxdepth 1 -type d); do
+ OSARCH=$(basename ${PLATFORM})
+
+ if [ $OSARCH = "dist" ]; then
+ continue
+ fi
+
+ echo "--> ${OSARCH}"
+ pushd $PLATFORM >/dev/null 2>&1
+ zip ../dist/elasticfeed_${VERSION}_${OSARCH}.zip ./*
+ popd >/dev/null 2>&1
+done
+
+# Make the checksums
+echo "==> Checksumming..."
+pushd ./pkg/dist >/dev/null 2>&1
+shasum -a256 * > ./elasticfeed_${VERSION}_SHA256SUMS
+popd >/dev/null 2>&1
+
+echo "==> Uploading..."
+for ARCHIVE in ./pkg/dist/*; do
+ ARCHIVE_NAME=$(basename ${ARCHIVE})
+
+ echo Uploading: $ARCHIVE_NAME
+ curl \
+ -T ${ARCHIVE} \
+ -umitchellh:${BINTRAY_API_KEY} \
+ "https://api.bintray.com/content/kris-lab/elasticfeed/elasticfeed/${VERSION}/${ARCHIVE_NAME}"
+done
diff --git a/scripts/upload.sh b/scripts/upload.sh
new file mode 100755
index 0000000..f8a23b7
--- /dev/null
+++ b/scripts/upload.sh
@@ -0,0 +1,33 @@
+#!/bin/bash
+set -e
+
+# Get the parent directory of where this script is.
+SOURCE="${BASH_SOURCE[0]}"
+while [ -h "$SOURCE" ] ; do SOURCE="$(readlink "$SOURCE")"; done
+DIR="$( cd -P "$( dirname "$SOURCE" )/.." && pwd )"
+
+# Change into that dir because we expect that
+cd $DIR
+
+# Get the version from the command line
+VERSION=$1
+if [ -z $VERSION ]; then
+ echo "Please specify a version."
+ exit 1
+fi
+
+# Make sure we have a bintray API key
+if [ -z $BINTRAY_API_KEY ]; then
+ echo "Please set your bintray API key in the BINTRAY_API_KEY env var."
+ exit 1
+fi
+
+for ARCHIVE in ./pkg/dist/*; do
+ ARCHIVE_NAME=$(basename ${ARCHIVE})
+
+ echo Uploading: $ARCHIVE_NAME
+ curl \
+ -T ${ARCHIVE} \
+ -umitchellh:${BINTRAY_API_KEY} \
+ "https://api.bintray.com/content/kris-lab/elasticfeed/elasticfeed/${VERSION}/${ARCHIVE_NAME}"
+done
diff --git a/scripts/website_push.sh b/scripts/website_push.sh
new file mode 100755
index 0000000..fafcbd7
--- /dev/null
+++ b/scripts/website_push.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+# Get the parent directory of where this script is.
+SOURCE="${BASH_SOURCE[0]}"
+while [ -h "$SOURCE" ] ; do SOURCE="$(readlink "$SOURCE")"; done
+DIR="$( cd -P "$( dirname "$SOURCE" )/.." && pwd )"
+
+# Change into that directory
+cd $DIR
+
+# Push the subtree (force)
+git push heroku `git subtree split --prefix website master`:master --force
diff --git a/service/db/api.go b/service/db/api.go
deleted file mode 100644
index c51e66e..0000000
--- a/service/db/api.go
+++ /dev/null
@@ -1,7 +0,0 @@
-package api
-
-import (
- _ "github.com/feedlabs/elasticfeed/service/db/v1"
-)
-
-func init() {}
diff --git a/service/db/v1/api.go b/service/db/v1/api.go
deleted file mode 100644
index 522b251..0000000
--- a/service/db/v1/api.go
+++ /dev/null
@@ -1,8 +0,0 @@
-package v1
-
-import (
- _ "github.com/feedlabs/elasticfeed/service/db/v1/router"
- _ "github.com/feedlabs/elasticfeed/service/db/v1/controller"
-)
-
-func init() {}
diff --git a/service/db/v1/router/default.go b/service/db/v1/router/default.go
deleted file mode 100644
index 82db7f3..0000000
--- a/service/db/v1/router/default.go
+++ /dev/null
@@ -1,11 +0,0 @@
-package router
-
-import (
- "github.com/feedlabs/feedify"
- "github.com/feedlabs/elasticfeed/service/db/v1/controller"
-)
-
-func init() {
- feedify.Router("/", &controller.DefaultController{}, "get:Get")
- feedify.Router("/v1", &controller.DefaultController{}, "get:Get")
-}
diff --git a/service/service.go b/service/service.go
index 3e3d97e..02da517 100644
--- a/service/service.go
+++ b/service/service.go
@@ -1,9 +1,37 @@
package service
import (
- _ "github.com/feedlabs/elasticfeed/service/db"
- _ "github.com/feedlabs/elasticfeed/service/stream"
- _ "github.com/feedlabs/elasticfeed/service/system"
+ "github.com/feedlabs/elasticfeed/service/store"
+ "github.com/feedlabs/elasticfeed/service/stream"
+ "github.com/feedlabs/elasticfeed/service/system"
)
-func init() {}
+type Service struct {}
+
+type ServiceManager struct {
+ store *store.DbService
+ stream *stream.StreamService
+ system *system.SystemService
+}
+
+func (this *ServiceManager) Init() {
+ this.system.Init()
+ this.stream.Init()
+ this.store.Init()
+}
+
+func (this *ServiceManager) GetDbService() *store.DbService {
+ return this.store
+}
+
+func (this *ServiceManager) GetStreamService() *stream.StreamService {
+ return this.stream
+}
+
+func (this *ServiceManager) GetSystemService() *system.SystemService {
+ return this.system
+}
+
+func NewServiceManager() *ServiceManager {
+ return &ServiceManager{}
+}
diff --git a/service/store/api.go b/service/store/api.go
new file mode 100644
index 0000000..ff25023
--- /dev/null
+++ b/service/store/api.go
@@ -0,0 +1,17 @@
+package store
+
+import (
+ "github.com/feedlabs/elasticfeed/service/store/v1/router"
+ "github.com/feedlabs/elasticfeed/service/store/v1/controller"
+)
+
+type DbService struct {}
+
+func (this *DbService) Init() {
+ router.InitRouters()
+ controller.InitService()
+}
+
+func NewMetricService() *DbService {
+ return &DbService{}
+}
diff --git a/service/db/v1/controller/admin.go b/service/store/v1/controller/admin.go
similarity index 97%
rename from service/db/v1/controller/admin.go
rename to service/store/v1/controller/admin.go
index 71807de..0ba1fdb 100644
--- a/service/db/v1/controller/admin.go
+++ b/service/store/v1/controller/admin.go
@@ -4,7 +4,7 @@ import (
"encoding/json"
"github.com/feedlabs/elasticfeed/resource"
- "github.com/feedlabs/elasticfeed/service/db/v1/template/admin"
+ "github.com/feedlabs/elasticfeed/service/store/v1/template/admin"
)
type AdminController struct {
diff --git a/service/db/v1/controller/application.go b/service/store/v1/controller/application.go
similarity index 98%
rename from service/db/v1/controller/application.go
rename to service/store/v1/controller/application.go
index 63c1bd0..2304431 100644
--- a/service/db/v1/controller/application.go
+++ b/service/store/v1/controller/application.go
@@ -4,7 +4,7 @@ import (
"encoding/json"
"github.com/feedlabs/elasticfeed/resource"
- "github.com/feedlabs/elasticfeed/service/db/v1/template/application"
+ "github.com/feedlabs/elasticfeed/service/store/v1/template/application"
)
type ApplicationController struct {
diff --git a/service/db/v1/controller/default.go b/service/store/v1/controller/default.go
similarity index 90%
rename from service/db/v1/controller/default.go
rename to service/store/v1/controller/default.go
index 63a464a..db36df9 100644
--- a/service/db/v1/controller/default.go
+++ b/service/store/v1/controller/default.go
@@ -6,8 +6,8 @@ import (
"github.com/feedlabs/feedify"
"github.com/feedlabs/elasticfeed/resource"
- "github.com/feedlabs/elasticfeed/helper"
- "github.com/feedlabs/elasticfeed/service/db/v1/template"
+ "github.com/feedlabs/elasticfeed/common"
+ "github.com/feedlabs/elasticfeed/service/store/v1/template"
)
type DefaultController struct {
@@ -53,7 +53,7 @@ func SetGlobalResponseHeader() {
func SetAuthenticationFilter() {
var AuthUser = func(ctx *context.Context) {
- ctx.Input.Data["admin"] = helper.Auth(ctx)
+ ctx.Input.Data["admin"] = common.Auth(ctx)
}
beego.InsertFilter("/v1/*", beego.BeforeRouter, AuthUser)
}
@@ -67,7 +67,7 @@ func NoRoutes() {
beego.InsertFilter("/*", beego.AfterStatic, router)
}
-func init() {
+func InitService() {
SetAuthenticationFilter()
SetGlobalResponseHeader()
// NoRoutes()
diff --git a/service/db/v1/controller/entry.go b/service/store/v1/controller/entry.go
similarity index 98%
rename from service/db/v1/controller/entry.go
rename to service/store/v1/controller/entry.go
index 8bc6a24..7bb3772 100644
--- a/service/db/v1/controller/entry.go
+++ b/service/store/v1/controller/entry.go
@@ -4,7 +4,7 @@ import (
"encoding/json"
"github.com/feedlabs/elasticfeed/resource"
- "github.com/feedlabs/elasticfeed/service/db/v1/template/entry"
+ "github.com/feedlabs/elasticfeed/service/store/v1/template/entry"
)
type EntryController struct {
diff --git a/service/db/v1/controller/feed.go b/service/store/v1/controller/feed.go
similarity index 96%
rename from service/db/v1/controller/feed.go
rename to service/store/v1/controller/feed.go
index e421435..99ff1e1 100644
--- a/service/db/v1/controller/feed.go
+++ b/service/store/v1/controller/feed.go
@@ -4,9 +4,9 @@ import (
"encoding/json"
"github.com/feedlabs/elasticfeed/resource"
- "github.com/feedlabs/elasticfeed/service/db/v1/template/feed"
- "github.com/feedlabs/elasticfeed/service/db/v1/template"
+ "github.com/feedlabs/elasticfeed/service/store/v1/template"
+ "github.com/feedlabs/elasticfeed/service/store/v1/template/feed"
)
type FeedController struct {
diff --git a/service/db/v1/controller/org.go b/service/store/v1/controller/org.go
similarity index 97%
rename from service/db/v1/controller/org.go
rename to service/store/v1/controller/org.go
index c26211a..f18ed78 100644
--- a/service/db/v1/controller/org.go
+++ b/service/store/v1/controller/org.go
@@ -4,7 +4,7 @@ import (
"encoding/json"
"github.com/feedlabs/elasticfeed/resource"
- template "github.com/feedlabs/elasticfeed/service/db/v1/template/org"
+ template "github.com/feedlabs/elasticfeed/service/store/v1/template/org"
)
type OrgController struct {
diff --git a/service/db/v1/controller/token.go b/service/store/v1/controller/token.go
similarity index 98%
rename from service/db/v1/controller/token.go
rename to service/store/v1/controller/token.go
index 4b7cad6..c7a0324 100644
--- a/service/db/v1/controller/token.go
+++ b/service/store/v1/controller/token.go
@@ -4,7 +4,7 @@ import (
"encoding/json"
"github.com/feedlabs/elasticfeed/resource"
- "github.com/feedlabs/elasticfeed/service/db/v1/template/token"
+ "github.com/feedlabs/elasticfeed/service/store/v1/template/token"
)
type TokenController struct {
diff --git a/service/db/v1/router/admin.go b/service/store/v1/router/admin.go
similarity index 74%
rename from service/db/v1/router/admin.go
rename to service/store/v1/router/admin.go
index 47bfbd8..19198bf 100644
--- a/service/db/v1/router/admin.go
+++ b/service/store/v1/router/admin.go
@@ -2,10 +2,10 @@ package router
import (
"github.com/feedlabs/feedify"
- "github.com/feedlabs/elasticfeed/service/db/v1/controller"
+ "github.com/feedlabs/elasticfeed/service/store/v1/controller"
)
-func init() {
+func InitAdminRouters() {
feedify.Router("/v1/admin", &controller.AdminController{}, "get:GetList;post:Post")
feedify.Router("/v1/admin/:adminId:string", &controller.AdminController{}, "get:Get;delete:Delete;put:Put")
}
diff --git a/service/db/v1/router/application.go b/service/store/v1/router/application.go
similarity index 75%
rename from service/db/v1/router/application.go
rename to service/store/v1/router/application.go
index 4a72333..2f5a634 100644
--- a/service/db/v1/router/application.go
+++ b/service/store/v1/router/application.go
@@ -2,10 +2,10 @@ package router
import (
"github.com/feedlabs/feedify"
- "github.com/feedlabs/elasticfeed/service/db/v1/controller"
+ "github.com/feedlabs/elasticfeed/service/store/v1/controller"
)
-func init() {
+func InitApplicationRouters() {
feedify.Router("/v1/application", &controller.ApplicationController{}, "get:GetList;post:Post")
feedify.Router("/v1/application/:applicationId:string", &controller.ApplicationController{}, "get:Get;delete:Delete;put:Put")
}
diff --git a/service/store/v1/router/default.go b/service/store/v1/router/default.go
new file mode 100644
index 0000000..eb0b3ca
--- /dev/null
+++ b/service/store/v1/router/default.go
@@ -0,0 +1,21 @@
+package router
+
+import (
+ "github.com/feedlabs/feedify"
+ "github.com/feedlabs/elasticfeed/service/store/v1/controller"
+)
+
+func InitDefaultRouters() {
+ feedify.Router("/", &controller.DefaultController{}, "get:Get")
+ feedify.Router("/v1", &controller.DefaultController{}, "get:Get")
+}
+
+func InitRouters() {
+ InitDefaultRouters()
+ InitAdminRouters()
+ InitApplicationRouters()
+ InitEntryRouters()
+ InitFeedRouters()
+ InitOrgRouters()
+ InitTokenRouters()
+}
diff --git a/service/db/v1/router/entry.go b/service/store/v1/router/entry.go
similarity index 87%
rename from service/db/v1/router/entry.go
rename to service/store/v1/router/entry.go
index dfa9104..44d1398 100644
--- a/service/db/v1/router/entry.go
+++ b/service/store/v1/router/entry.go
@@ -2,10 +2,10 @@ package router
import (
"github.com/feedlabs/feedify"
- "github.com/feedlabs/elasticfeed/service/db/v1/controller"
+ "github.com/feedlabs/elasticfeed/service/store/v1/controller"
)
-func init() {
+func InitEntryRouters() {
feedify.Router("/v1/application/:applicationId:string/entry", &controller.EntryController{}, "post:Post")
feedify.Router("/v1/application/:applicationId:string/entry/:feedEntryId:int", &controller.EntryController{}, "get:Get;delete:Delete;put:Put")
diff --git a/service/db/v1/router/feed.go b/service/store/v1/router/feed.go
similarity index 86%
rename from service/db/v1/router/feed.go
rename to service/store/v1/router/feed.go
index bde8170..799624f 100644
--- a/service/db/v1/router/feed.go
+++ b/service/store/v1/router/feed.go
@@ -2,10 +2,10 @@ package router
import (
"github.com/feedlabs/feedify"
- "github.com/feedlabs/elasticfeed/service/db/v1/controller"
+ "github.com/feedlabs/elasticfeed/service/store/v1/controller"
)
-func init() {
+func InitFeedRouters() {
feedify.Router("/v1/application/:applicationId:string/feed", &controller.FeedController{}, "get:GetList;post:Post")
feedify.Router("/v1/application/:applicationId:string/feed/:feedId:int", &controller.FeedController{}, "get:Get;delete:Delete;put:Put")
diff --git a/service/db/v1/router/org.go b/service/store/v1/router/org.go
similarity index 73%
rename from service/db/v1/router/org.go
rename to service/store/v1/router/org.go
index 6a6b08a..8e8a1eb 100644
--- a/service/db/v1/router/org.go
+++ b/service/store/v1/router/org.go
@@ -2,10 +2,10 @@ package router
import (
"github.com/feedlabs/feedify"
- "github.com/feedlabs/elasticfeed/service/db/v1/controller"
+ "github.com/feedlabs/elasticfeed/service/store/v1/controller"
)
-func init() {
+func InitOrgRouters() {
feedify.Router("/v1/org", &controller.OrgController{}, "get:GetList;post:Post")
feedify.Router("/v1/org/:orgId:string", &controller.OrgController{}, "get:Get;delete:Delete;put:Put")
}
diff --git a/service/db/v1/router/token.go b/service/store/v1/router/token.go
similarity index 85%
rename from service/db/v1/router/token.go
rename to service/store/v1/router/token.go
index f3be790..098aafd 100644
--- a/service/db/v1/router/token.go
+++ b/service/store/v1/router/token.go
@@ -2,10 +2,10 @@ package router
import (
"github.com/feedlabs/feedify"
- "github.com/feedlabs/elasticfeed/service/db/v1/controller"
+ "github.com/feedlabs/elasticfeed/service/store/v1/controller"
)
-func init() {
+func InitTokenRouters() {
feedify.Router("/v1/org/:orgId:string/token", &controller.TokenController{}, "get:GetOrgList;post:PostOrg")
feedify.Router("/v1/org/:orgId:string/token/:tokenId:string", &controller.TokenController{}, "get:GetOrg;delete:DeleteOrg")
diff --git a/service/db/v1/template/admin/request.go b/service/store/v1/template/admin/request.go
similarity index 100%
rename from service/db/v1/template/admin/request.go
rename to service/store/v1/template/admin/request.go
diff --git a/service/db/v1/template/admin/response.go b/service/store/v1/template/admin/response.go
similarity index 100%
rename from service/db/v1/template/admin/response.go
rename to service/store/v1/template/admin/response.go
diff --git a/service/db/v1/template/application/request.go b/service/store/v1/template/application/request.go
similarity index 100%
rename from service/db/v1/template/application/request.go
rename to service/store/v1/template/application/request.go
diff --git a/service/db/v1/template/application/response.go b/service/store/v1/template/application/response.go
similarity index 100%
rename from service/db/v1/template/application/response.go
rename to service/store/v1/template/application/response.go
diff --git a/service/db/v1/template/const.go b/service/store/v1/template/const.go
similarity index 100%
rename from service/db/v1/template/const.go
rename to service/store/v1/template/const.go
diff --git a/service/db/v1/template/default.go b/service/store/v1/template/default.go
similarity index 100%
rename from service/db/v1/template/default.go
rename to service/store/v1/template/default.go
diff --git a/service/db/v1/template/entry/request.go b/service/store/v1/template/entry/request.go
similarity index 100%
rename from service/db/v1/template/entry/request.go
rename to service/store/v1/template/entry/request.go
diff --git a/service/db/v1/template/entry/response.go b/service/store/v1/template/entry/response.go
similarity index 100%
rename from service/db/v1/template/entry/response.go
rename to service/store/v1/template/entry/response.go
diff --git a/service/db/v1/template/feed/request.go b/service/store/v1/template/feed/request.go
similarity index 100%
rename from service/db/v1/template/feed/request.go
rename to service/store/v1/template/feed/request.go
diff --git a/service/db/v1/template/feed/response.go b/service/store/v1/template/feed/response.go
similarity index 100%
rename from service/db/v1/template/feed/response.go
rename to service/store/v1/template/feed/response.go
diff --git a/service/db/v1/template/org/request.go b/service/store/v1/template/org/request.go
similarity index 96%
rename from service/db/v1/template/org/request.go
rename to service/store/v1/template/org/request.go
index 0efc62b..b484e24 100644
--- a/service/db/v1/template/org/request.go
+++ b/service/store/v1/template/org/request.go
@@ -3,7 +3,7 @@ package org
import (
"errors"
"github.com/feedlabs/feedify/context"
- "github.com/feedlabs/elasticfeed/service/db/v1/template"
+ "github.com/feedlabs/elasticfeed/service/store/v1/template"
)
func CheckRequiredParams() {
diff --git a/service/db/v1/template/org/response.go b/service/store/v1/template/org/response.go
similarity index 98%
rename from service/db/v1/template/org/response.go
rename to service/store/v1/template/org/response.go
index 3c951bf..37a7b0a 100644
--- a/service/db/v1/template/org/response.go
+++ b/service/store/v1/template/org/response.go
@@ -3,7 +3,7 @@ package org
import (
"strconv"
"github.com/feedlabs/elasticfeed/resource"
- "github.com/feedlabs/elasticfeed/service/db/v1/template"
+ "github.com/feedlabs/elasticfeed/service/store/v1/template"
"errors"
"sort"
)
diff --git a/service/db/v1/template/request.go b/service/store/v1/template/request.go
similarity index 100%
rename from service/db/v1/template/request.go
rename to service/store/v1/template/request.go
diff --git a/service/db/v1/template/response.go b/service/store/v1/template/response.go
similarity index 100%
rename from service/db/v1/template/response.go
rename to service/store/v1/template/response.go
diff --git a/service/db/v1/template/token/request.go b/service/store/v1/template/token/request.go
similarity index 100%
rename from service/db/v1/template/token/request.go
rename to service/store/v1/template/token/request.go
diff --git a/service/db/v1/template/token/response.go b/service/store/v1/template/token/response.go
similarity index 100%
rename from service/db/v1/template/token/response.go
rename to service/store/v1/template/token/response.go
diff --git a/service/stream/controller/channel/long_pooling.go b/service/stream/controller/channel/long_pooling.go
index 8e2d838..0ea3a4f 100644
--- a/service/stream/controller/channel/long_pooling.go
+++ b/service/stream/controller/channel/long_pooling.go
@@ -23,6 +23,12 @@ func (this *LongPollingController) Join() {
defer sess.SessionRelease(w)
room.Join(chid, nil)
+
+ list := make(map[string]interface {})
+ list["response"] = room.NewChannelEvent(room.CHANNEL_JOIN, "system", "join")
+
+ this.Data["json"] = list
+ this.ServeJson()
}
func (this *LongPollingController) Post() {
@@ -37,7 +43,19 @@ func (this *LongPollingController) Post() {
// should be executed and returned directly to user
// lastReceived time should not be changed in that case
- room.Publish <- room.NewSystemEvent(room.CHANNEL_MESSAGE, chid, data)
+ // room.Publish <- room.NewSystemEvent(room.CHANNEL_MESSAGE, chid, data)
+
+ ch := make(chan []byte)
+
+ room.ResourceEvent <- room.NewSocketEvent([]byte(data), nil, ch)
+
+ response := <-ch
+
+ list := make(map[string]string)
+ list["response"] = string(response)
+
+ this.Data["json"] = list
+ this.ServeJson()
}
func (this *LongPollingController) Fetch() {
diff --git a/service/stream/controller/channel/sse.go b/service/stream/controller/channel/sse.go
new file mode 100644
index 0000000..17f57a8
--- /dev/null
+++ b/service/stream/controller/channel/sse.go
@@ -0,0 +1,17 @@
+package channel
+
+import (
+ "github.com/feedlabs/feedify"
+
+// "github.com/mroth/sseserver"
+)
+
+type SSEController struct {
+ feedify.Controller
+}
+
+func (this *SSEController) Join() {
+}
+
+func (this *SSEController) Post() {
+}
diff --git a/service/stream/controller/channel/websocket.go b/service/stream/controller/channel/websocket.go
index 568cc00..c23ab62 100644
--- a/service/stream/controller/channel/websocket.go
+++ b/service/stream/controller/channel/websocket.go
@@ -2,6 +2,7 @@ package channel
import (
"net/http"
+ "encoding/json"
"github.com/feedlabs/feedify"
"github.com/gorilla/websocket"
@@ -36,13 +37,15 @@ func (this *WebSocketController) Join() {
room.Join(chid, ws)
defer room.Leave(chid)
+ data, _ := json.Marshal(room.NewChannelEvent(room.CHANNEL_JOIN, chid, "join"))
+ ws.WriteMessage(websocket.TextMessage, data)
+
for {
_, p, err := ws.ReadMessage()
if err != nil {
return
}
- room.Publish <- room.NewSystemEvent(room.CHANNEL_MESSAGE, chid, string(p))
- room.P2P <- ws
+ room.ResourceEvent <- room.NewSocketEvent(p, ws, nil)
}
}
diff --git a/service/stream/controller/controller.go b/service/stream/controller/controller.go
index 86c04e6..533e71a 100644
--- a/service/stream/controller/controller.go
+++ b/service/stream/controller/controller.go
@@ -1,8 +1,9 @@
package controller
import (
- _ "github.com/feedlabs/elasticfeed/service/stream/controller/channel"
- _ "github.com/feedlabs/elasticfeed/service/stream/controller/room"
+ "github.com/feedlabs/elasticfeed/service/stream/controller/room"
)
-func init() {}
+func InitRooms() {
+ room.InitFeedRoom()
+}
diff --git a/service/stream/controller/room/feed.go b/service/stream/controller/room/feed.go
index 2ef6d2e..c839d4b 100644
--- a/service/stream/controller/room/feed.go
+++ b/service/stream/controller/room/feed.go
@@ -8,7 +8,9 @@ import (
"github.com/feedlabs/feedify"
"github.com/gorilla/websocket"
+
"github.com/astaxie/beego/session"
+
"github.com/feedlabs/elasticfeed/service/stream/model"
)
@@ -19,18 +21,14 @@ const (
SYSTEM_FEED_MESSAGE = 1
- FEED_RELOAD = 1
- FEED_EMPTY = 2
- FEED_ENTRY_NEW = 3
- FEED_ENTRY_INIT = 4
- FEED_ENTRY_MORE = 5
- FEED_HIDE = 6
- FEED_SHOW = 7
- FEED_ENTRY_MESSAGE = 8
- FEED_AUTHENTICATED = 100
- FEED_AUTHENTICATION_REQUIRED = 101
- FEED_AUTHENTICATION_FAILED = 102
- FEED_LOGGED_OUT = 103
+ FEED_RELOAD = 1
+ FEED_EMPTY = 2
+ FEED_ENTRY_NEW = 3
+ FEED_ENTRY_INIT = 4
+ FEED_ENTRY_MORE = 5
+ FEED_HIDE = 6
+ FEED_SHOW = 7
+ FEED_ENTRY_MESSAGE = 8
ENTRY_UPDATE = 1
ENTRY_DELETE = 2
@@ -39,14 +37,16 @@ const (
)
var (
- Subscribe = make(chan Subscriber, 10)
- Unsubscribe = make(chan string, 10)
- Publish = make(chan model.Event, 10)
- P2P = make(chan *websocket.Conn, 10)
+ Subscribe = make(chan Subscriber, 10)
+ Unsubscribe = make(chan string, 10)
+ Publish = make(chan model.Event, 10)
+ ResourceEvent = make(chan model.SocketEvent, 10)
WaitingList = list.New()
Subscribers = list.New()
+ FeedSubscribers = make(map[string]interface{})
+
GlobalSessions *session.Manager
)
@@ -56,8 +56,8 @@ type Subscription struct {
}
type Subscriber struct {
- Name string
- Conn *websocket.Conn
+ Name string
+ Conn *websocket.Conn
}
func NewEvent(ep model.EventType, user, msg string) model.Event {
@@ -65,6 +65,14 @@ func NewEvent(ep model.EventType, user, msg string) model.Event {
return model.Event{ep, user, ts, strconv.Itoa(int(ts)), msg}
}
+func NewSocketEvent(msg []byte, ws *websocket.Conn, ch chan []byte) model.SocketEvent {
+ data := make(map[string]interface{})
+
+ json.Unmarshal(msg, &data)
+
+ return model.SocketEvent{ws, ch, data["feedId"].(string), data["appId"].(string), data["orgId"].(string)}
+}
+
func NewChannelEvent(ep model.EventType, user, msg string) model.Event {
return NewEvent(ep, user, msg)
}
@@ -108,13 +116,17 @@ func FeedManager() {
case sub := <-Subscribe:
Subscribers.PushBack(sub)
- Publish <- NewChannelEvent(CHANNEL_JOIN, sub.Name, "")
-
- case client := <-P2P:
- data, _ := json.Marshal(NewSystemEvent(CHANNEL_MESSAGE, "system", "ok"))
- client.WriteMessage(websocket.TextMessage, data)
case event := <-Publish:
+
+ // here must be handled where to send notification
+ // - or to all sockets
+ // - or to specific client/feed (single socket)
+ // - or to public feed (multiple sockets)
+ //
+ // could be setup by resource manager go routine
+ // room.FeedSubscribers[socketEvent.FeedId][channelID] = socketEvent
+
model.NewArchive(event)
for ch := WaitingList.Back(); ch != nil; ch = ch.Prev() {
@@ -159,9 +171,9 @@ func broadcastWebSocket(event model.Event) {
}
}
-func init() {
+func InitFeedRoom() {
GlobalSessions, _ = session.NewManager("memory", `{"cookieName":"elasticfeedsessid","gclifetime":3600}`)
- go FeedManager()
go GlobalSessions.GC()
+ go FeedManager()
}
diff --git a/service/stream/model/event.go b/service/stream/model/event.go
index eb8b776..40a287c 100644
--- a/service/stream/model/event.go
+++ b/service/stream/model/event.go
@@ -2,6 +2,8 @@ package model
import (
"container/list"
+
+ "github.com/gorilla/websocket"
)
type EventType int
@@ -14,6 +16,14 @@ type Event struct {
Content string
}
+type SocketEvent struct {
+ Ws *websocket.Conn
+ Ch chan []byte
+ FeedId string
+ AppId string
+ OrgId string
+}
+
const archiveSize = 1
var Archive = list.New()
diff --git a/service/stream/router/default.go b/service/stream/router/default.go
index acf4c10..a1dbd32 100644
--- a/service/stream/router/default.go
+++ b/service/stream/router/default.go
@@ -5,12 +5,13 @@ import (
"github.com/feedlabs/elasticfeed/service/stream/controller/channel"
)
-func init() {
- feedify.SetStaticPath("/static", "service/stream/static")
-
+func InitRouters() {
feedify.Router("/stream/lp/join", &channel.LongPollingController{}, "get:Join")
feedify.Router("/stream/lp/post", &channel.LongPollingController{})
feedify.Router("/stream/lp/fetch", &channel.LongPollingController{}, "get:Fetch")
feedify.Router("/stream/ws/join", &channel.WebSocketController{}, "get:Join")
+
+ feedify.Router("/stream/sse/join", &channel.SSEController{}, "get:Join")
+ feedify.Router("/stream/sse/post", &channel.SSEController{})
}
diff --git a/service/stream/static/elasticfeed.js b/service/stream/static/elasticfeed.js
deleted file mode 100644
index 4f155a8..0000000
--- a/service/stream/static/elasticfeed.js
+++ /dev/null
@@ -1,130 +0,0 @@
-function includeJs(jsFilePath) {
- var js = document.createElement("script");
-
- js.type = "text/javascript";
- js.src = jsFilePath;
-
- document.body.appendChild(js);
-}
-
-includeJs('lib/feed.js');
-includeJs('lib/entry.js');
-includeJs('lib/channel.js');
-includeJs('lib/event.js');
-
-(function(window) {
-
- /** @type {Object} */
- var defaultOptions = {
- channel: {
- url: 'localhost',
- transport: 'ws'
- },
- stylerFunction: function(data) {
- }
- }
-
- var elasticfeed = {
-
- /** @type {Object} */
- options: {},
-
- /** @type {Object} */
- channelList: {},
-
- /** @type {Object} */
- feedList: {},
-
- init: function(options) {
- this.options = _extend(defaultOptions, options);
- },
-
- initFeed: function(id, options) {
- if (id == undefined) {
- return false;
- }
-
- if (this.feedList[id] == undefined) {
- opts = _extend(this.options, options || {});
- channel = this.getChannel(opts.channel);
-
- this.feedList[id] = new Feed(id, opts, channel);
- }
-
- return this.feedList[id];
- },
-
- /**
- * Returns Channel defined per API url
- * @param options
- * @param credential
- * @returns {*}
- */
- getChannel: function(options, credential) {
- if (options.url == undefined) {
- return false;
- }
-
- if (this.channelList[options.url] == undefined) {
- this.channelList[options.url] = new Channel(options, credential)
- }
-
- return this.channelList[options.url];
- },
-
- findFeed: function(id) {
- if (this.feedList[id] == undefined) {
- return false;
- }
- return this.feedList[id];
- },
-
- findChannel: function(url) {
- if (this.channelList[url] == undefined) {
- return false;
- }
- return this.channelList[url];
- }
-
- };
-
- // Helpers
-
- var _extend = function(a, b) {
- var c = {}, prop;
- for (prop in a) {
- if (a.hasOwnProperty(prop)) {
- c[prop] = a[prop];
- }
- }
- for (prop in b) {
- if (b.hasOwnProperty(prop)) {
- c[prop] = b[prop];
- }
- }
- return c;
- }
-
- if ("function" === typeof define) {
- define(function(require) {
- return elasticfeed;
- });
- } else {
- window.elasticfeed = elasticfeed;
- }
-
-}(window));
-
-// Helpers
-
-Element.prototype.remove = function() {
- this.parentElement.removeChild(this);
-};
-
-NodeList.prototype.remove = HTMLCollection.prototype.remove = function() {
- for (var i = 0, len = this.length; i < len; i++) {
- if (this[i] && this[i].parentElement) {
- this[i].parentElement.removeChild(this[i]);
- }
- }
-};
diff --git a/service/stream/static/lib/channel.js b/service/stream/static/lib/channel.js
deleted file mode 100644
index 24d1ba6..0000000
--- a/service/stream/static/lib/channel.js
+++ /dev/null
@@ -1,280 +0,0 @@
-var Channel = (function() {
-
- const JOIN = 0
- const LEAVE = 1
- const MESSAGE = 2
-
- var defaultOptions = {
- id: null,
- transport: 'ws',
- connectOnInit: true
- }
-
- function Channel(options) {
-
- /** @type {String} */
- this.id = _uniqueId();
-
- /** @type {String} */
- this.url = null
-
- /** @type {Object} */
- this.options = _extend(defaultOptions, options);
-
- if (this.options.id != null) {
- this.id = this.options.id;
- }
-
- if (this.options.url != null) {
- this.url = this.options.url;
- }
-
- /** @type {Object} */
- this._handlers = {};
-
- /** @type {WebSocket} */
- this._socket = null;
- }
-
- // Handlers
-
- /**
- * @param {Event} event
- * @param {Function} callback
- */
- Channel.prototype.on = function(name, callback) {
- switch (name) {
- case 'join':
- type = JOIN
- break;
- case 'leave':
- type = LEAVE
- break;
- case 'message':
- type = MESSAGE
- break;
- default:
- return false;
- break;
- }
- if (this._handlers[type] == undefined) {
- this._handlers[type] = []
- }
- this._handlers[type].push(callback);
- return true;
- }
-
- // Events
-
- /**
- * @param {Event} event
- */
- Channel.prototype.onData = function(event) {
- switch (event.type) {
- case JOIN:
- this.onJoin(event.user, event.ts)
- break;
- case LEAVE:
- this.onLeave(event.user, event.ts)
- break;
- case MESSAGE:
- this.onMessage(event.user, event.ts, event.content)
- break;
- }
- }
-
- Channel.prototype.onJoin = function(chid, timestamp) {
- for (var i in this._handlers[JOIN]) {
- this._handlers[JOIN][i].call(this, chid, timestamp);
- }
- }
-
- Channel.prototype.onLeave = function(chid, timestamp) {
- for (var i in this._handlers[LEAVE]) {
- this._handlers[LEAVE][i].call(this, chid, timestamp);
- }
- }
-
- Channel.prototype.onMessage = function(chid, timestamp, data) {
- systemEvent = new Event(data);
-
- for (var i in this._handlers[MESSAGE]) {
- this._handlers[MESSAGE][i].call(this, chid, timestamp, systemEvent);
- }
- }
-
- // Connection
-
- Channel.prototype.isWebSocket = function() {
- return this._socket != undefined;
- }
-
- Channel.prototype.getConnection = function() {
- }
-
- Channel.prototype.getWebSocketConnection = function() {
- this._socket = new WebSocket('ws://localhost:10100/stream/ws/join?chid=' + this.id);
-
- self = this
- this._socket.onmessage = function(event) {
- event = new Event(JSON.parse(event.data))
- self.onData(event)
- };
-
- self = this
- return {
- send: function(data) {
- self._socket.send(JSON.stringify(data))
- }
- };
- }
-
- Channel.prototype.getLongPoolingConnection = function() {
- var lastReceived = 0;
- var isWait = false;
-
- this.getJSON('http://localhost:10100/stream/lp/join?chid=' + this.id, function(data) {
- // should set timestamp to proper one!
- })
-
- self = this;
- var fetch = function() {
- if (isWait) {
- return;
- }
- isWait = true;
- self.getJSON("http://localhost:10100/stream/lp/fetch?lastReceived=" + lastReceived, function(data, code) {
-
- if (code == 4) {
- isWait = false
- }
-
- if (data == null) {
- return;
- }
-
- self.each(data, function(i, event) {
- event = new Event(event)
- self.onData(event)
-
- lastReceived = event.GetTimestamp();
- });
- isWait = false;
- });
- }
-
- setInterval(fetch, 3000);
- fetch()
-
- return {
- send: function(data) {
- self.post("/stream/lp/post", {chid: self.id, data: JSON.stringify(data)}, function(status) {
- });
- }
- };
- }
-
- Channel.prototype.load = function(url, callback) {
- var xhr;
- if (typeof XMLHttpRequest !== 'undefined') {
- xhr = new XMLHttpRequest();
- } else {
- var versions = ["MSXML2.XmlHttp.5.0", "MSXML2.XmlHttp.4.0", "MSXML2.XmlHttp.3.0", "MSXML2.XmlHttp.2.0", "Microsoft.XmlHttp"];
- for (var i = 0, len = versions.length; i < len; i++) {
- try {
- xhr = new ActiveXObject(versions[i]);
- break;
- }
- catch (e) {
- }
- }
- }
- xhr.onreadystatechange = ensureReadiness;
- function ensureReadiness() {
- if (xhr.readyState < 4) {
- return;
- }
- if (xhr.status !== 200) {
- return;
- }
- if (xhr.readyState === 4) {
- callback(xhr);
- }
- }
-
- xhr.open('GET', url, true);
- xhr.send('');
- }
-
- // HTTP
-
- Channel.prototype.getJSON = function(url, callback) {
- xhr = new XMLHttpRequest;
- xhr.onreadystatechange = function() {
- if (xhr.readyState == 4 && xhr.status == 200) {
- if (xhr.responseText != "") {
- data = JSON.parse(xhr.responseText);
- callback.call(this, data, xhr.readyState)
- } else {
- callback.call(this, null, xhr.readyState)
- }
- } else {
- callback.call(this, null, xhr.readyState)
- }
- }
- xhr.open("GET", url)
- xhr.send();
- }
-
- Channel.prototype.each = function(obj, callback) {
- for (i = 0; i < obj.length; i++) {
- value = callback.call(obj[i], i, obj[i]);
-
- if (value === false) {
- break;
- }
- }
- }
-
- Channel.prototype.queryString = function(obj) {
- return Object.keys(obj).map(function(key) {
- return encodeURIComponent(key) + '=' + encodeURIComponent(obj[key]);
- }).join('&');
- }
-
- Channel.prototype.post = function(url, data, callback) {
- xhr1 = new XMLHttpRequest;
- xhr1.onreadystatechange = function() {
- if (xhr1.readyState == 4 && xhr1.status == 200) {
- callback.call(this, xhr1.responseText)
- }
- }
- dataString = this.queryString(data)
- xhr1.open("POST", url + "?" + dataString, true);
- xhr1.send(dataString);
- }
-
- // Helpers
-
- var _extend = function(a, b) {
- var c = {}, prop;
- for (prop in a) {
- if (a.hasOwnProperty(prop)) {
- c[prop] = a[prop];
- }
- }
- for (prop in b) {
- if (b.hasOwnProperty(prop)) {
- c[prop] = b[prop];
- }
- }
- return c;
- }
-
- var _uniqueId = function() {
- return '_' + Math.random().toString(36).substr(2, 36);
- }
-
- return Channel;
-
-})();
diff --git a/service/stream/static/lib/entry.js b/service/stream/static/lib/entry.js
deleted file mode 100644
index ced554a..0000000
--- a/service/stream/static/lib/entry.js
+++ /dev/null
@@ -1,182 +0,0 @@
-var Entry = (function() {
-
- const UPDATE = 1
- const DELETE = 2
- const HIDE = 3
- const SHOW = 4
-
- /** @type {Entry} */
- var localCache = {}
-
- function Entry(data, options) {
- /** @type {String} */
- this.id = null;
-
- /** @type {String} */
- this.viewId = _uniqueId();
-
- /** @type {String} */
- this.data = data;
-
- /** @type {Object} */
- this._feed = null;
-
- /** @type {Function} */
- this._styler = (options ? options.styler : undefined) || function() {
- return data;
- };
-
- /** @type {Object} */
- this._handlers = {};
- }
-
- Entry.prototype.setParent = function(feed) {
- this._feed = feed;
- this.bindMessages();
- }
-
- Entry.prototype.getViewId = function() {
- return this.viewId;
- }
-
- // UI
-
- // TODO:
- // should make animations, should be configurable by developer
- // first level is style function; second level is render function
- Entry.prototype.render = function() {
- try {
- document.getElementById(this.viewId).innerHTML = this._styler.call(this, JSON.stringify(this.data));
- } catch (e) {
- // serious error
- }
- }
-
- // Events
-
- Entry.prototype.on = function(type, callback) {
- switch (name) {
- case 'delete':
- type = DELETE
- break;
- case 'update':
- type = UPDATE
- break;
- case 'hide':
- type = HIDE
- break;
- case 'show':
- type = SHOW
- break;
- default:
- return false;
- break;
- }
- if (this._handlers[type] == undefined) {
- this._handlers[type] = []
- }
- this._handlers[type].push(callback);
- return true;
- }
-
- Entry.prototype.onData = function(entryEvent) {
- switch (entryEvent.type) {
- case DELETE:
- this.onDelete(entryEvent.ts)
- break;
- case UPDATE:
- this.onUpdate(entryEvent.ts, entryEvent.content)
- break;
- case HIDE:
- this.onHide(entryEvent.ts)
- break;
- case SHOW:
- this.onShow(entryEvent.ts)
- break;
- }
- }
-
- // Management
-
- Entry.prototype.update = function(timestamp, data) {
- this.data = data;
- this.render();
- }
-
- Entry.prototype.delete = function() {
- this.unbindMessages();
- document.getElementById(this.viewId).remove();
- }
-
- Entry.prototype.hide = function() {
- }
-
- Entry.prototype.show = function() {
- }
-
- // API
-
- Entry.prototype.apiEntryUpdate = function(data) {
- }
-
- Entry.prototype.apiMetricSave = function(data) {
- }
-
- // Events callbacks
-
- Entry.prototype.onUpdate = function(timestamp, data) {
- this.update(timestamp, data);
-
- for (var i in this._handlers[UPDATE]) {
- this._handlers[UPDATE][i].call(this, timestamp, data);
- }
- }
-
- Entry.prototype.onDelete = function(timestamp) {
- for (var i in this._handlers[DELETE]) {
- this._handlers[DELETE][i].call(this, timestamp);
- }
- }
-
- Entry.prototype.onHide = function(timestamp) {
- for (var i in this._handlers[HIDE]) {
- this._handlers[HIDE][i].call(this, timestamp);
- }
- }
-
- Entry.prototype.onShow = function(timestamp) {
- for (var i in this._handlers[SHOW]) {
- this._handlers[SHOW][i].call(this, timestamp);
- }
- }
-
- // Handlers
-
- Entry.prototype.bindMessages = function() {
- var self = this;
- this.__bindFeed = this._feed.on('entry-message', function(ts, entryEvent) {
- if (entryEvent.id == self.id || entryEvent.id == '*') {
- self.onData(entryEvent);
- }
- });
- }
-
- Entry.prototype.unbindMessages = function() {
- this._feed.off(this.__bindFeed);
- }
-
- // Getters
-
- Entry.prototype.getTimestamp = function() {
- return this.ts;
- }
-
- // Helpers
-
- var _uniqueId = function() {
- return '_' + Math.random().toString(36).substr(2, 36);
- }
-
- return Entry;
-
-})();
diff --git a/service/stream/static/lib/event.js b/service/stream/static/lib/event.js
deleted file mode 100644
index 5706608..0000000
--- a/service/stream/static/lib/event.js
+++ /dev/null
@@ -1,48 +0,0 @@
-var Event = (function() {
-
- function Event(event) {
-
- /** @type {String} */
- this.id = event.Id || null;
-
- /** @type {Integer} */
- this.ts = event.Timestamp;
-
- /** @type {Integer} */
- this.actionGroup = null
-
- /** @type {Integer} */
- this.actionType = null
-
- /** @type {String} */
- this.user = event.User
-
- /** @type {String} */
- this.type = event.Type
-
- /** @type {String} */
- this.contentType = 'string'
-
- /** @type {String} */
- try {
- this.content = JSON.parse(event.Content)
- this.contentType = 'json'
- } catch (e) {
- this.content = event.Content
- }
- }
-
- Event.prototype.GetTimestamp = function() {
- return this.ts;
- }
-
- Event.prototype.PrintContent = function() {
- if (this.contentType == 'string') {
- return this.content
- }
- return JSON.stringify(this.content)
- }
-
- return Event;
-
-})();
diff --git a/service/stream/static/lib/feed.js b/service/stream/static/lib/feed.js
deleted file mode 100644
index 626fb6f..0000000
--- a/service/stream/static/lib/feed.js
+++ /dev/null
@@ -1,346 +0,0 @@
-var Feed = (function() {
-
- const SYSTEM_FEED_MESSAGE = 1
-
- const RELOAD = 1
- const EMPTY = 2
- const ENTRY_NEW = 3
- const ENTRY_INIT = 4
- const ENTRY_MORE = 5
- const HIDE = 6
- const SHOW = 7
- const ENTRY_MESSAGE = 8
-
- const AUTHENTICATED = 100
- const AUTHENTICATION_REQUIRED = 101
- const AUTHENTICATION_FAILED = 102
- const LOGGED_OUT = 103
-
- /** @type {Feed} */
- var localCache = {}
-
- /** @type {Object} */
- var globalOptions = {
- feedId: '',
- outputContainerId: 'defaultContainerId',
- defaultElementLayout: '',
- defaultElementCount: 0
- };
-
- /** @type {Object} */
- var globalCredential = {
- username: null,
- token: null,
- method: 'basic'
- };
-
- function Feed(id, options, channel) {
-
- /** @type {String} */
- this.id = id;
-
- /** @type {Channel} */
- this.channel = channel;
-
- /** @type {Array} */
- this.entryList = [];
-
- /** @type {Object} */
- if (this.channel.options.transport == 'ws') {
- if (this.channel._socket == undefined) {
- this.socket = this.channel.getWebSocketConnection();
- } else {
- this.socket = this.channel._socket;
- }
- } else if (this.channel.options.transport == 'lp') {
- this.socket = this.channel.getLongPoolingConnection();
- }
-
- /** @type {Object} */
- this.options = _extend(globalOptions, options);
-
- /** @type {Function} */
- this.stylerFunction = options.stylerFunction || this._stylerFunction;
-
- /** @type {Function} */
- this.renderFunction = options.renderFunction || this._renderFunction;
-
- /** @type {DOM} */
- this.outputContainer = document.getElementById(this.options.outputContainerId);
-
- this.bindChannel(this.channel);
-
- /** @type {Object} */
- this._handlers = {};
- }
-
- Feed.prototype.on = function(name, callback) {
- switch (name) {
- case 'reload':
- type = RELOAD
- break;
- case 'empty':
- type = EMPTY
- break;
- case 'entry':
- type = ENTRY_NEW
- break;
- case 'entry-init':
- type = ENTRY_INIT
- break;
- case 'entry-more':
- type = ENTRY_MORE
- break;
- case 'hide':
- type = HIDE
- break;
- case 'show':
- type = SHOW
- break;
- case 'entry-message':
- type = ENTRY_MESSAGE
- break;
- case 'authenticated':
- type = AUTHENTICATED
- break;
- case 'authentication-required':
- type = AUTHENTICATION_REQUIRED
- break;
- case 'authentication-failed':
- type = AUTHENTICATION_FAILED
- break;
- case 'logout':
- type = LOGGED_OUT
- break;
- default:
- break;
- }
- if (this._handlers[type] == undefined) {
- this._handlers[type] = []
- }
- this._handlers[type].push(callback);
-
- return callback;
- }
-
- Feed.prototype.off = function(callback) {
- for (var i in this._handlers) {
- for (var x in this._handlers[i]) {
- if (this._handlers[i][x] == callback) {
- delete this._handlers[i][x];
- return;
- }
- }
- }
- }
-
- Feed.prototype.onData = function(feedEvent) {
- switch (feedEvent.type) {
- case RELOAD:
- this.onReload(feedEvent.ts)
- break;
- case EMPTY:
- this.onEmpty(feedEvent.ts)
- break;
- case ENTRY_NEW:
- this.onEntryNew(feedEvent.ts, feedEvent.content)
- break;
- case ENTRY_INIT:
- this.onEntryInit(feedEvent.ts, feedEvent.content)
- break;
- case ENTRY_MORE:
- this.onEntryMore(feedEvent.ts, feedEvent.content)
- break;
- case HIDE:
- this.onHide(feedEvent.ts)
- break;
- case SHOW:
- this.onShow(feedEvent.ts)
- break;
- case ENTRY_MESSAGE:
- this.onEntryMessage(feedEvent.ts, feedEvent.content)
- break;
- case AUTHENTICATED:
- this.onAuthenticated(feedEvent.ts, feedEvent.content)
- break;
- case AUTHENTICATION_REQUIRED:
- this.onAuthenticationRequired(feedEvent.ts, feedEvent.content)
- break;
- case AUTHENTICATION_FAILED:
- this.onAuthenticationFailed(feedEvent.ts, feedEvent.content)
- break;
- case LOGGED_OUT:
- this.onLogout(feedEvent.ts, feedEvent.content)
- break;
- }
- }
-
- // Events callbacks
-
- Feed.prototype.onReload = function(timestamp) {
- for (var i in this._handlers[RELOAD]) {
- this._handlers[RELOAD][i].call(this, timestamp);
- }
- }
-
- Feed.prototype.onEmpty = function(timestamp) {
- for (var i in this._handlers[EMPTY]) {
- this._handlers[EMPTY][i].call(this, timestamp);
- }
- }
-
- Feed.prototype.onEntryNew = function(timestamp, data) {
- entry = new Entry(data, {styler: this.stylerFunction});
-
- this.addEntry(entry)
-
- for (var i in this._handlers[ENTRY_NEW]) {
- this._handlers[ENTRY_NEW][i].call(this, timestamp, entry);
- }
- }
-
- Feed.prototype.onEntryInit = function(timestamp, data) {
- entries = JSON.parse(data);
-
- for (var i in this._handlers[ENTRY_INIT]) {
- this._handlers[ENTRY_INIT][i].call(this, timestamp, entries);
- }
- }
-
- Feed.prototype.onEntryMore = function(timestamp, data) {
- entries = JSON.parse(data);
-
- for (var i in this._handlers[ENTRY_MORE]) {
- this._handlers[ENTRY_MORE][i].call(this, timestamp, entries);
- }
- }
-
- Feed.prototype.onHide = function(timestamp) {
- for (var i in this._handlers[HIDE]) {
- this._handlers[HIDE][i].call(this, timestamp);
- }
- }
-
- Feed.prototype.onShow = function(timestamp) {
- for (var i in this._handlers[SHOW]) {
- this._handlers[SHOW][i].call(this, timestamp);
- }
- }
-
- Feed.prototype.onEntryMessage = function(timestamp, content) {
- entryEvent = new Event(content);
-
- for (var i in this._handlers[ENTRY_MESSAGE]) {
- this._handlers[ENTRY_MESSAGE][i].call(this, timestamp, entryEvent);
- }
- }
-
- Feed.prototype.onAuthenticated = function(timestamp, content) {
- for (var i in this._handlers[AUTHENTICATED]) {
- this._handlers[AUTHENTICATED][i].call(this, timestamp);
- }
- }
-
- Feed.prototype.onAuthenticationRequired = function(timestamp, content) {
- for (var i in this._handlers[AUTHENTICATION_REQUIRED]) {
- this._handlers[AUTHENTICATION_REQUIRED][i].call(this, timestamp);
- }
- }
-
- Feed.prototype.onAuthenticationFailed = function(timestamp, content) {
- for (var i in this._handlers[AUTHENTICATION_FAILED]) {
- this._handlers[AUTHENTICATION_FAILED][i].call(this, timestamp);
- }
- }
-
- Feed.prototype.onLogout = function(timestamp, content) {
- for (var i in this._handlers[LOGGED_OUT]) {
- this._handlers[LOGGED_OUT][i].call(this, timestamp);
- }
- }
-
- // Entries management
-
- Feed.prototype.addEntry = function(entry) {
-
- // types
- // add by: timestamp up/down; always to top; always to bottom
-
- entry.setParent(this);
- this.entryList.push(entry);
-
- this.outputContainer.innerHTML = '' + this.outputContainer.innerHTML;
-
- entry.render();
- }
-
- Feed.prototype.deleteEntry = function(entry) {
- entry.delete();
- }
-
- Feed.prototype.updateEntry = function(entry, data) {
- }
-
- Feed.prototype.empty = function() {
- for (var i in this.entryList) {
- this.deleteEntry(this.entryList[i]);
- delete this.entryList[i];
- }
- this.entryList = []
- }
-
- Feed.prototype.findEntry = function(id) {
- }
-
- // UI
-
- Feed.prototype.render = function(id) {
- for (var i in this.entryList) {
- this.entryList[i].render();
- }
- }
-
- // Handlers
-
- Feed.prototype.bindChannel = function(channel) {
- var self = this;
- channel.on('message', function(chid, ts, systemEvent) {
- if (systemEvent.type == SYSTEM_FEED_MESSAGE) {
- feedEvent = new Event(systemEvent.content);
- if (feedEvent.user == self.id || feedEvent.user == '*') {
- self.onData(feedEvent);
- }
- }
- });
- }
-
- // Stylers
-
- Feed.prototype._stylerFunction = function(data) {
- return JSON.stringify(data);
- }
-
- Feed.prototype._renderFunction = function(data) {
- return JSON.stringify(data);
- }
-
- // Helpers
-
- var _extend = function(a, b) {
- var c = {}, prop;
- for (prop in a) {
- if (a.hasOwnProperty(prop)) {
- c[prop] = a[prop];
- }
- }
- for (prop in b) {
- if (b.hasOwnProperty(prop)) {
- c[prop] = b[prop];
- }
- }
- return c;
- }
-
- return Feed;
-
-})();
diff --git a/service/stream/static/lp.html b/service/stream/static/lp.html
deleted file mode 100644
index 85eca45..0000000
--- a/service/stream/static/lp.html
+++ /dev/null
@@ -1,32 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/service/stream/static/ws.html b/service/stream/static/ws.html
deleted file mode 100644
index 032a291..0000000
--- a/service/stream/static/ws.html
+++ /dev/null
@@ -1,65 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/service/stream/stream.go b/service/stream/stream.go
index 948887d..56469c0 100644
--- a/service/stream/stream.go
+++ b/service/stream/stream.go
@@ -1,8 +1,17 @@
package stream
import (
- _ "github.com/feedlabs/elasticfeed/service/stream/router"
- _ "github.com/feedlabs/elasticfeed/service/stream/controller"
+ "github.com/feedlabs/elasticfeed/service/stream/router"
+ "github.com/feedlabs/elasticfeed/service/stream/controller"
)
-func init() {}
+type StreamService struct {}
+
+func (this *StreamService) Init() {
+ router.InitRouters()
+ controller.InitRooms()
+}
+
+func NewMetricService() *StreamService {
+ return &StreamService{}
+}
diff --git a/service/system/router/status.go b/service/system/router/status.go
index 79a01a3..c6ba3cf 100644
--- a/service/system/router/status.go
+++ b/service/system/router/status.go
@@ -5,6 +5,6 @@ import (
"github.com/feedlabs/elasticfeed/service/system/controller"
)
-func init() {
+func InitRouters() {
feedify.Router("/system/status", &controller.StatusController{}, "get:Get")
}
diff --git a/service/system/system.go b/service/system/system.go
index f4352a0..bb8dca2 100644
--- a/service/system/system.go
+++ b/service/system/system.go
@@ -1,8 +1,15 @@
package system
import (
- _ "github.com/feedlabs/elasticfeed/service/system/controller"
- _ "github.com/feedlabs/elasticfeed/service/system/router"
+ "github.com/feedlabs/elasticfeed/service/system/router"
)
-func init() {}
+type SystemService struct {}
+
+func (this *SystemService) Init() {
+ router.InitRouters()
+}
+
+func NewMetricService() *SystemService {
+ return &SystemService{}
+}
diff --git a/website/.buildpacks b/website/.buildpacks
new file mode 100644
index 0000000..f85b304
--- /dev/null
+++ b/website/.buildpacks
@@ -0,0 +1,2 @@
+https://github.com/heroku/heroku-buildpack-ruby.git
+https://github.com/hashicorp/heroku-buildpack-middleman.git
diff --git a/website/Gemfile b/website/Gemfile
new file mode 100644
index 0000000..8fc173c
--- /dev/null
+++ b/website/Gemfile
@@ -0,0 +1,5 @@
+source 'https://rubygems.org'
+
+ruby "2.0.0"
+
+gem 'middleman-hashicorp', github: 'hashicorp/middleman-hashicorp'
diff --git a/website/Gemfile.lock b/website/Gemfile.lock
new file mode 100644
index 0000000..170034e
--- /dev/null
+++ b/website/Gemfile.lock
@@ -0,0 +1,173 @@
+GIT
+ remote: git://github.com/hashicorp/middleman-hashicorp.git
+ revision: b82c2c2fdc244cd0bd529ff27cfab24e43f07708
+ specs:
+ middleman-hashicorp (0.1.0)
+ bootstrap-sass (~> 3.2)
+ builder (~> 3.2)
+ less (~> 2.6)
+ middleman (~> 3.3)
+ middleman-livereload (~> 3.3)
+ middleman-minify-html (~> 3.4)
+ middleman-syntax (~> 2.0)
+ rack-contrib (~> 1.1)
+ rack-rewrite (~> 1.5)
+ rack-ssl-enforcer (~> 0.2)
+ redcarpet (~> 3.1)
+ therubyracer (~> 0.12)
+ thin (~> 1.6)
+
+GEM
+ remote: https://rubygems.org/
+ specs:
+ activesupport (4.1.6)
+ i18n (~> 0.6, >= 0.6.9)
+ json (~> 1.7, >= 1.7.7)
+ minitest (~> 5.1)
+ thread_safe (~> 0.1)
+ tzinfo (~> 1.1)
+ bootstrap-sass (3.2.0.2)
+ sass (~> 3.2)
+ builder (3.2.2)
+ celluloid (0.16.0)
+ timers (~> 4.0.0)
+ chunky_png (1.3.3)
+ coffee-script (2.3.0)
+ coffee-script-source
+ execjs
+ coffee-script-source (1.8.0)
+ commonjs (0.2.7)
+ compass (1.0.1)
+ chunky_png (~> 1.2)
+ compass-core (~> 1.0.1)
+ compass-import-once (~> 1.0.5)
+ rb-fsevent (>= 0.9.3)
+ rb-inotify (>= 0.9)
+ sass (>= 3.3.13, < 3.5)
+ compass-core (1.0.1)
+ multi_json (~> 1.0)
+ sass (>= 3.3.0, < 3.5)
+ compass-import-once (1.0.5)
+ sass (>= 3.2, < 3.5)
+ daemons (1.1.9)
+ em-websocket (0.5.1)
+ eventmachine (>= 0.12.9)
+ http_parser.rb (~> 0.6.0)
+ erubis (2.7.0)
+ eventmachine (1.0.3)
+ execjs (2.2.2)
+ ffi (1.9.6)
+ haml (4.0.5)
+ tilt
+ hike (1.2.3)
+ hitimes (1.2.2)
+ hooks (0.4.0)
+ uber (~> 0.0.4)
+ htmlcompressor (0.1.2)
+ http_parser.rb (0.6.0)
+ i18n (0.6.11)
+ json (1.8.1)
+ kramdown (1.5.0)
+ less (2.6.0)
+ commonjs (~> 0.2.7)
+ libv8 (3.16.14.7)
+ listen (2.7.11)
+ celluloid (>= 0.15.2)
+ rb-fsevent (>= 0.9.3)
+ rb-inotify (>= 0.9)
+ middleman (3.3.6)
+ coffee-script (~> 2.2)
+ compass (>= 1.0.0, < 2.0.0)
+ compass-import-once (= 1.0.5)
+ execjs (~> 2.0)
+ haml (>= 4.0.5)
+ kramdown (~> 1.2)
+ middleman-core (= 3.3.6)
+ middleman-sprockets (>= 3.1.2)
+ sass (>= 3.4.0, < 4.0)
+ uglifier (~> 2.5)
+ middleman-core (3.3.6)
+ activesupport (~> 4.1.0)
+ bundler (~> 1.1)
+ erubis
+ hooks (~> 0.3)
+ i18n (~> 0.6.9)
+ listen (>= 2.7.9, < 3.0)
+ padrino-helpers (~> 0.12.3)
+ rack (>= 1.4.5, < 2.0)
+ rack-test (~> 0.6.2)
+ thor (>= 0.15.2, < 2.0)
+ tilt (~> 1.4.1, < 2.0)
+ middleman-livereload (3.3.4)
+ em-websocket (~> 0.5.1)
+ middleman-core (~> 3.2)
+ rack-livereload (~> 0.3.15)
+ middleman-minify-html (3.4.0)
+ htmlcompressor (~> 0.1.0)
+ middleman-core (>= 3.2)
+ middleman-sprockets (3.3.10)
+ middleman-core (~> 3.3)
+ sprockets (~> 2.12.1)
+ sprockets-helpers (~> 1.1.0)
+ sprockets-sass (~> 1.2.0)
+ middleman-syntax (2.0.0)
+ middleman-core (~> 3.2)
+ rouge (~> 1.0)
+ minitest (5.4.2)
+ multi_json (1.10.1)
+ padrino-helpers (0.12.4)
+ i18n (~> 0.6, >= 0.6.7)
+ padrino-support (= 0.12.4)
+ tilt (~> 1.4.1)
+ padrino-support (0.12.4)
+ activesupport (>= 3.1)
+ rack (1.5.2)
+ rack-contrib (1.1.0)
+ rack (>= 0.9.1)
+ rack-livereload (0.3.15)
+ rack
+ rack-rewrite (1.5.0)
+ rack-ssl-enforcer (0.2.8)
+ rack-test (0.6.2)
+ rack (>= 1.0)
+ rb-fsevent (0.9.4)
+ rb-inotify (0.9.5)
+ ffi (>= 0.5.0)
+ redcarpet (3.2.0)
+ ref (1.0.5)
+ rouge (1.7.2)
+ sass (3.4.6)
+ sprockets (2.12.2)
+ hike (~> 1.2)
+ multi_json (~> 1.0)
+ rack (~> 1.0)
+ tilt (~> 1.1, != 1.3.0)
+ sprockets-helpers (1.1.0)
+ sprockets (~> 2.0)
+ sprockets-sass (1.2.0)
+ sprockets (~> 2.0)
+ tilt (~> 1.1)
+ therubyracer (0.12.1)
+ libv8 (~> 3.16.14.0)
+ ref
+ thin (1.6.3)
+ daemons (~> 1.0, >= 1.0.9)
+ eventmachine (~> 1.0)
+ rack (~> 1.0)
+ thor (0.19.1)
+ thread_safe (0.3.4)
+ tilt (1.4.1)
+ timers (4.0.1)
+ hitimes
+ tzinfo (1.2.2)
+ thread_safe (~> 0.1)
+ uber (0.0.10)
+ uglifier (2.5.3)
+ execjs (>= 0.3.0)
+ json (>= 1.8.0)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ middleman-hashicorp!
diff --git a/website/Procfile b/website/Procfile
new file mode 100644
index 0000000..58361e4
--- /dev/null
+++ b/website/Procfile
@@ -0,0 +1 @@
+web: bundle exec thin start -p $PORT
diff --git a/website/README.md b/website/README.md
new file mode 100644
index 0000000..dd986e4
--- /dev/null
+++ b/website/README.md
@@ -0,0 +1,24 @@
+# Elasticfeed Website
+
+This subdirectory contains the entire source for the [Elasticfeed website](http://www.elasticfeed.io).
+This is a [Middleman](http://middlemanapp.com) project, which builds a static
+site from these source files.
+
+## Contributions Welcome!
+
+If you find a typo or you feel like you can improve the HTML, CSS, or
+JavaScript, we welcome contributions. Feel free to open issues or pull
+requests like any normal GitHub project, and we'll merge it in.
+
+## Running the Site Locally
+
+Running the site locally is simple. Clone this repo and run the following
+commands:
+
+```
+$ bundle
+$ ELASTICFEED_DISABLE_DOWNLOAD_FETCH=true ELASTICFEED_VERSION=1.0 bundle exec middleman server
+```
+
+Then open up `localhost:4567`. Note that some URLs you may need to append
+".html" to make them work (in the navigation and such).
diff --git a/website/Vagrantfile b/website/Vagrantfile
new file mode 100644
index 0000000..b8da4b2
--- /dev/null
+++ b/website/Vagrantfile
@@ -0,0 +1,25 @@
+# -*- mode: ruby -*-
+# vi: set ft=ruby :
+
+$script = <
+
+
+
+
+
+ <%= yield %>
+
+
+
+
+
+
+
+