This commit is contained in:
Jeff Cross
2012-12-20 11:34:39 -07:00
16 changed files with 319 additions and 145 deletions

10
bin/dpd
View File

@@ -328,8 +328,14 @@ program
d.package(tar, function (err) {
if(err) return console.error(err);
console.log('pushing...');
d.publish('http://deploy.deploydapp.com:3003', tar, key, function (err) {
if(err) return console.error('failed to push:', err.message || 'could not push app to ' + DPDAPP_URL);
d.publish('http://cloud.deployd.com', tar, key, function (err) {
if(err) {
if (err.statusCode === 401) {
d.setConfig('sid', undefined);
d.setConfig('user', undefined);
}
return console.error('failed to push:', err.message || 'could not push app to ' + DPDAPP_URL);
}
console.log('deployd app to http://' + d.subdomain + '.deploydapp.com!');
});
});

View File

@@ -15,8 +15,8 @@ var path = require('path')
function Deployment(appPath, user, subdomain) {
var remote = this.remote = 'deploydapp.com';
this.api = "http://api.deploydapp.com:3000";
this.deployUrl = "http://deploy.deploydapp.com:3003";
this.api = "http://api.deployd.com";
this.deployUrl = "http://cloud.deployd.com";
this.path = path.resolve(appPath);
@@ -100,7 +100,12 @@ Deployment.prototype.publish = function (url, tar, key, callback) {
function done(err, res, body) {
if(err) return callback(err);
if(res.statusCode >= 400) return callback(new Error(body));
if(res.statusCode >= 400) {
var error = new Error(body);
error.statusCode = res.statusCode;
return callback(error);
}
callback(null, deployment.getConfig(deployment.configKey()));
}
@@ -119,12 +124,7 @@ Deployment.prototype.publish = function (url, tar, key, callback) {
return callback(err);
}
if(res.statusCode !== 200) {
deployment.setConfig('sid', undefined);
deployment.setConfig('user', undefined);
delete deployment.sid;
delete deployment.user;
} else {
if(res.statusCode === 200) {
try {
body = JSON.parse(body);
@@ -227,6 +227,7 @@ Deployment.prototype.getCurrentUser = function(fn) {
jar: false
}, function(err, res, user) {
if (err) return fn(err);
if (res.statusCode === 404) return fn(new Error("Could not connect"));
if (user) user = JSON.parse(user);
if (!user) return fn(new Error("Not authenticated"));
fn(null, user);

View File

@@ -1,9 +1,15 @@
<div id="deployments">
<div class="well hide" id="deployments-connection-error">
<h3>Deployments</h3>
<div class="">
<p>Could not connect to <a target="_blank" href="http://cloud.deployd.com">cloud.deployd.com</a></p>
<a href="">Refresh</a>
<div class="modal hide" id="deployments-connection-error">
<div class="modal-header">
<h3>Deployments</h3>
</div>
<div class="modal-body">
<p>Could not connect to <a target="_blank" href="http://cloud.deployd.com">cloud.deployd.com</a>.</p>
</div>
<div class="modal-footer">
<a href="" class="btn">Refresh</a>
<a href="/dashboard" class="cancel-btn">cancel</a>
</div>
</div>
<div class="well hide" id="inner-deployments">
@@ -16,36 +22,40 @@
<div class="clearfix"></div>
<div id="deployments-empty" class="hide">
<hr />
<p>Create your first Deployment to host your app on <a href="http://cloud.deployd.com">cloud.deployd.com</a>:</p>
</div>
<div id="deployment-list-container">
<ul class="component-list" id="deployment-list">
</ul>
<hr />
<div class="form-placeholder">
</div>
</div>
<form class="form-inline" id="deploy-new-form">
<input type="text" class="input-large deployment-name" placeholder="new-deployment" />
<span class="help-inline">.deploydapp.com</span>
</div>
<button class="pull-right btn btn-success">
<i class="icon-plus icon-white"></i>
Add and Deploy
</button>
<br />
<div id="deployments-empty" class="modal hide">
<div class="modal-header clearfix">
<h3>
Create a Deployment
<div class="header-link pull-right">
<span class="username">USERNAME</span>
| <a href="#" class="logout-btn">log out</a>
</div>
</h3>
<select id="existing-deployment-dropdown" class="empty hide">
</select>
</form>
</div>
<div class="modal-body">
<p>Create your first Deployment to host your app on <a href="http://cloud.deployd.com">cloud.deployd.com</a>:</p>
<hr />
<div class="form-placeholder">
</div>
</div>
<!-- <div class="modal-footer">
<a href="#" class="cancel-btn logout-btn">log out</a>
<a href="/dashboard" class="cancel-btn">cancel</a>
</div> -->
</div>
</div>
@@ -86,10 +96,36 @@
</div>
</div>
<form class="form-inline hide" id="deploy-new-form">
<input type="text" class="input-large deployment-name" placeholder="new-deployment" />
<span class="help-inline">.deploydapp.com</span>
<button class="pull-right btn btn-success">
<i class="icon-plus icon-white"></i>
Add and Deploy
</button>
<br />
<div id="existing-deployment-dropdown-btn" class="hide btn-group pull-right" style="margin-top: 10px">
<a class="btn dropdown-toggle" data-toggle="dropdown" href="#">
<i class="icon-plus"></i>
Add Existing Deployment
<span class="caret"></span>
</a>
<ul class="dropdown-menu" id="existing-deployment-dropdown">
</ul>
</div>
<div class="clearfix"></div>
<!-- <select id="existing-deployment-dropdown" class="empty hide">
</select> -->
</form>
<script type="text/html" id="deployment-template">
<li id="deployment-<%= deployment.appId %>" class="component-item <%= deployment.deploying ? 'deploying' : '' %>" data-index="<%= index %>">
<div class="component-item-header">
<span class="code deploymentname"><%= deployment.name %> <i class="icon-white icon-external-link open-icon"></i></span>
<span class="code deploymentname"><%= deployment.name %> <% if (!deployment.deploying) { %><i class="icon-white icon-external-link open-icon"></i><% } %></span>
<% if (deployment.deploying) { %>
<div class="pull-right deploy-status">
<span class="deploy-status-text">Deploying...</span>

View File

@@ -12,7 +12,7 @@
var dpdDeployments = dpd('__deployments');
var deploymentTemplate = _.template($('#deployment-template').html());
var $modal = $('#deployAuthModal').modal({
backdrop: 'static',
backdrop: false,
keyboard: false,
show: false
});
@@ -27,7 +27,7 @@
$('#deployment-list').on('click', '.deploy-btn', onClickDeployBtn);
$('#deployment-list').on('click', '.remove-btn', onClickRemoveBtn);
$('#inner-deployments h3 .logout-btn').click(function() {
$('#deployments h3 .logout-btn').click(function() {
onClickLogoutBtn();
return false;
});
@@ -38,7 +38,7 @@
deployNew();
return false;
});
$('#existing-deployment-dropdown').change(onSelectExistingDeployment);
$('#existing-deployment-dropdown').on('click', 'li a', onSelectExistingDeployment);
function loadDeployments() {
dpdDeployments.get(function(deployments, error) {
@@ -114,6 +114,7 @@
Object.keys(result).forEach(function(k) {
deployment[k] = result[k];
});
deployment.id = result.appId;
deployment.__deployed = true;
}
@@ -152,6 +153,7 @@
function onClickDeployment(e) {
if ($(e.target).is('a')) return true;
var deployment = getDeployment(e.currentTarget);
if (deployment.deploying) return true;
var href = "http://" + deployment.name + ".deploydapp.com";
window.open(href, "_blank");
window.focus();
@@ -194,16 +196,12 @@
}
function onSelectExistingDeployment(e) {
var $dropdown = $(this);
var name = $dropdown.val();
if (name === "__") return true;
var $link = $(this);
var name = $link.attr('data-value');
$dropdown.attr('disabled', true);
deployNew(name);
deployNew(name, function() {
$dropdown.removeAttr('disabled');
$dropdown.val("__");
});
e.preventDefault();
}
function onClickLogoutBtn() {
@@ -213,14 +211,13 @@
}
function showLogin(error) {
renderDeployments();
if (scope.user) {
$modal.modal('hide');
$('#inner-deployments').show();
$('#deployments h3 .username').text(scope.user.username);
$('#deploy-new-form .deployment-name').focus();
} else {
$modal.modal('show');
$('#inner-deployments').hide();
if (error) {
$('#auth-error').show();
} else {
@@ -231,53 +228,62 @@
}
function renderDeployments() {
if (scope.deployments === null || scope.deployments.length) {
$('#deployments-empty').hide();
$('#deployment-list-container').show();
if (scope.user) {
if (scope.deployments === null || scope.deployments.length) {
$('#deployments-empty').hide();
$('#inner-deployments').show();
$('#deploy-new-form').appendTo('#inner-deployments .form-placeholder').show();
} else {
$('#deployments-empty').show();
$('#inner-deployments').hide();
$('#deploy-new-form').appendTo('#deployments-empty .form-placeholder').show();
var $input = $('#deploy-new-form .deployment-name');
if (!$input.val()) $input.val(Context.appName);
}
if (scope.deployments) {
$('#deployment-list').empty();
// clean up orphaned tooltips
$('body > .tooltip').each(function() {
var $tooltip = $(this);
$tooltip.fadeOut(function() {
$tooltip.remove();
});
});
scope.deployments.forEach(function(d, i) {
$('#deployment-list').append(deploymentTemplate({
deployment: d,
index: i
}));
if (d.__deployed) {
showDeploymentTooltip(d);
d.__deployed = false;
}
});
}
} else {
$('#deployments-empty').show();
$('#deployment-list-container').hide();
var $input = $('#deploy-new-form .deployment-name');
if (!$input.val()) $input.val(Context.appName);
}
if (scope.deployments) {
$('#deployment-list').empty();
// clean up orphaned tooltips
$('body > .tooltip').each(function() {
var $tooltip = $(this);
$tooltip.fadeOut(function() {
$tooltip.remove();
});
});
scope.deployments.forEach(function(d, i) {
$('#deployment-list').append(deploymentTemplate({
deployment: d,
index: i
}));
if (d.__deployed) {
showDeploymentTooltip(d);
d.__deployed = false;
}
});
$('#deployments-empty').hide();
$('#deployments-empty').hide();
}
}
function renderOnlineDeployments() {
var $dropdownBtn = $('#existing-deployment-dropdown-btn');
var $dropdown = $('#existing-deployment-dropdown');
if (scope.onlineDeployments && scope.onlineDeployments.length) {
$dropdown.show().empty();
$dropdown.append('<option value="__">or add an existing deployment...</option>');
$dropdownBtn.show();
$dropdown.empty();
// $dropdown.append('<option value="__">or add an existing deployment...</option>');
scope.onlineDeployments.forEach(function(o) {
$dropdown.append('<option value="' + o.name + '">' + o.name + "</option>");
$dropdown.append('<li><a href="#" data-value="' + o.name + '">' + o.name + "</a></li>");
});
} else {
$dropdown.hide();
$dropdownBtn.hide();
}
}

View File

@@ -37,6 +37,10 @@
width: 585px;
max-width: 585px;
#deploy-new-form {
margin-bottom: 0;
}
hr {
border-bottom: none;
border-width: 1px;
@@ -45,9 +49,16 @@
#deployments-empty {
.well {
padding: 20px;
color: #fff;
overflow: visible;
.modal-body {
overflow-y: visible;
}
hr {
border-top-color: #ccc;
}
}
@@ -101,18 +112,27 @@
}
}
#existing-deployment-dropdown {
/*#existing-deployment-dropdown {
margin-top: 10px;
color: #999;
option {
&:first-child {
color: #999;
}
color: white;
}
}
#deployments-empty #existing-deployment-dropdown {
option {
color: @black;
}
}
#inner-deployments #existing-deployment-dropdown {
option {
color: white;
}
}*/
}
#deployAuthModal {
@@ -141,7 +161,9 @@
color: @textColor !important;
}
}
}
.modal {
.cancel-btn {
display: inline-block;
margin-left: 8px;

View File

@@ -34,15 +34,20 @@ body {
color: white;
display: inline-block;
margin: 0;
font-size: 11pt;
font-size: 16px;
line-height: 38px;
> i {
margin-top: 2px;
}
&:hover, &.active {
background: rgba(255,255,255, 0.1);
text-decoration: none;
}
.caret {
opacity: 1;
border-top-color: white;
margin-top: 18px;
}

View File

@@ -4071,15 +4071,19 @@ body {
color: white;
display: inline-block;
margin: 0;
font-size: 11pt;
font-size: 16px;
line-height: 38px;
}
#header .header-link > i {
margin-top: 2px;
}
#header .header-link:hover,
#header .header-link.active {
background: rgba(255, 255, 255, 0.1);
text-decoration: none;
}
#header .header-link .caret {
opacity: 1;
border-top-color: white;
margin-top: 18px;
}
@@ -4267,15 +4271,43 @@ body {
#deployments {
width: 585px;
max-width: 585px;
/*#existing-deployment-dropdown {
margin-top: 10px;
color: #999;
}
#deployments-empty #existing-deployment-dropdown {
option {
color: @black;
}
}
#inner-deployments #existing-deployment-dropdown {
option {
color: white;
}
}*/
}
#deployments #deploy-new-form {
margin-bottom: 0;
}
#deployments hr {
border-bottom: none;
border-width: 1px;
border-top-color: #555555;
}
#deployments #deployments-empty .well {
padding: 20px;
color: #fff;
#deployments #deployments-empty {
overflow: visible;
}
#deployments #deployments-empty .modal-body {
overflow-y: visible;
}
#deployments #deployments-empty hr {
border-top-color: #ccc;
}
#deployments .component-item.deploying .component-item-header {
box-shadow: inset 0 0 10px 0px #429e96;
@@ -4313,16 +4345,6 @@ body {
#deployments .component-item-header:hover .open-icon {
visibility: visible;
}
#deployments #existing-deployment-dropdown {
margin-top: 10px;
color: #999;
}
#deployments #existing-deployment-dropdown option {
color: white;
}
#deployments #existing-deployment-dropdown option:first-child {
color: #999;
}
#deployAuthModal .row {
margin-top: 20px;
}
@@ -4338,7 +4360,7 @@ body {
#deployAuthModal .form-vertical label {
color: #666666 !important;
}
#deployAuthModal .cancel-btn {
.modal .cancel-btn {
display: inline-block;
margin-left: 8px;
vertical-align: middle;
@@ -5109,7 +5131,9 @@ select {
#dialog input,
.modal input,
#dialog textarea,
.modal textarea {
.modal textarea,
#dialog select,
.modal select {
background-color: #ffffff;
color: #666666;
}
@@ -5726,6 +5750,8 @@ table input[type=number] {
}
.modal-header h3 {
color: #363535;
line-height: 22px;
margin: 2px 0 2px 0;
}
#api table {
margin-bottom: 0;

View File

@@ -100,7 +100,7 @@ background-color: darken(@black, 10%);
input, textarea, select {background: darken(@black, 10%); color: @white; border: solid 1px @black;}
#dialog, .modal {
input, textarea {
input, textarea, select {
background-color: #ffffff;
color: @textColor;
}
@@ -788,7 +788,11 @@ table input {
}
.modal-header {
h3 {color: @black;}
h3 {
color: @black;
line-height: 22px;
margin: 2px 0 2px 0;
}
}
#api {

View File

@@ -4,6 +4,8 @@ var util = require('util')
, path = require('path')
, q = require('q')
, qutil = require('../util/qutil')
, Keys = require('../keys')
, keys = new Keys()
, Deployment = require('../client/deploy').Deployment;
function InternalDeployments() {
@@ -119,9 +121,23 @@ InternalDeployments.prototype.deploy = function(ctx, next) {
var d = new Deployment('.', null, subdomain);
var keyGenQ = q.nfcall(function(fn) {
keys.getLocal(function (err, key) {
if(err) return fn(err);
if(key) {
fn(null, key);
} else {
keys.create(function(err, key) {
if(err) return fn(err);
fn(null, key);
});
}
});
});
var authQ = qutil.cinvoke(d, 'authenticate');
var packageQ = authQ.then(function(ok) {
var packageQ = q.spread([authQ, keyGenQ], function(ok) {
if (ok) {
return q.ninvoke(d, 'package', self.packageFile);
} else {

View File

@@ -1,5 +1,5 @@
<div data-bind="scrollbarWidth: view.scrollWidth"></div>
<div id="data" class="well full-page">
<div id="data" class="well">
<h3>Data</h3>
<div id="no-property-warning" class="alert alert-info hide">
@@ -7,7 +7,7 @@
<a href="../properties">Add some now</a> before editing data.
</div>
<div id="table-container" data-bind="scrollY: view.scrollY, scrollX: view.scrollX, screenDimensions: view.dimensions, reflow: view.init">
<div id="table-container" data-bind="scrollY: view.scrollY, scrollX: view.scrollX, screenDimensions: view.dimensions, reflow: view.init, style: {height: view.dimensions().height + 'px'}">
<div id="editor-modal" class="modal hide" data-bind="if: propertiesLoaded, bootstrapModal: edit.editingModal">
<div class="modal-header">
@@ -29,7 +29,7 @@
<div class="mini-edit" data-bind="style: {top: view.selectedCellPos().top + 'px', left: view.selectedCellPos().left + 'px'}, if: edit.editingInline, click: view.noOp, clickBubble: false">
<!-- ko if: selectedProp().type === 'string' -->
<a href="#" title="Edit in modal" data-bind="click: edit.openModal"><i class="icon-edit icon-white"></i></a>
<input type="text" data-bind="hasfocus: edit.focusInput, select: 'selectEditor', value: edit.editValue, valueUpdate: 'afterkeydown', typeahead: edit.typeahead" />
<input type="text" class="inline-editor-input" data-bind="hasfocus: edit.focusInput, select: 'selectEditor', customValue: edit.editValue, valueUpdate: 'afterkeydown', typeahead: edit.typeahead" />
<!-- /ko -->
<!-- ko if: selectedProp().type === 'number' -->
<input type="text" class="number" data-bind="hasfocus: edit.focusInput, select: 'selectEditor', numberValue: edit.editValue, valueUpdate: 'afterkeydown'" />

View File

@@ -207,7 +207,7 @@
var val = vm.selectedRow()[vm.selectedProp().name]();
if (type === 'string') {
return !val || val.indexOf('\n') === -1;
return !val || val.toString().indexOf('\n') === -1;
}
return true;

View File

@@ -159,22 +159,28 @@ ko.bindingHandlers.screenDimensions = {
function calculateScreenDimensions(element) {
var $element = $(element)
, $window = $(window)
, top = $element.offset().top - $window.scrollTop()
, windowHeight = $window.height()
, windowWidth = $window.width()
, staticTop = $element.offset().top
, top = staticTop - $window.scrollTop()
, left = $element.offset().left - $window.scrollLeft()
, height = $element.height()
, height = windowHeight - staticTop - 60 // 60 is bottom padding - probably shouldn't be hardcoded
, width = $element.width()
, bottom = top + height
, right = left + width
, bottomRelative = $(window).height() - bottom
, rightRelative = $(window).width() - right;
, bottomRelative = windowHeight - bottom
, rightRelative = windowWidth - right;
return {
top: top
staticTop: staticTop
, top: top
, left: left
, bottom: bottom
, right: right
, height: height
, width: width
, windowHeight: windowHeight
, windowWidth: windowWidth
, bottomRelative: bottomRelative
, rightRelative: rightRelative
};
@@ -220,13 +226,46 @@ ko.bindingHandlers.element = {
}
};
// I don't know why I have to do this; seems like Knock's built-in version is bugged
ko.bindingHandlers.customValue = {
init: function(element, valueAccessor, allBindingsAccessor, vm) {
var prop = valueAccessor()
, allBindings = allBindingsAccessor()
, updateMode = allBindings.valueUpdate || 'blur';
if (typeof prop === 'function') {
if (updateMode === 'afterkeydown') {
$(element).on('keydown', function(e) {
setTimeout(function() {
prop($(element).val());
}, 0);
});
}
$(element).blur(function() {
prop($(element).val());
});
}
}, update: function(element, valueAccessor) {
var newVal = ko.utils.unwrapObservable(valueAccessor());
if (newVal !== $(element).val()) {
$(element).val(newVal);
}
}
};
ko.bindingHandlers.numberValue = {
init: function(element, valueAccessor, allBindingsAccessor, vm) {
var prop = valueAccessor()
, allBindings = allBindingsAccessor()
, updateMode = allBindings.valueUpdate || 'blur';
$(element).val(ko.utils.unwrapObservable(prop));
if (parseFloat($(element).val()) !== ko.utils.unwrapObservable(prop)) {
$(element).val(ko.utils.unwrapObservable(prop));
}
if (typeof prop === 'function') {
@@ -234,7 +273,7 @@ ko.bindingHandlers.numberValue = {
$(element).on('keydown', function(e) {
var num = parseFloat($(element).val());
if (isNaN(num)) return;
if (isNaN(num)) return true;
if (e.which == 38) { // up
prop(num + 1);
@@ -266,7 +305,7 @@ ko.bindingHandlers.numberValue = {
function setNumberProp(element, prop) {
var num = parseFloat($(element).val());
if (!isNaN(num)) prop(num);
if (!isNaN(num) && prop() != num) prop(num);
}
ko.bindingHandlers.select = {
@@ -323,10 +362,10 @@ ko.bindingHandlers.aceEditor = {
$(element).data('aceEditor', editor);
editor.getSession().on('change', function(e) {
var newVal = editor.getValue();
if (newVal !== val()) {
val(newVal);
}
var newVal = editor.getValue();
if (newVal !== val() && $(element).is(':visible')) {
val(newVal);
}
});
}, update: function(element, valueAccessor) {
var editor = $(element).data('aceEditor')
@@ -338,7 +377,6 @@ ko.bindingHandlers.aceEditor = {
val = val.toString();
}
if (val !== editor.getValue()) {
editor.setValue(val);
}
@@ -358,6 +396,21 @@ ko.bindingHandlers.aceEditorResize = {
}
};
// ko.bindingHandlers.customHasFocus = {
// update: function(element, valueAccessor) {
// var $el = $(element)
// , val = ko.utils.unwrapObservable(valueAccessor());
// setTimeout(function() {
// if (val && !$el.is(':focus')) {
// $el.focus();
// } else if (!val && $el.is(':focus')) {
// $el.blur();
// }
// }, 5);
// }
// };
ko.bindingHandlers.aceEditorFocus = {
update: function(element, valueAccessor) {
var editor = $(element).data('aceEditor')

View File

@@ -15,7 +15,7 @@
bottom: 0;
right: 0;
left: 0;
background: rgba(0, 0, 0, 0.5);
background: rgba(0, 0, 0, 0);
z-index: 100;
}
#inline-editor #inline-editor-table-overlay {
@@ -65,11 +65,6 @@
}
#table-container {
position: relative;
box-flex: 1;
-moz-box-flex: 1;
-webkit-box-flex: 1;
-ms-box-flex: 1;
-o-box-flex: 1;
height: 0px;
overflow: scroll;
}

View File

@@ -29,7 +29,7 @@
right: 0;
left: 0;
background: rgba(0, 0, 0, 0.5);
background: rgba(0, 0, 0, 0);
z-index: 100;
#inline-editor-table-overlay {
@@ -78,7 +78,7 @@
#table-container {
position: relative;
.box-flex(1);
// .box-flex(1);
height: 0px;
overflow: scroll;

View File

@@ -580,9 +580,13 @@ Collection.prototype.save = function (ctx, fn) {
}
if(err) return done(err);
// copy previous obj
Object.keys(obj).forEach(function (key) {
prev[key] = obj[key];
});
// merge changes
Object.keys(item).forEach(function (key) {
prev[key] = obj[key];
obj[key] = item[key];
});

View File

@@ -3,7 +3,7 @@ var Server = require('./server')
, Monitor = require('./monitor')
, commands = {};
require('longjohn');
//require('longjohn');
/**
* Commands exposed to parent process.