/public/javascripts/main.js

https://github.com/nodester/admin-panel · JavaScript · 500 lines · 417 code · 68 blank · 15 comment · 16 complexity · a9700fe8006cc4a0fc6e200cdc21bb3e MD5 · raw file

  1. (function() {
  2. // Set underscore templates to mustache style
  3. _.templateSettings = {
  4. interpolate : /\{\{(.+?)\}\}/g
  5. };
  6. var setObject = function(key, value) {
  7. localStorage.setItem(key, JSON.stringify(value));
  8. },
  9. getObject = function(key) {
  10. return JSON.parse(localStorage.getItem(key));
  11. },
  12. getAppNames = function(){
  13. var names= [];
  14. var apps = getObject('apps');
  15. for (var i=0; i < apps.length; i++) {
  16. names.push(apps[i].name);
  17. };
  18. return names;
  19. },
  20. hasDomain = function(appname){
  21. var apps = getAppNames();
  22. var results = _.filter(apps, function(app){ return app === appname; });
  23. console.log(results);
  24. return results.length > 0;
  25. },
  26. flashMessage = function(message, type){
  27. $('.message-container')
  28. .addClass('alert alert-success alert-block')
  29. .html('<a class="close" data-dismiss="alert">x</a>' + message)
  30. .show();
  31. setTimeout(function(){
  32. $('.message-container').hide(400);
  33. }, 2000);
  34. };
  35. // Global object
  36. window.panel = {};
  37. // Models & Collections
  38. // --------------------
  39. var App = Backbone.Model.extend({
  40. idAttribute: 'name',
  41. initialize: function() {
  42. this.setStatus();
  43. this.on('change:running', this.setStatus, this);
  44. },
  45. setStatus: function() {
  46. var appStatus = this._parseRunning(this.get('running'));
  47. this.set({up: appStatus[0], appStatus: appStatus[1]});
  48. },
  49. _parseRunning: function(running) {
  50. if (running === undefined) {
  51. running = 'Application failed to start';
  52. }
  53. switch (running) {
  54. case 'true':
  55. case true:
  56. return [true, 'running'];
  57. break;
  58. case 'false':
  59. case false :
  60. return [false, 'stopped'];
  61. break;
  62. default:
  63. var text = running.toString().split('-').join(' ').split('_').join(' ');
  64. return [false, text];
  65. break;
  66. }
  67. }
  68. });
  69. var Apps = Backbone.Collection.extend({
  70. model: App,
  71. url: '/api/apps',
  72. getByName: function(name) {
  73. return this.find(function(app) {
  74. return app.get('name') === name;
  75. });
  76. }
  77. });
  78. var Domain = Backbone.Model.extend({
  79. url: function() { return '/api/appdomains/'; }
  80. });
  81. var Domains = Backbone.Collection.extend({
  82. model: Domain,
  83. url: '/api/appdomains'
  84. });
  85. var EnvVar = Backbone.Model.extend({
  86. url: '/api/env',
  87. sync : function(method, model, options){
  88. if(method==='update'){
  89. model.url = 'api/env/' + model.get('appname')+'/' + model.get('key');
  90. }else if(method ==='create'){
  91. model.url ='/api/env/' ;
  92. options.method = 'PUT';
  93. method = 'update';
  94. }else if(method==='DELETE'){
  95. model.url ='/api/env/' + model.get('appname') +'/' + model.get('key');
  96. }
  97. console.log(options);
  98. return Backbone.sync(method, model, options);
  99. }
  100. });
  101. var apps = new Apps();
  102. // Router
  103. // ------
  104. // Meh, not sure how to handle this yet,
  105. // and its utility seems minimal. Later.
  106. var Router = Backbone.Router.extend({
  107. initialize: function() {
  108. panel.navView = new NavView();
  109. },
  110. routes: {
  111. 'apps': 'apps',
  112. 'domains': 'domains',
  113. 'login': 'login',
  114. 'apps/:appname' :'appDetail',
  115. 'apps/:appname/envvars' : 'envVars',
  116. '*path': 'default'
  117. },
  118. apps: function() {
  119. panel.appListView = new AppListView({collection: apps});
  120. panel.appListView.render();
  121. },
  122. appDetail:function(appname){
  123. panel.appDetailView = new AppDetailView({model: new App({name:appname})});
  124. },
  125. domains: function() {
  126. var domains = new Domains();
  127. panel.domainListView = new DomainListView({collection: domains});
  128. panel.domainListView.render();
  129. },
  130. envVars: function(){
  131. },
  132. login: function() {
  133. },
  134. default: function() {
  135. this.navigate('apps');
  136. }
  137. });
  138. // Views
  139. // -----
  140. var NavView = Backbone.View.extend({
  141. el: '.nav',
  142. events: {
  143. 'click li a': 'navigate'
  144. },
  145. navigate: function(e) {
  146. e.preventDefault();
  147. var href = $(e.currentTarget).attr('href');
  148. panel.router.navigate(href.substr(1), {trigger: true});
  149. // Switch active tab display
  150. this.$('li').removeClass('active');
  151. $(e.currentTarget).parent().addClass('active');
  152. }
  153. });
  154. var AppView = Backbone.View.extend({
  155. tagName: 'tr',
  156. events: {
  157. 'click .start': 'startApp',
  158. 'click .stop': 'stopApp',
  159. 'click .applogs': 'showLogs',
  160. 'click .app-info' : 'showInfo',
  161. 'click .env-vars' : 'showEnvVars',
  162. 'click .git-reset' : 'gitReset'
  163. },
  164. initialize: function() {
  165. this.tmpl = $('#app-tmpl').html();
  166. this.model.on('sync', this.render, this);
  167. },
  168. render: function() {
  169. var html = Mustache.to_html(this.tmpl, this.model.toJSON());
  170. this.$el.html(html);
  171. return this;
  172. },
  173. startApp: function(e) {
  174. e.preventDefault();
  175. this.model.set('running', true);
  176. this.model.save();
  177. },
  178. stopApp: function(e) {
  179. e.preventDefault();
  180. this.model.set('running', false);
  181. this.model.save();
  182. },
  183. showLogs: function(e) {
  184. e.preventDefault();
  185. var appname = this.model.get('name');
  186. $.get('/api/applogs/' + this.model.get('name'), function(res) {
  187. var lines;
  188. if(res.status && res.status === 'failure'){
  189. res.lines = ['No logs found'];
  190. }
  191. var logTmpl = $('#log-tmpl').html();
  192. var html = Mustache.to_html(logTmpl, {name: appname, lines: res.lines});
  193. $('#modal').html(html);
  194. $('#modal').modal('show');
  195. });
  196. },
  197. showInfo: function(e) {
  198. e.preventDefault();
  199. var appName = this.model.get('name');
  200. panel.router.navigate('apps/' + appName, {trigger: true});
  201. var app = apps.getByName(appName);
  202. var infoTmpl = $('#app-info-tmpl').html();
  203. var html = Mustache.to_html(infoTmpl, app.toJSON());
  204. $('#modal').html(html);
  205. $('#modal').modal('show');
  206. },
  207. showEnvVars: function(e){
  208. e.preventDefault();
  209. var appname= this.model.get('name');
  210. panel.router.navigate('apps/'+appname +'/envvars' , {trigger: true });
  211. //skip the mvc for now, ajax get the env vars and the build
  212. $.ajax({
  213. url:$(e.currentTarget).attr('href'),
  214. success: function(r){
  215. var vars = [];
  216. for (var key in r.message){
  217. vars.push({key: key, value: r.message[key]});
  218. };
  219. console.log({vars: vars})
  220. var envTmpl = $('#envvar-list-tmpl').html();
  221. var html = Mustache.to_html(envTmpl, {name: appname , vars: vars});
  222. $('#modal').html(html);
  223. $('#modal').modal('show');
  224. }
  225. })
  226. },
  227. gitReset: function(e){
  228. console.log('git reset');
  229. e.preventDefault();
  230. $.ajax({
  231. url: $(e.currentTarget).attr('href'),
  232. type: "DELETE",
  233. success: function(r) {
  234. flashMessage('The git repository and npm list has been reset');
  235. }
  236. });
  237. }
  238. });
  239. var AppDetailView = Backbone.View.extend({
  240. el: '#modal',
  241. initialize: function() {
  242. this.tmpl = $('#app-info-tmpl').html();
  243. this.model.on('sync', this.render, this);
  244. },
  245. render: function() {
  246. var html = Mustache.to_html(this.tmpl, this.model.toJSON());
  247. this.$el.html(html);
  248. return this;
  249. },
  250. events: {
  251. "click .delete" : "deleteApp"
  252. },
  253. deleteApp : function(e){
  254. e.preventDefault();
  255. if(!hasDomain($(e.currentTarget).data('params').appname)){
  256. $.ajax({
  257. url: $(e.currentTarget).attr('href'),
  258. type: "DELETE",
  259. success: function(r) {
  260. flashMessage('App Removed');
  261. panel.appListView.collection.fetch();
  262. $('#modal').modal('hide');
  263. }
  264. });
  265. }
  266. }
  267. });
  268. var AppListView = Backbone.View.extend({
  269. el: 'body',
  270. initialize: function() {
  271. this.tmpl = $('#app-list-tmpl').html();
  272. this.collection.fetch();
  273. this.collection.on('reset', this.render, this);
  274. },
  275. render: function() {
  276. //this feels very wrong
  277. if(window.location.pathname === '/apps' || window.location.pathname ==='/'){
  278. setObject('apps', this.collection.toJSON());
  279. var html = Mustache.to_html(this.tmpl);
  280. $('.content').html(html).fadeIn('fast');
  281. this.collection.each(function(app) {
  282. var view = new AppView({model: app});
  283. $('.tree tbody').append(view.render().el);
  284. });
  285. }
  286. return this;
  287. },
  288. events: {
  289. "click #new-app" : "newApp",
  290. "click #create-app" : "createApp"
  291. },
  292. newApp: function(e){
  293. e.preventDefault();
  294. var html = Mustache.to_html($('#app-new-tmpl').html());
  295. $('#modal').html(html);
  296. $('#modal').modal('show');
  297. },
  298. createApp: function(e){
  299. panel.router.navigate('apps/new' , {trigger: true });
  300. e.preventDefault();
  301. var appname = $('#newapp-name').val(),
  302. start = $('#newapp-start').val();
  303. var newApp = new App();
  304. newApp.save({name:appname, start:start},{ success: function(){
  305. $('#modal').modal('hide');
  306. }});
  307. }
  308. });
  309. var DomainView = Backbone.View.extend({
  310. tagName: 'tr',
  311. initialize: function() {
  312. this.tmpl = $('#domain-tmpl').html();
  313. this.model.on('sync', this.render, this);
  314. },
  315. render: function() {
  316. var html = Mustache.to_html(this.tmpl, this.model.toJSON());
  317. this.$el.html(html);
  318. return this;
  319. },
  320. events: {
  321. "click .delete" : "deleteDomain"
  322. },
  323. deleteDomain : function(e){
  324. e.preventDefault();
  325. // destroy does not work, different urlthis.destroy();
  326. $.ajax({
  327. url: $(e.currentTarget).attr('href'),
  328. type: "DELETE",
  329. success: function(r) {
  330. flashMessage('Domain Removed');
  331. panel.domainListView.collection.fetch();
  332. $('#modal').modal('hide');
  333. }
  334. })
  335. }
  336. });
  337. var DomainListView = Backbone.View.extend({
  338. el:'body',
  339. initialize: function() {
  340. this.tmpl = $('#domain-list-tmpl').html();
  341. this.collection.fetch();
  342. this.collection.on('reset', this.render, this);
  343. },
  344. render: function() {
  345. if(window.location.pathname !== '/domains'){
  346. return this;
  347. }
  348. setObject('domains', this.collection.toJSON());
  349. var html = Mustache.to_html(this.tmpl);
  350. $('.content').html(html).fadeIn('fast');
  351. this.collection.each(function(domain) {
  352. var view = new DomainView({model: domain});
  353. $('.tree tbody').append(view.render().el);
  354. });
  355. return this;
  356. },
  357. events: {
  358. 'click #new-domain' : 'openNewDomainModal',
  359. 'click #create-domain' : 'addDomain'
  360. },
  361. openNewDomainModal: function(e){
  362. e.preventDefault();
  363. var html = Mustache.to_html($('#domain-new-tmpl').html());
  364. $('#modal').html(html);
  365. $('#modal').modal('show');
  366. $('#newdomain-appname').typeahead({source:getAppNames()})
  367. panel.router.navigate('appdomains/new' , {trigger: true });
  368. },
  369. addDomain: function(e){
  370. e.preventDefault();
  371. var appname = $('#newdomain-appname').val(),
  372. domain = $('#newdomain-domain').val();
  373. var newDomain = new Domain();
  374. newDomain.save({appname:appname, domain:domain},{ success: function(){
  375. $('#modal').modal('hide');
  376. }});
  377. }
  378. });
  379. var EnvVarListView = Backbone.View.extend({
  380. });
  381. $(function() {
  382. if(window.location.pathname==='/login'){
  383. return;
  384. }
  385. panel.router = new Router();
  386. Backbone.history.start({pushState: true});
  387. panel.router.navigate('/apps');
  388. //HACK Until I wire it into the backbone view
  389. $(".swap > span").on("click", function(e){
  390. $(this).hide().next().show().focus();
  391. });
  392. $(".swap.startfile > input").on("change", function(e){
  393. var $input = $(this),
  394. val = $input.val(),
  395. data = $input.data('params');
  396. appname= data.appname,
  397. data.start = val;
  398. $.ajax({
  399. url: "/api/apps/" + appname,
  400. type: "PUT",
  401. data: {"start": data.start, "appname": appname, "name": appname},
  402. success: function(r) {
  403. alert('You will need to restart server for change');
  404. panel.appListView.collection.fetch()
  405. }
  406. })
  407. $(this).hide().prev().html(val).show();
  408. });
  409. $(".swap.envvar > input").on("change", function(e){
  410. var $input = $(this),
  411. val = $input.val(),
  412. data = $input.data('params');
  413. appname= data.appname,
  414. data.start = val;
  415. var env = new EnvVar();
  416. env.set({appname:appname, key : data.key, value:val});
  417. env.save();
  418. $(this).hide().prev().html(val).show();
  419. $('#modal').modal('hide');
  420. flashMessage('You will need to restart server for change.');
  421. });
  422. $('#modal').on('hidden', function(){
  423. history.back();
  424. });
  425. $('#loader').ajaxStart(function(){
  426. $(this).show();
  427. }).ajaxStop(function(){
  428. $(this).hide();
  429. })
  430. ;
  431. });
  432. })();