From e8abb11c19acc29209b89f62a271a7b4f68d9a26 Mon Sep 17 00:00:00 2001 From: Brad Rydzewski Date: Sun, 22 Jun 2014 02:04:07 -0700 Subject: [PATCH] got websockets working. added stubs for html5 notifications api --- .drone.yml | 2 ++ server/app/index.html | 4 ++- server/app/scripts/app.js | 43 ++++++++++---------------- server/app/scripts/controllers/home.js | 8 ++--- server/app/scripts/controllers/user.js | 9 +++++- server/app/scripts/services/feed.js | 24 ++++++++++++++ server/app/scripts/services/notify.js | 23 ++++++++++++++ server/app/scripts/services/stdout.js | 27 ++++++++++++++++ server/app/scripts/services/ws.js | 18 ----------- server/app/views/account.html | 4 +++ server/database/perm.go | 18 +++++++++++ server/handler/ws.go | 13 ++++---- server/pubsub/opts.go | 5 +++ server/worker/request.go | 6 ++-- server/worker/worker.go | 4 +-- 15 files changed, 145 insertions(+), 63 deletions(-) create mode 100644 server/app/scripts/services/feed.js create mode 100644 server/app/scripts/services/notify.js create mode 100644 server/app/scripts/services/stdout.js delete mode 100644 server/app/scripts/services/ws.js diff --git a/.drone.yml b/.drone.yml index 946e7b9c5..c6804280c 100644 --- a/.drone.yml +++ b/.drone.yml @@ -6,6 +6,8 @@ env: - PATH=$PATH:$GOROOT/bin:$GOPATH/bin script: - sudo apt-get -y install libsqlite3-dev sqlite3 1> /dev/null 2> /dev/null + - sudo npm install -g uglify-js + - sudo npm install -g less - make deps - make build - make test diff --git a/server/app/index.html b/server/app/index.html index b75f883e2..0056d8f4a 100644 --- a/server/app/index.html +++ b/server/app/index.html @@ -37,7 +37,9 @@ - + + + \ No newline at end of file diff --git a/server/app/scripts/app.js b/server/app/scripts/app.js index 7fd7bdbc7..1007bf6d8 100644 --- a/server/app/scripts/app.js +++ b/server/app/scripts/app.js @@ -280,14 +280,6 @@ app.controller("RepoController", function($scope, $http, $routeParams, user, rep }); }; - $scope.options={ - barColor:"#40C598", - trackColor:'#EEEEEE', - scaleColor:false, - lineWidth:10, - lineCap:'butt', - size:130 - }; }); app.controller("BranchController", function($scope, $http, $routeParams, user) { @@ -327,15 +319,21 @@ app.controller("BranchController", function($scope, $http, $routeParams, user) { }); }); -app.controller("CommitController", function($scope, $http, $routeParams, user) { +app.controller("CommitController", function($scope, $http, $routeParams, stdout, feed) { - $scope.user = user; var remote = $routeParams.remote; var owner = $routeParams.owner; var name = $routeParams.name; var branch = $routeParams.branch; var commit = $routeParams.commit; + feed.subscribe(function(event) { + if (event.commit.sha == commit + && event.commit.branch == branch) { + $scope.commit = event.commit; + } + }); + // load the repo meta-data $http({method: 'GET', url: '/v1/repos/'+remote+'/'+owner+"/"+name}). success(function(data, status, headers, config) { @@ -351,6 +349,14 @@ app.controller("CommitController", function($scope, $http, $routeParams, user) { $scope.commit = data; $scope.coverage=45; $scope.passing=100; + + if (data.status!='Started' && data.status!='Pending') { + return; + } + + stdout.subscribe(data.id, function(out){ + console.log(out); + }); }). error(function(data, status, headers, config) { console.log(data); @@ -375,21 +381,6 @@ app.controller("CommitController", function($scope, $http, $routeParams, user) { }); - $scope.options={ - barColor:"#40C598", - trackColor:'#EEEEEE', - scaleColor:false, - lineWidth:10, - lineCap:'butt', - size:130 - }; -}); -function barColor(percent) { - switch(true) { - case percent > 80: return "#40C598"; - case percent < 50: return "rgba(189, 54, 47, 0.8)"; - default: return "#f0ad4e"; - } -} \ No newline at end of file +}); \ No newline at end of file diff --git a/server/app/scripts/controllers/home.js b/server/app/scripts/controllers/home.js index 81d1edadb..e7fbae57e 100644 --- a/server/app/scripts/controllers/home.js +++ b/server/app/scripts/controllers/home.js @@ -1,11 +1,9 @@ 'use strict'; -angular.module('app').controller("HomeController", function($scope, $http, user, websocket) { +angular.module('app').controller("HomeController", function($scope, $http, feed, notify) { - $scope.user = user; - - websocket.subscribeRepos(function(repos) { - console.log(repos); + feed.subscribe(function(message) { + notify.send(message.repo.name); }); $http({method: 'GET', url: '/v1/user/feed'}). diff --git a/server/app/scripts/controllers/user.js b/server/app/scripts/controllers/user.js index 72d765252..66c6a87bf 100644 --- a/server/app/scripts/controllers/user.js +++ b/server/app/scripts/controllers/user.js @@ -1,6 +1,6 @@ 'use strict'; -angular.module('app').controller("UserController", function($scope, $http, user) { +angular.module('app').controller("UserController", function($scope, $http, user, notify) { $scope.user = user; @@ -17,6 +17,10 @@ angular.module('app').controller("UserController", function($scope, $http, user) console.log(data); }); + $scope.notifications = {} + $scope.notifications.supported = notify.supported(); + $scope.notifications.granted = notify.granted(); + $scope.save = function() { // request to create a new repository $http({method: 'PUT', url: '/v1/user', data: $scope.userTemp }). @@ -35,4 +39,7 @@ angular.module('app').controller("UserController", function($scope, $http, user) name : $scope.user.name }; }; + $scope.enableNotifications = function() { + notify.requestPermission(); + }; }); \ No newline at end of file diff --git a/server/app/scripts/services/feed.js b/server/app/scripts/services/feed.js new file mode 100644 index 000000000..cee9da0a4 --- /dev/null +++ b/server/app/scripts/services/feed.js @@ -0,0 +1,24 @@ +'use strict'; + +angular.module('app').service('feed', ['$http', '$window', function($http, $window) { + + var proto = ($window.location.protocol == 'https:' ? 'wss' : 'ws'); + var route = [proto, "://", $window.location.host, '/ws/user'].join(''); + + var wsCallback = undefined; + var ws = new WebSocket(route); + ws.onmessage = function(event) { + var data = angular.fromJson(event.data); + if (wsCallback != undefined) { + wsCallback(data); + } + }; + + this.subscribe = function(callback) { + wsCallback = callback; + }; + + this.unsubscribe = function() { + ws.close(); + }; +}]); diff --git a/server/app/scripts/services/notify.js b/server/app/scripts/services/notify.js new file mode 100644 index 000000000..4983786b0 --- /dev/null +++ b/server/app/scripts/services/notify.js @@ -0,0 +1,23 @@ +'use strict'; + +angular.module('app').service('notify', ['$window', '$timeout', function($window, $timeout) { + + this.supported = function() { + return ("Notification" in $window) + } + + this.granted = function() { + return ("Notification" in $window) && Notification.permission === "granted"; + } + + this.requestPermission = function() { + Notification.requestPermission(); + } + + this.send = function(title, opts) { + if ("Notification" in $window) { + var n = new Notification(title, opts); + $timeout(function() { n.close(); }, 10000); + } + }; +}]); diff --git a/server/app/scripts/services/stdout.js b/server/app/scripts/services/stdout.js new file mode 100644 index 000000000..75f827c83 --- /dev/null +++ b/server/app/scripts/services/stdout.js @@ -0,0 +1,27 @@ +'use strict'; + +angular.module('app').service('stdout', ['$window', function($window) { + var callback = undefined; + var websocket = undefined; + + this.subscribe = function(path, _callback) { + callback = _callback; + + var proto = ($window.location.protocol == 'https:' ? 'wss' : 'ws'); + var route = [proto, "://", $window.location.host, '/ws/stdout/', path].join(''); + + websocket = new WebSocket(route); + websocket.onmessage = function(event) { + if (callback != undefined) { + callback(event.data); + } + }; + }; + + this.unsubscribe = function() { + callback = undefined; + if (webscoket != undefined) { + websocket.close(); + } + }; +}]); diff --git a/server/app/scripts/services/ws.js b/server/app/scripts/services/ws.js deleted file mode 100644 index a6e715de6..000000000 --- a/server/app/scripts/services/ws.js +++ /dev/null @@ -1,18 +0,0 @@ -'use strict'; - -angular.module('app').service('websocket', function($q, $http, $window) { - var wsCallback = undefined; - var ws = new WebSocket('ws://localhost:8080/ws/user'); - ws.onmessage = function(event) { - var data = angular.fromJson(event.data); - if (wsCallback != undefined) { - wsCallback(data); - } - }; - return { - subscribeRepos: function(callback) { - wsCallback = callback; - } - }; -}); - diff --git a/server/app/views/account.html b/server/app/views/account.html index f2c9f1bc0..61312f220 100644 --- a/server/app/views/account.html +++ b/server/app/views/account.html @@ -54,6 +54,10 @@ {{failure}} +
+ +
+
diff --git a/server/database/perm.go b/server/database/perm.go index d10fe339e..b1c5c9e57 100644 --- a/server/database/perm.go +++ b/server/database/perm.go @@ -27,6 +27,10 @@ type PermManager interface { // Admin returns true if the specified user is an // administrator of the repository. Admin(u *model.User, r *model.Repo) (bool, error) + + // Member returns true if the specified user is a + // collaborator on the repository. + Member(u *model.User, r *model.Repo) (bool, error) } // permManager manages user permissions to access repositories. @@ -147,6 +151,20 @@ func (db *permManager) Admin(u *model.User, r *model.Repo) (bool, error) { return perm.Admin, err } +func (db *permManager) Member(u *model.User, r *model.Repo) (bool, error) { + switch { + // if the user is nil, deny access + case u == nil: + return false, nil + case u.ID == r.UserID: + return true, nil + } + + // get the permissions from the database + perm, err := db.find(u, r) + return perm.Read, err +} + func (db *permManager) find(u *model.User, r *model.Repo) (*perm, error) { var dst = perm{} var err = meddler.QueryRow(db, &dst, findPermQuery, u.ID, r.ID) diff --git a/server/handler/ws.go b/server/handler/ws.go index 3c018ae23..7002dd8cd 100644 --- a/server/handler/ws.go +++ b/server/handler/ws.go @@ -2,6 +2,7 @@ package handler import ( "net/http" + "strconv" "time" "github.com/drone/drone/server/database" @@ -77,7 +78,7 @@ func (h *WsHandler) WsUser(w http.ResponseWriter, r *http.Request) error { // user must have read access to the repository // in order to pass this message along - if ok, _ := h.perms.Read(user, work.Repo); !ok { + if ok, _ := h.perms.Member(user, work.Repo); !ok { break } @@ -109,15 +110,13 @@ func (h *WsHandler) WsUser(w http.ResponseWriter, r *http.Request) error { // WsConsole will upgrade the connection to a Websocket and will stream // the build output to the browser. func (h *WsHandler) WsConsole(w http.ResponseWriter, r *http.Request) error { - var host, owner, name = parseRepo(r) - var branch = r.FormValue(":branch") - var sha = r.FormValue(":commit") + var commitID, _ = strconv.Atoi(r.FormValue(":id")) - repo, err := h.repos.FindName(host, owner, name) + commit, err := h.commits.Find(int64(commitID)) if err != nil { return notFound{err} } - commit, err := h.commits.FindSha(repo.ID, branch, sha) + repo, err := h.repos.Find(commit.RepoID) if err != nil { return notFound{err} } @@ -212,5 +211,5 @@ func (h *WsHandler) Ping(w http.ResponseWriter, r *http.Request) error { func (h *WsHandler) Register(r *pat.Router) { r.Post("/ws/ping", errorHandler(h.Ping)) r.Get("/ws/user", errorHandler(h.WsUser)) - r.Get("/ws/{host}/{owner}/{name}/branches/{branch}/commits/{commit}", errorHandler(h.WsConsole)) + r.Get("/ws/stdout/{id}", errorHandler(h.WsConsole)) } diff --git a/server/pubsub/opts.go b/server/pubsub/opts.go index 19499f910..02e2b4818 100644 --- a/server/pubsub/opts.go +++ b/server/pubsub/opts.go @@ -19,3 +19,8 @@ var DefaultOpts = &Opts{ Timeout: 0, Record: false, } + +var ConsoleOpts = &Opts{ + Timeout: time.Minute * 60, + Record: true, +} diff --git a/server/worker/request.go b/server/worker/request.go index e322e302a..b9eb44305 100644 --- a/server/worker/request.go +++ b/server/worker/request.go @@ -5,8 +5,8 @@ import ( ) type Request struct { - User *model.User - Repo *model.Repo - Commit *model.Commit + User *model.User `json:"-"` + Repo *model.Repo `json:"repo"` + Commit *model.Commit `json:"commit"` server *model.Server } diff --git a/server/worker/worker.go b/server/worker/worker.go index adadc4ef9..84dfd5f61 100644 --- a/server/worker/worker.go +++ b/server/worker/worker.go @@ -84,9 +84,9 @@ func (w *worker) Execute(r *Request) { w.commits.Update(r.Commit) // notify all listeners that the build is started - commitc := w.pubsub.Register("_") + commitc := w.pubsub.Register("_global") commitc.Publish(r) - stdoutc := w.pubsub.Register(r.Commit.ID) + stdoutc := w.pubsub.RegisterOpts(r.Commit.ID, pubsub.ConsoleOpts) defer stdoutc.Close() // create a special buffer that will also