Fiddled with saving and loading so that admin users can push and pull to parent apps.

git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@3806 ed5197a5-7fde-0310-b194-c3ffbd925b24
This commit is contained in:
Cory McWilliams 2022-01-30 14:51:09 +00:00
parent ec5d7c1a01
commit 0278aceb62
4 changed files with 163 additions and 117 deletions

View File

@ -64,6 +64,7 @@ function socket(request, response, client) {
var packageName; var packageName;
var blobId; var blobId;
var match; var match;
var parentApp;
if (match = /^\/([&%][^\.]{44}(?:\.\w+)?)(\/?.*)/.exec(message.path)) { if (match = /^\/([&%][^\.]{44}(?:\.\w+)?)(\/?.*)/.exec(message.path)) {
blobId = match[1]; blobId = match[1];
} else if (match = /^\/\~([^\/]+)\/([^\/]+)\/$/.exec(message.path)) { } else if (match = /^\/\~([^\/]+)\/([^\/]+)\/$/.exec(message.path)) {
@ -74,8 +75,20 @@ function socket(request, response, client) {
response.send(JSON.stringify({action: "error", error: message.path + ' not found'}), 0x1); response.send(JSON.stringify({action: "error", error: message.path + ' not found'}), 0x1);
return; return;
} }
if (user != 'core') {
var coreId = await new Database('core').get('path:' + path);
parentApp = {
path: '/~core/' + path + '/',
id: coreId,
};
} }
response.send(JSON.stringify({action: "session", credentials: credentials}), 0x1); }
response.send(JSON.stringify({
action: "session",
credentials: credentials,
parentApp: parentApp,
id: blobId,
}), 0x1);
options.api = message.api || []; options.api = message.api || [];
options.credentials = credentials; options.credentials = credentials;

View File

@ -9,6 +9,7 @@ var gApp = {files: {}};
var gEditor; var gEditor;
var gSplit; var gSplit;
var gGraphs = {}; var gGraphs = {};
var gParentApp;
var kErrorColor = "#dc322f"; var kErrorColor = "#dc322f";
var kStatusColor = "#fff"; var kStatusColor = "#fff";
@ -164,6 +165,7 @@ function guessMode(name) {
} }
function loadFile(name, id) { function loadFile(name, id) {
return new Promise(function(resolve, reject) {
var request = new XMLHttpRequest(); var request = new XMLHttpRequest();
request.addEventListener("loadend", function() { request.addEventListener("loadend", function() {
if (request.status == 200) { if (request.status == 200) {
@ -171,26 +173,34 @@ function loadFile(name, id) {
if (!Object.values(gFiles).some(x => !x.doc)) { if (!Object.values(gFiles).some(x => !x.doc)) {
document.getElementById("editPane").style.display = 'flex'; document.getElementById("editPane").style.display = 'flex';
openFile(Object.keys(gFiles).sort()[0]); openFile(Object.keys(gFiles).sort()[0]);
resolve();
} }
} else {
reject('Error loading source.');
} }
}); });
request.addEventListener("error", function() { request.addEventListener("error", function() {
alert("Error loading source."); alert("Error loading source.");
closeEditor(); closeEditor();
reject('Error loading source.');
}); });
request.addEventListener("timeout", function() { request.addEventListener("timeout", function() {
alert("Timed out loading source."); alert("Timed out loading source.");
closeEditor(); closeEditor();
reject('Timed out loading source.');
}); });
request.addEventListener("abort", function() { request.addEventListener("abort", function() {
alert("Loading source aborted."); alert("Loading source aborted.");
closeEditor(); closeEditor();
reject('Loading source aborted.');
}); });
request.open("GET", "/" + id + "/view"); request.open("GET", "/" + id + "/view");
request.send(); request.send();
});
} }
function load() { function load(path) {
return new Promise(function(resolve, reject) {
var request = new XMLHttpRequest(); var request = new XMLHttpRequest();
request.addEventListener("loadend", function() { request.addEventListener("loadend", function() {
if (request.status == 200 || request.status == 404) { if (request.status == 200 || request.status == 404) {
@ -210,6 +220,7 @@ function load() {
gFiles = {}; gFiles = {};
var text; var text;
var isApp = false; var isApp = false;
var promises = [];
if (request.status == 200) { if (request.status == 200) {
text = request.responseText; text = request.responseText;
try { try {
@ -218,7 +229,7 @@ function load() {
isApp = true; isApp = true;
Object.keys(json['files']).forEach(function(name) { Object.keys(json['files']).forEach(function(name) {
gFiles[name] = {}; gFiles[name] = {};
loadFile(name, json['files'][name]); promises.push(loadFile(name, json['files'][name]));
}); });
if (Object.keys(json['files']).length == 0) { if (Object.keys(json['files']).length == 0) {
document.getElementById("editPane").style.display = 'flex'; document.getElementById("editPane").style.display = 'flex';
@ -227,6 +238,8 @@ function load() {
} }
} catch { } catch {
} }
} else {
reject('Load failed.');
} }
if (!isApp) { if (!isApp) {
document.getElementById("editPane").style.display = 'flex'; document.getElementById("editPane").style.display = 'flex';
@ -239,22 +252,27 @@ function load() {
}; };
openFile(gCurrentFile); openFile(gCurrentFile);
} }
Promise.all(promises).then(resolve).catch(reject);
} }
}); });
request.addEventListener("error", function() { request.addEventListener("error", function() {
alert("Error loading source."); alert("Error loading source.");
closeEditor(); closeEditor();
reject('Error loading source.');
}); });
request.addEventListener("timeout", function() { request.addEventListener("timeout", function() {
alert("Timed out loading source."); alert("Timed out loading source.");
closeEditor(); closeEditor();
reject('Timed out loading source.');
}); });
request.addEventListener("abort", function() { request.addEventListener("abort", function() {
alert("Loading source aborted."); alert("Loading source aborted.");
closeEditor(); closeEditor();
reject('Loading source aborted.');
}); });
request.open("GET", url() + "view"); request.open("GET", (path || url()) + "view");
request.send(); request.send();
});
} }
function closeStats() { function closeStats() {
@ -277,22 +295,34 @@ function explodePath() {
return /^\/~([^\/]+)\/([^\/]+)(.*)/.exec(window.location.pathname); return /^\/~([^\/]+)\/([^\/]+)(.*)/.exec(window.location.pathname);
} }
function save() { function save(save_to) {
document.getElementById("save").disabled = true; document.getElementById("save").disabled = true;
document.getElementById("push_to_parent").disabled = true;
document.getElementById("pull_from_parent").disabled = true;
if (gCurrentFile) { if (gCurrentFile) {
gFiles[gCurrentFile].doc = gEditor.getDoc(); gFiles[gCurrentFile].doc = gEditor.getDoc();
} }
var run = document.getElementById("run").checked;
var appFinished = function(success) { var appFinished = function(success) {
document.getElementById("save").disabled = false; document.getElementById("save").disabled = false;
document.getElementById("push_to_parent").disabled = false;
document.getElementById("pull_from_parent").disabled = false;
Object.values(gFiles).forEach(function(file) { Object.values(gFiles).forEach(function(file) {
file.generation = file.doc.changeGeneration(); file.generation = file.doc.changeGeneration();
}); });
updateFiles(); updateFiles();
} }
var save_path = save_to;
if (!save_to) {
var name = document.getElementById("name");
if (name && name.value) {
save_path = name.value;
} else {
save_path = url();
}
}
var always = function() { var always = function() {
var anyUnfinished = Object.values(gFiles).some(x => x.request); var anyUnfinished = Object.values(gFiles).some(x => x.request);
var anyUnsaved = Object.values(gFiles).some(x => !x.doc.isClean(x.generation) && !x.id); var anyUnsaved = Object.values(gFiles).some(x => !x.doc.isClean(x.generation) && !x.id);
@ -312,15 +342,10 @@ function save() {
}); });
request.addEventListener("loadend", function() { request.addEventListener("loadend", function() {
if (request.status == 200) { if (request.status == 200) {
var latest = document.getElementById('latest'); if (save_path != window.location.pathname) {
latest.href = request.responseText; alert('Saved to ' + save_path + '.');
latest.style.visibility = request.responseText != window.location.path ? 'visible' : 'hidden';
if (run) {
if (request.responseText) {
reconnect(request.responseText);
} else { } else {
reconnect(window.location.path); reconnect(save_path);
}
} }
appFinished(true); appFinished(true);
} else { } else {
@ -337,14 +362,7 @@ function save() {
appFinished(false); appFinished(false);
}); });
var saveTo = null; request.open("POST", save_path + 'save', true);
var name = document.getElementById("name");
if (name && name.value) {
saveTo = name.value + "save";
} else {
saveTo = url() + "save";
}
request.open("POST", saveTo, true);
request.setRequestHeader("Content-Type", "text/json"); request.setRequestHeader("Content-Type", "text/json");
request.send(JSON.stringify(app)); request.send(JSON.stringify(app));
} else if (!anyUnfinished) { } else if (!anyUnfinished) {
@ -399,6 +417,14 @@ function save() {
} }
} }
function pullFromParent() {
load(gParentApp ? gParentApp.path : null).then(x => save());
}
function pushToParent() {
save(gParentApp ? gParentApp.path : null);
}
function url() { function url() {
var hash = window.location.href.indexOf('#'); var hash = window.location.href.indexOf('#');
var question = window.location.href.indexOf('?'); var question = window.location.href.indexOf('?');
@ -422,7 +448,11 @@ function receive(message) {
if (message && message.action == "session") { if (message && message.action == "session") {
setStatusMessage("...Executing...", kStatusColor, true); setStatusMessage("...Executing...", kStatusColor, true);
gCredentials = message.credentials; gCredentials = message.credentials;
gParentApp = message.parentApp;
updateLogin(); updateLogin();
var parent_enabled = message.parentApp;
document.getElementById('push_to_parent').style.display = parent_enabled ? 'inline-block' : 'none';
document.getElementById('pull_from_parent').style.display = parent_enabled ? 'inline-block' : 'none';
} else if (message && message.action == "ready") { } else if (message && message.action == "ready") {
setStatusMessage(null); setStatusMessage(null);
if (window.location.hash) { if (window.location.hash) {

View File

@ -479,11 +479,9 @@ async function blobHandler(request, response, blobId, uri) {
var user = match[1]; var user = match[1];
var app = match[2]; var app = match[2];
var credentials = auth.query(request.headers); var credentials = auth.query(request.headers);
if (!credentials || !credentials.session || credentials.session.name != user) { if (credentials && credentials.session &&
response.writeHead(401, {"Content-Type": "text/plain; charset=utf-8"}); (credentials.session.name == user ||
response.end("401 Unauthorized"); (credentials.permissions.administration && user == 'core'))) {
return;
}
var database = new Database(user); var database = new Database(user);
var apps = new Set(); var apps = new Set();
try { try {
@ -495,6 +493,11 @@ async function blobHandler(request, response, blobId, uri) {
database.set('apps', JSON.stringify([...apps])); database.set('apps', JSON.stringify([...apps]));
} }
database.set('path:' + app, newBlobId); database.set('path:' + app, newBlobId);
} else {
response.writeHead(401, {"Content-Type": "text/plain; charset=utf-8"});
response.end("401 Unauthorized");
return;
}
} }
response.writeHead(200, {"Content-Type": "text/plain; charset=utf-8"}); response.writeHead(200, {"Content-Type": "text/plain; charset=utf-8"});
@ -594,7 +597,7 @@ loadSettings().then(function() {
return staticFileHandler(request, response, null, match[1]); return staticFileHandler(request, response, null, match[1]);
} else if (match = /^\/perfetto\/([\.\w-/]*)$/.exec(request.uri)) { } else if (match = /^\/perfetto\/([\.\w-/]*)$/.exec(request.uri)) {
return perfettoHandler(request, response, match[1]); return perfettoHandler(request, response, match[1]);
} else if (match = /^(.*)(\/save)$/.exec(request.uri)) { } else if (match = /^(.*)(\/save?)$/.exec(request.uri)) {
return blobHandler(request, response, match[1], match[2]); return blobHandler(request, response, match[1], match[2]);
} else if (match = /^\/trace$/.exec(request.uri)) { } else if (match = /^\/trace$/.exec(request.uri)) {
var data = trace(); var data = trace();

View File

@ -31,9 +31,9 @@
<input type="button" id="closeEditor" name="closeEditor" value="Close" onclick="closeEditor()"> <input type="button" id="closeEditor" name="closeEditor" value="Close" onclick="closeEditor()">
<input type="button" id="save" name="save" value="Save" onclick="save()"> <input type="button" id="save" name="save" value="Save" onclick="save()">
<input type="text" id="name" name="name"></input> <input type="text" id="name" name="name"></input>
<input type="checkbox" id="run" name="run" checked><label for="run">Restart after save</label> <input type="button" id="push_to_parent" value="Push to Parent" onclick="pushToParent()">
<input type="button" id="pull_from_parent" value="Pull from Parent" onclick="pullFromParent()">
<input type="button" id="revert" name="revert" value="Revert to Saved" onclick="revert()"> <input type="button" id="revert" name="revert" value="Revert to Saved" onclick="revert()">
<a id="latest" href="">Latest</a>
</div> </div>
<div class="hbox" style="height: 100%"> <div class="hbox" style="height: 100%">
<div id="filesPane"> <div id="filesPane">