mirror of
https://github.com/slynn1324/tinypin.git
synced 2026-01-23 02:25:08 +00:00
Added file upload capabilties via the add pin dialog or drag-and-drop on the board page
This commit is contained in:
parent
8ee0acda17
commit
0f3fc05594
11 changed files with 690 additions and 54 deletions
166
client/app.js
166
client/app.js
|
|
@ -71,6 +71,8 @@ app.addSetter('load.board', async (data, force) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
app.addSetter('load.user', async (data) => {
|
app.addSetter('load.user', async (data) => {
|
||||||
|
|
||||||
|
console.log("load.user");
|
||||||
store.do("loader.show");
|
store.do("loader.show");
|
||||||
|
|
||||||
let res = await fetch("/api/whoami");
|
let res = await fetch("/api/whoami");
|
||||||
|
|
@ -96,6 +98,102 @@ app.addSetter("hash.update", (data) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
app.addSetter("app.uploadDroppedFiles", async (data, evt) => {
|
||||||
|
|
||||||
|
let boardId = store.data.board.id;
|
||||||
|
|
||||||
|
if ( boardId ){
|
||||||
|
let hasFiles = event.dataTransfer.types.find(i => i == "Files") == "Files";
|
||||||
|
if ( hasFiles ){
|
||||||
|
|
||||||
|
if ( evt.dataTransfer.items ){
|
||||||
|
|
||||||
|
let files = [];
|
||||||
|
|
||||||
|
for ( let i = 0; i < evt.dataTransfer.items.length; ++i ){
|
||||||
|
if ( evt.dataTransfer.items[i].kind === "file" ){
|
||||||
|
let file = evt.dataTransfer.items[i].getAsFile();
|
||||||
|
|
||||||
|
if ( file.type != "image/jpeg" && file.type != "image/png" ){
|
||||||
|
|
||||||
|
window.alert("Unsupported file type. JPEG and PNG images are supported.");
|
||||||
|
console.log("Unsupported file type: " + file.type);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check size
|
||||||
|
if ( file.size >= 26214400 ){
|
||||||
|
window.alert("File size exceeds the 25MB limit.");
|
||||||
|
console.log("File size exceeds the 25MB limit. size=" + file.size);
|
||||||
|
document.getElementById("fileInput").value = "";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
files.push(file);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("Number of files=" + files.length);
|
||||||
|
|
||||||
|
for ( let i = 0; i < files.length; ++i ){
|
||||||
|
|
||||||
|
data.dropUploadMessage = `Uploading ${i+1} of ${files.length}`;
|
||||||
|
|
||||||
|
try {
|
||||||
|
let newPin = await multipartUpload(files[i], boardId);
|
||||||
|
if ( data.board && data.board.id == boardId ){
|
||||||
|
data.board.pins.push(newPin);
|
||||||
|
}
|
||||||
|
} catch (e){
|
||||||
|
window.alert("Error uploading images.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data.dropUploadMessage = null;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
function PostException(statusCode, errorMessage){
|
||||||
|
this.statusCode = statusCode;
|
||||||
|
this.errorMessage = errorMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function multipartUpload(file, boardId, newBoardName, siteUrl, description){
|
||||||
|
console.log("attempting multipart upload");
|
||||||
|
let formData = new FormData();
|
||||||
|
formData.append("file", file);
|
||||||
|
formData.append("boardId", boardId);
|
||||||
|
if ( newBoardName ){
|
||||||
|
formData.append("newBoardName", newBoardName);
|
||||||
|
}
|
||||||
|
if ( siteUrl ){
|
||||||
|
formData.append("siteUrl", siteUrl);
|
||||||
|
}
|
||||||
|
if ( description ){
|
||||||
|
formData.append("description", description);
|
||||||
|
}
|
||||||
|
|
||||||
|
let res = await fetch("./multiup", {
|
||||||
|
method: "POST",
|
||||||
|
body: formData
|
||||||
|
});
|
||||||
|
|
||||||
|
if ( res.status == 200 ){
|
||||||
|
return res.json();
|
||||||
|
} else {
|
||||||
|
console.error("error uploading status=" + res.status + " body=" + await res.text());
|
||||||
|
throw new PostException(res.status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function dispatchSocketConnect(){
|
function dispatchSocketConnect(){
|
||||||
window.dispatchEvent(new CustomEvent("socket-connect"));
|
window.dispatchEvent(new CustomEvent("socket-connect"));
|
||||||
}
|
}
|
||||||
|
|
@ -121,7 +219,8 @@ let store = new Reef.Store({
|
||||||
previewImageUrl: null,
|
previewImageUrl: null,
|
||||||
siteUrl: "",
|
siteUrl: "",
|
||||||
description: "",
|
description: "",
|
||||||
saveInProgress: false
|
saveInProgress: false,
|
||||||
|
didYouKnowDragAndDropMessageDisabled: window.localStorage.addPinModal_didYouKnowDragAndDropMessageDisabled == "true" || false
|
||||||
},
|
},
|
||||||
pinZoomModal: {
|
pinZoomModal: {
|
||||||
active: false,
|
active: false,
|
||||||
|
|
@ -167,6 +266,23 @@ const appComponent = new Reef("#app", {
|
||||||
<div id="editBoardModal"></div>
|
<div id="editBoardModal"></div>
|
||||||
<div id="aboutModal"></div>
|
<div id="aboutModal"></div>
|
||||||
<div id="editPinModal"></div>
|
<div id="editPinModal"></div>
|
||||||
|
<div id="dragAndDropModal" class="modal">
|
||||||
|
<div class="modal-background"></div>
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="box">
|
||||||
|
<div class="m-6">drop to add pins</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal ${data.dropUploadMessage ? 'is-active' : ''}">
|
||||||
|
<div class="modal-background"></div>
|
||||||
|
<div class="modal-content has-text-centered">
|
||||||
|
<div class="box">
|
||||||
|
<div class="button is-text is-large is-loading"></div>
|
||||||
|
<div>${data.dropUploadMessage}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
`
|
`
|
||||||
//<div id="loader" class="button is-text ${data.loading ? 'is-loading' : ''}"></div>
|
//<div id="loader" class="button is-text ${data.loading ? 'is-loading' : ''}"></div>
|
||||||
}
|
}
|
||||||
|
|
@ -290,4 +406,50 @@ document.addEventListener("visibilitychange", async () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
window.dragInProgress = false;
|
||||||
|
|
||||||
|
window.ondragover = (evt) => {
|
||||||
|
|
||||||
|
let data = store.data;
|
||||||
|
|
||||||
|
if ( !data.board || data.addPinModal.active || data.editPinModal.active || data.aboutModal.active || data.pinZoomModal.active ){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
evt.preventDefault();
|
||||||
|
|
||||||
|
let hasFiles = event.dataTransfer.types.find(i => i == "Files") == "Files";
|
||||||
|
if ( hasFiles ){
|
||||||
|
window.dragInProgress = true;
|
||||||
|
document.getElementById("dragAndDropModal").classList.add("is-active");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.ondragleave = (evt) => {
|
||||||
|
if ( evt.x == 0 && evt.y == 0 ){
|
||||||
|
document.getElementById("dragAndDropModal").classList.remove("is-active");
|
||||||
|
window.dragInProgress = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.ondrop = async (evt) => {
|
||||||
|
|
||||||
|
if ( window.dragInProgress ){
|
||||||
|
evt.preventDefault();
|
||||||
|
|
||||||
|
document.getElementById("dragAndDropModal").classList.remove("is-active");
|
||||||
|
|
||||||
|
let hasFiles = event.dataTransfer.types.find(i => i == "Files") == "Files";
|
||||||
|
if ( hasFiles ){
|
||||||
|
store.do("app.uploadDroppedFiles", evt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -437,4 +437,15 @@ body.socketConnected #socketConnected {
|
||||||
.is-touch .navbar.is-light .navbar-brand .navbar-link:hover,
|
.is-touch .navbar.is-light .navbar-brand .navbar-link:hover,
|
||||||
.is-touch .navbar.is-light .navbar-brand .navbar-link.is-active {
|
.is-touch .navbar.is-light .navbar-brand .navbar-link.is-active {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#dragAndDropModal .modal-background{
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
#dragAndDropModal .box {
|
||||||
|
background: none;
|
||||||
|
border: 3px dashed #000;
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
@ -21,9 +21,18 @@ app.addSetter('addPinModal.close', (data) => {
|
||||||
data.addPinModal.description = "";
|
data.addPinModal.description = "";
|
||||||
data.addPinModal.newBoardName = "";
|
data.addPinModal.newBoardName = "";
|
||||||
data.addPinModal.saveInProgress = false;
|
data.addPinModal.saveInProgress = false;
|
||||||
|
data.addPinModal.uploadFile = null;
|
||||||
|
|
||||||
|
|
||||||
|
// weird hack to pick up whether it redraws or not...
|
||||||
|
let fileInput = document.getElementById("fileInput");
|
||||||
|
if ( fileInput ){
|
||||||
|
fileInput.value = "";
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.addSetter('addPinModal.updatePreview', (data) => {
|
app.addSetter('addPinModal.updatePreview', (data) => {
|
||||||
|
|
||||||
if ( data.addPinModal.imageUrl.startsWith("http") ){
|
if ( data.addPinModal.imageUrl.startsWith("http") ){
|
||||||
( async() => {
|
( async() => {
|
||||||
let res = await fetch(data.addPinModal.imageUrl, {
|
let res = await fetch(data.addPinModal.imageUrl, {
|
||||||
|
|
@ -38,6 +47,7 @@ app.addSetter('addPinModal.updatePreview', (data) => {
|
||||||
} else {
|
} else {
|
||||||
data.addPinModal.previewImageUrl = null;
|
data.addPinModal.previewImageUrl = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
app.addSetter('addPinModal.save', async (data) => {
|
app.addSetter('addPinModal.save', async (data) => {
|
||||||
|
|
@ -48,37 +58,63 @@ app.addSetter('addPinModal.save', async (data) => {
|
||||||
|
|
||||||
let boardId = data.addPinModal.boardId;
|
let boardId = data.addPinModal.boardId;
|
||||||
|
|
||||||
let postData = {
|
|
||||||
boardId: boardId,
|
|
||||||
newBoardName: data.addPinModal.newBoardName,
|
|
||||||
imageUrl: data.addPinModal.imageUrl,
|
|
||||||
siteUrl: data.addPinModal.siteUrl,
|
|
||||||
description: data.addPinModal.description
|
|
||||||
};
|
|
||||||
|
|
||||||
let res = await fetch('api/pins', {
|
if ( data.addPinModal.uploadFile ){
|
||||||
method: 'POST',
|
// do file upload
|
||||||
headers: {
|
|
||||||
'Content-Type': "application/json"
|
console.log("attempting multipart file uploading");
|
||||||
},
|
|
||||||
body: JSON.stringify(postData)
|
try {
|
||||||
});
|
let newPin = await multipartUpload(data.addPinModal.uploadFile, boardId, data.addPinModal.newBoardName, data.addPinModal.siteUrl, data.addPinModal.description);
|
||||||
|
if ( data.board && data.board.id == boardId ){
|
||||||
if ( res.status == 200 ){
|
data.board.pins.push(newPin);
|
||||||
|
}
|
||||||
let body = await res.json();
|
|
||||||
if ( data.board && data.board.id == boardId ){
|
window.localStorage.addPinLastBoardId = boardId;
|
||||||
data.board.pins.push(body);
|
store.do("addPinModal.close");
|
||||||
|
|
||||||
|
if ( boardId == "new" && !window.socketConnected ){
|
||||||
|
store.do("load.boards");
|
||||||
|
}
|
||||||
|
} catch (e){
|
||||||
|
window.alert("Error uploading images.");
|
||||||
|
console.error("Error uploading images: ", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
window.localStorage.addPinLastBoardId = boardId;
|
} else {
|
||||||
store.do("addPinModal.close");
|
|
||||||
|
|
||||||
// if we don't have a listening socket, we need to trigger our own update
|
let postData = {
|
||||||
if ( boardId == "new" && !window.socketConnected ){
|
boardId: boardId,
|
||||||
store.do("load.boards");
|
newBoardName: data.addPinModal.newBoardName,
|
||||||
|
imageUrl: data.addPinModal.imageUrl,
|
||||||
|
siteUrl: data.addPinModal.siteUrl,
|
||||||
|
description: data.addPinModal.description
|
||||||
|
};
|
||||||
|
|
||||||
|
let res = await fetch('api/pins', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': "application/json"
|
||||||
|
},
|
||||||
|
body: JSON.stringify(postData)
|
||||||
|
});
|
||||||
|
|
||||||
|
if ( res.status == 200 ){
|
||||||
|
|
||||||
|
let body = await res.json();
|
||||||
|
if ( data.board && data.board.id == boardId ){
|
||||||
|
data.board.pins.push(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.localStorage.addPinLastBoardId = boardId;
|
||||||
|
store.do("addPinModal.close");
|
||||||
|
|
||||||
|
// if we don't have a listening socket, we need to trigger our own update
|
||||||
|
if ( boardId == "new" && !window.socketConnected ){
|
||||||
|
store.do("load.boards");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
store.do("loader.hide");
|
store.do("loader.hide");
|
||||||
|
|
||||||
|
|
@ -101,6 +137,54 @@ app.addGetter('addPinModal.isValid', (data) => {
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
app.addSetter('addPinModal.fileChosen', (data, target) => {
|
||||||
|
|
||||||
|
let file = target.files[0];
|
||||||
|
|
||||||
|
// check type
|
||||||
|
if ( file.type != "image/jpeg" && file.type != "image/png" ){
|
||||||
|
|
||||||
|
window.alert("Unsupported file type. JPEG and PNG images are supported.");
|
||||||
|
console.log("Unsupported file type: " + file.type);
|
||||||
|
|
||||||
|
document.getElementById("fileInput").value = "";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// check size
|
||||||
|
if ( file.size >= 26214400 ){
|
||||||
|
window.alert("File size exceeds the 25MB limit.");
|
||||||
|
console.log("File size exceeds the 25MB limit. size=" + file.size);
|
||||||
|
document.getElementById("fileInput").value = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
else {
|
||||||
|
|
||||||
|
let imageUrl = window.URL.createObjectURL(file);
|
||||||
|
|
||||||
|
data.addPinModal.uploadFile = file;
|
||||||
|
data.addPinModal.previewImageUrl = imageUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
app.addSetter('addPinModal.removeUploadFile', (data, target) => {
|
||||||
|
data.addPinModal.uploadFile = null;
|
||||||
|
data.addPinModal.previewImageUrl = null;
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
app.addSetter('addPinModal.disableDidYouKnowDragAndDropMessage', (data) => {
|
||||||
|
data.addPinModal.didYouKnowDragAndDropMessageDisabled = true;
|
||||||
|
window.localStorage.addPinModal_didYouKnowDragAndDropMessageDisabled = "true";
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener("input", (evt) => {
|
||||||
|
if ( evt.target.id == "fileInput" ){
|
||||||
|
store.do("addPinModal.fileChosen", evt.target);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
app.addComponent('addPinModal', (store) => { return new Reef("#addPinModal", {
|
app.addComponent('addPinModal', (store) => { return new Reef("#addPinModal", {
|
||||||
|
|
||||||
store: store,
|
store: store,
|
||||||
|
|
@ -109,8 +193,11 @@ app.addComponent('addPinModal', (store) => { return new Reef("#addPinModal", {
|
||||||
let imagePlaceholder = 'data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22300%22%20height%3D%22300%22%3E%3Crect%20x%3D%222%22%20y%3D%222%22%20width%3D%22300%22%20height%3D%22300%22%20style%3D%22fill%3A%23dedede%3B%22%2F%3E%3Ctext%20x%3D%2250%25%22%20y%3D%2250%25%22%20font-size%3D%2218%22%20text-anchor%3D%22middle%22%20alignment-baseline%3D%22middle%22%20font-family%3D%22monospace%2C%20sans-serif%22%20fill%3D%22%23555555%22%3Eimage%3C%2Ftext%3E%3C%2Fsvg%3E';
|
let imagePlaceholder = 'data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22300%22%20height%3D%22300%22%3E%3Crect%20x%3D%222%22%20y%3D%222%22%20width%3D%22300%22%20height%3D%22300%22%20style%3D%22fill%3A%23dedede%3B%22%2F%3E%3Ctext%20x%3D%2250%25%22%20y%3D%2250%25%22%20font-size%3D%2218%22%20text-anchor%3D%22middle%22%20alignment-baseline%3D%22middle%22%20font-family%3D%22monospace%2C%20sans-serif%22%20fill%3D%22%23555555%22%3Eimage%3C%2Ftext%3E%3C%2Fsvg%3E';
|
||||||
|
|
||||||
let options = "";
|
let options = "";
|
||||||
|
|
||||||
for ( let i = 0; i < data.boards.length; ++i ){
|
for ( let i = 0; i < data.boards.length; ++i ){
|
||||||
options += `<option value="${data.boards[i].id}">${data.boards[i].name}</option>`;
|
if ( data.showHiddenBoards || !data.boards[i].hidden ){
|
||||||
|
options += `<option value="${data.boards[i].id}">${data.boards[i].name}</option>`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let newBoardField = '';
|
let newBoardField = '';
|
||||||
|
|
@ -134,6 +221,17 @@ app.addComponent('addPinModal', (store) => { return new Reef("#addPinModal", {
|
||||||
<button class="delete" aria-label="close" data-onclick="addPinModal.close"></button>
|
<button class="delete" aria-label="close" data-onclick="addPinModal.close"></button>
|
||||||
</header>
|
</header>
|
||||||
<section class="modal-card-body">
|
<section class="modal-card-body">
|
||||||
|
${ !data.addPinModal.didYouKnowDragAndDropMessageDisabled ? /*html*/`
|
||||||
|
<div class="message is-success">
|
||||||
|
<div class="message-header">
|
||||||
|
<p>Did you know?</p>
|
||||||
|
<button type="button" class="delete" aria-label="delete" label="Don't show again" data-onclick="addPinModal.disableDidYouKnowDragAndDropMessage"></button>
|
||||||
|
</div>
|
||||||
|
<div class="message-body">
|
||||||
|
Did you know? You can now upload files to an existing board by drag-and-drop!
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
` : ''}
|
||||||
<div class="add-pin-flex">
|
<div class="add-pin-flex">
|
||||||
<div class="add-pin-flex-left">
|
<div class="add-pin-flex-left">
|
||||||
<img id="add-pin-modal-img" src="${data.addPinModal.previewImageUrl ? data.addPinModal.previewImageUrl : imagePlaceholder}" />
|
<img id="add-pin-modal-img" src="${data.addPinModal.previewImageUrl ? data.addPinModal.previewImageUrl : imagePlaceholder}" />
|
||||||
|
|
@ -141,6 +239,8 @@ app.addComponent('addPinModal', (store) => { return new Reef("#addPinModal", {
|
||||||
<div class="add-pin-flex-right">
|
<div class="add-pin-flex-right">
|
||||||
<form>
|
<form>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="label">Board</label>
|
<label class="label">Board</label>
|
||||||
<div class="select">
|
<div class="select">
|
||||||
|
|
@ -153,13 +253,31 @@ app.addComponent('addPinModal', (store) => { return new Reef("#addPinModal", {
|
||||||
|
|
||||||
${newBoardField}
|
${newBoardField}
|
||||||
|
|
||||||
|
${ data.addPinModal.uploadFile ? /*html*/`
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">Image File</label>
|
||||||
|
<div class="control">
|
||||||
|
<span>${data.addPinModal.uploadFile.name}</span>
|
||||||
|
<button type="button" class="delete" aria-label="remove" data-onclick="addPinModal.removeUploadFile"></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
` :
|
||||||
|
/*html*/`
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="label">Image Url</label>
|
<label class="label">Image Url</label>
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<input class="input" type="text" data-bind="addPinModal.imageUrl" data-onblur="addPinModal.updatePreview"/>
|
<input class="input" type="text" data-bind="addPinModal.imageUrl" data-onblur="addPinModal.updatePreview" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<lable class="label">or choose file</lable>
|
||||||
|
<div class="control">
|
||||||
|
<input class="input" type="file" id="fileInput" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`}
|
||||||
|
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="label">Website Url</label>
|
<label class="label">Website Url</label>
|
||||||
<div class="control">
|
<div class="control">
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ app.addGetter('editPinModal.isValid', (data) => {
|
||||||
|
|
||||||
let pin = getPinById(data.editPinModal.pin.id);
|
let pin = getPinById(data.editPinModal.pin.id);
|
||||||
|
|
||||||
if ( pin.siteUrl == data.editPinModal.pin.siteUrl &&
|
if ( pin && pin.siteUrl == data.editPinModal.pin.siteUrl &&
|
||||||
pin.description == data.editPinModal.pin.description &&
|
pin.description == data.editPinModal.pin.description &&
|
||||||
pin.boardId == data.editPinModal.pin.boardId ){
|
pin.boardId == data.editPinModal.pin.boardId ){
|
||||||
return false;
|
return false;
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@ app.addComponent('navbar', (store) => { return new Reef("#navbar", {
|
||||||
}
|
}
|
||||||
|
|
||||||
let settingsItem = "";
|
let settingsItem = "";
|
||||||
if (data.user.admin == 1){
|
if (data.user && data.user.admin == 1){
|
||||||
settingsItem = `
|
settingsItem = `
|
||||||
<a class="navbar-item has-text-right" href="./settings">
|
<a class="navbar-item has-text-right" href="./settings">
|
||||||
<span>tinypin settings</span>
|
<span>tinypin settings</span>
|
||||||
|
|
|
||||||
|
|
@ -146,6 +146,7 @@ Reef.databind = function(reef){
|
||||||
} else {
|
} else {
|
||||||
elem.checked = false;
|
elem.checked = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
elem.value = val;
|
elem.value = val;
|
||||||
}
|
}
|
||||||
|
|
@ -170,7 +171,7 @@ Reef.databind = function(reef){
|
||||||
}
|
}
|
||||||
|
|
||||||
// multiple selects need special handling
|
// multiple selects need special handling
|
||||||
if ( target.tagName == 'SELECT' && target.matches("[multiple]") ){
|
else if ( target.tagName == 'SELECT' && target.matches("[multiple]") ){
|
||||||
val = [];
|
val = [];
|
||||||
let options = target.querySelectorAll("option");
|
let options = target.querySelectorAll("option");
|
||||||
for ( let i = 0; i < options.length; ++i ){
|
for ( let i = 0; i < options.length; ++i ){
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,11 @@ function getPinIndexById(id){
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPinById(id){
|
function getPinById(id){
|
||||||
return store.data.board.pins[getPinIndexById(id)];
|
try{
|
||||||
|
return store.data.board.pins[getPinIndexById(id)];
|
||||||
|
} catch (e){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function sleep(ms){ return new Promise((resolve) => setTimeout(resolve, ms)); }
|
async function sleep(ms){ return new Promise((resolve) => setTimeout(resolve, ms)); }
|
||||||
|
|
|
||||||
264
package-lock.json
generated
264
package-lock.json
generated
|
|
@ -14,6 +14,7 @@
|
||||||
"eta": "^1.12.1",
|
"eta": "^1.12.1",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"express-ws": "^4.0.0",
|
"express-ws": "^4.0.0",
|
||||||
|
"multer": "^1.4.3",
|
||||||
"node-fetch": "^2.6.1",
|
"node-fetch": "^2.6.1",
|
||||||
"sharp": "^0.27.0",
|
"sharp": "^0.27.0",
|
||||||
"yargs": "^16.2.0"
|
"yargs": "^16.2.0"
|
||||||
|
|
@ -69,6 +70,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
|
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
|
||||||
},
|
},
|
||||||
|
"node_modules/append-field": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-HjRA6RXwsSA9I3SOeO3XubW0PlY="
|
||||||
|
},
|
||||||
"node_modules/aproba": {
|
"node_modules/aproba": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
|
||||||
|
|
@ -326,6 +332,44 @@
|
||||||
"ieee754": "^1.1.13"
|
"ieee754": "^1.1.13"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/buffer-from": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="
|
||||||
|
},
|
||||||
|
"node_modules/busboy": {
|
||||||
|
"version": "0.2.14",
|
||||||
|
"resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz",
|
||||||
|
"integrity": "sha1-bCpiLvz0fFe7vh4qnDetNseSVFM=",
|
||||||
|
"dependencies": {
|
||||||
|
"dicer": "0.2.5",
|
||||||
|
"readable-stream": "1.1.x"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/busboy/node_modules/isarray": {
|
||||||
|
"version": "0.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
|
||||||
|
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
|
||||||
|
},
|
||||||
|
"node_modules/busboy/node_modules/readable-stream": {
|
||||||
|
"version": "1.1.14",
|
||||||
|
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
|
||||||
|
"integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
|
||||||
|
"dependencies": {
|
||||||
|
"core-util-is": "~1.0.0",
|
||||||
|
"inherits": "~2.0.1",
|
||||||
|
"isarray": "0.0.1",
|
||||||
|
"string_decoder": "~0.10.x"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/busboy/node_modules/string_decoder": {
|
||||||
|
"version": "0.10.31",
|
||||||
|
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
|
||||||
|
"integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
|
||||||
|
},
|
||||||
"node_modules/bytes": {
|
"node_modules/bytes": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
|
||||||
|
|
@ -428,6 +472,20 @@
|
||||||
"simple-swizzle": "^0.2.2"
|
"simple-swizzle": "^0.2.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/concat-stream": {
|
||||||
|
"version": "1.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
|
||||||
|
"integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
|
||||||
|
"engines": [
|
||||||
|
"node >= 0.8"
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"buffer-from": "^1.0.0",
|
||||||
|
"inherits": "^2.0.3",
|
||||||
|
"readable-stream": "^2.2.2",
|
||||||
|
"typedarray": "^0.0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/console-control-strings": {
|
"node_modules/console-control-strings": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
|
||||||
|
|
@ -541,6 +599,39 @@
|
||||||
"node": ">=0.10"
|
"node": ">=0.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/dicer": {
|
||||||
|
"version": "0.2.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz",
|
||||||
|
"integrity": "sha1-WZbAhrszIYyBLAkL3cCc0S+stw8=",
|
||||||
|
"dependencies": {
|
||||||
|
"readable-stream": "1.1.x",
|
||||||
|
"streamsearch": "0.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/dicer/node_modules/isarray": {
|
||||||
|
"version": "0.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
|
||||||
|
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
|
||||||
|
},
|
||||||
|
"node_modules/dicer/node_modules/readable-stream": {
|
||||||
|
"version": "1.1.14",
|
||||||
|
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
|
||||||
|
"integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
|
||||||
|
"dependencies": {
|
||||||
|
"core-util-is": "~1.0.0",
|
||||||
|
"inherits": "~2.0.1",
|
||||||
|
"isarray": "0.0.1",
|
||||||
|
"string_decoder": "~0.10.x"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/dicer/node_modules/string_decoder": {
|
||||||
|
"version": "0.10.31",
|
||||||
|
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
|
||||||
|
"integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
|
||||||
|
},
|
||||||
"node_modules/ee-first": {
|
"node_modules/ee-first": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||||
|
|
@ -909,6 +1000,17 @@
|
||||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
||||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
|
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
|
||||||
},
|
},
|
||||||
|
"node_modules/mkdirp": {
|
||||||
|
"version": "0.5.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
|
||||||
|
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"minimist": "^1.2.5"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"mkdirp": "bin/cmd.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/mkdirp-classic": {
|
"node_modules/mkdirp-classic": {
|
||||||
"version": "0.5.3",
|
"version": "0.5.3",
|
||||||
"resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
|
"resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
|
||||||
|
|
@ -919,6 +1021,24 @@
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||||
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
||||||
},
|
},
|
||||||
|
"node_modules/multer": {
|
||||||
|
"version": "1.4.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/multer/-/multer-1.4.3.tgz",
|
||||||
|
"integrity": "sha512-np0YLKncuZoTzufbkM6wEKp68EhWJXcU6fq6QqrSwkckd2LlMgd1UqhUJLj6NS/5sZ8dE8LYDWslsltJznnXlg==",
|
||||||
|
"dependencies": {
|
||||||
|
"append-field": "^1.0.0",
|
||||||
|
"busboy": "^0.2.11",
|
||||||
|
"concat-stream": "^1.5.2",
|
||||||
|
"mkdirp": "^0.5.4",
|
||||||
|
"object-assign": "^4.1.1",
|
||||||
|
"on-finished": "^2.3.0",
|
||||||
|
"type-is": "^1.6.4",
|
||||||
|
"xtend": "^4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/napi-build-utils": {
|
"node_modules/napi-build-utils": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz",
|
||||||
|
|
@ -1341,6 +1461,14 @@
|
||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/streamsearch": {
|
||||||
|
"version": "0.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz",
|
||||||
|
"integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/string_decoder": {
|
"node_modules/string_decoder": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||||
|
|
@ -1451,6 +1579,11 @@
|
||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/typedarray": {
|
||||||
|
"version": "0.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
|
||||||
|
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
|
||||||
|
},
|
||||||
"node_modules/unpipe": {
|
"node_modules/unpipe": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
||||||
|
|
@ -1562,6 +1695,14 @@
|
||||||
"async-limiter": "~1.0.0"
|
"async-limiter": "~1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/xtend": {
|
||||||
|
"version": "4.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
||||||
|
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/y18n": {
|
"node_modules/y18n": {
|
||||||
"version": "5.0.5",
|
"version": "5.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.5.tgz",
|
||||||
|
|
@ -1679,6 +1820,11 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"append-field": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-HjRA6RXwsSA9I3SOeO3XubW0PlY="
|
||||||
|
},
|
||||||
"aproba": {
|
"aproba": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
|
||||||
|
|
@ -1869,6 +2015,43 @@
|
||||||
"ieee754": "^1.1.13"
|
"ieee754": "^1.1.13"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"buffer-from": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="
|
||||||
|
},
|
||||||
|
"busboy": {
|
||||||
|
"version": "0.2.14",
|
||||||
|
"resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz",
|
||||||
|
"integrity": "sha1-bCpiLvz0fFe7vh4qnDetNseSVFM=",
|
||||||
|
"requires": {
|
||||||
|
"dicer": "0.2.5",
|
||||||
|
"readable-stream": "1.1.x"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"isarray": {
|
||||||
|
"version": "0.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
|
||||||
|
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
|
||||||
|
},
|
||||||
|
"readable-stream": {
|
||||||
|
"version": "1.1.14",
|
||||||
|
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
|
||||||
|
"integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
|
||||||
|
"requires": {
|
||||||
|
"core-util-is": "~1.0.0",
|
||||||
|
"inherits": "~2.0.1",
|
||||||
|
"isarray": "0.0.1",
|
||||||
|
"string_decoder": "~0.10.x"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"string_decoder": {
|
||||||
|
"version": "0.10.31",
|
||||||
|
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
|
||||||
|
"integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"bytes": {
|
"bytes": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
|
||||||
|
|
@ -1955,6 +2138,17 @@
|
||||||
"simple-swizzle": "^0.2.2"
|
"simple-swizzle": "^0.2.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"concat-stream": {
|
||||||
|
"version": "1.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
|
||||||
|
"integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
|
||||||
|
"requires": {
|
||||||
|
"buffer-from": "^1.0.0",
|
||||||
|
"inherits": "^2.0.3",
|
||||||
|
"readable-stream": "^2.2.2",
|
||||||
|
"typedarray": "^0.0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"console-control-strings": {
|
"console-control-strings": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
|
||||||
|
|
@ -2038,6 +2232,38 @@
|
||||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
|
||||||
"integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups="
|
"integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups="
|
||||||
},
|
},
|
||||||
|
"dicer": {
|
||||||
|
"version": "0.2.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz",
|
||||||
|
"integrity": "sha1-WZbAhrszIYyBLAkL3cCc0S+stw8=",
|
||||||
|
"requires": {
|
||||||
|
"readable-stream": "1.1.x",
|
||||||
|
"streamsearch": "0.1.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"isarray": {
|
||||||
|
"version": "0.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
|
||||||
|
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
|
||||||
|
},
|
||||||
|
"readable-stream": {
|
||||||
|
"version": "1.1.14",
|
||||||
|
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
|
||||||
|
"integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
|
||||||
|
"requires": {
|
||||||
|
"core-util-is": "~1.0.0",
|
||||||
|
"inherits": "~2.0.1",
|
||||||
|
"isarray": "0.0.1",
|
||||||
|
"string_decoder": "~0.10.x"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"string_decoder": {
|
||||||
|
"version": "0.10.31",
|
||||||
|
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
|
||||||
|
"integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"ee-first": {
|
"ee-first": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||||
|
|
@ -2318,6 +2544,14 @@
|
||||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
||||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
|
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
|
||||||
},
|
},
|
||||||
|
"mkdirp": {
|
||||||
|
"version": "0.5.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
|
||||||
|
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
|
||||||
|
"requires": {
|
||||||
|
"minimist": "^1.2.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
"mkdirp-classic": {
|
"mkdirp-classic": {
|
||||||
"version": "0.5.3",
|
"version": "0.5.3",
|
||||||
"resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
|
"resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
|
||||||
|
|
@ -2328,6 +2562,21 @@
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||||
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
||||||
},
|
},
|
||||||
|
"multer": {
|
||||||
|
"version": "1.4.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/multer/-/multer-1.4.3.tgz",
|
||||||
|
"integrity": "sha512-np0YLKncuZoTzufbkM6wEKp68EhWJXcU6fq6QqrSwkckd2LlMgd1UqhUJLj6NS/5sZ8dE8LYDWslsltJznnXlg==",
|
||||||
|
"requires": {
|
||||||
|
"append-field": "^1.0.0",
|
||||||
|
"busboy": "^0.2.11",
|
||||||
|
"concat-stream": "^1.5.2",
|
||||||
|
"mkdirp": "^0.5.4",
|
||||||
|
"object-assign": "^4.1.1",
|
||||||
|
"on-finished": "^2.3.0",
|
||||||
|
"type-is": "^1.6.4",
|
||||||
|
"xtend": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"napi-build-utils": {
|
"napi-build-utils": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz",
|
||||||
|
|
@ -2652,6 +2901,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
|
||||||
"integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
|
"integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
|
||||||
},
|
},
|
||||||
|
"streamsearch": {
|
||||||
|
"version": "0.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz",
|
||||||
|
"integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo="
|
||||||
|
},
|
||||||
"string_decoder": {
|
"string_decoder": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||||
|
|
@ -2740,6 +2994,11 @@
|
||||||
"mime-types": "~2.1.24"
|
"mime-types": "~2.1.24"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"typedarray": {
|
||||||
|
"version": "0.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
|
||||||
|
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
|
||||||
|
},
|
||||||
"unpipe": {
|
"unpipe": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
||||||
|
|
@ -2826,6 +3085,11 @@
|
||||||
"async-limiter": "~1.0.0"
|
"async-limiter": "~1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"xtend": {
|
||||||
|
"version": "4.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
||||||
|
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="
|
||||||
|
},
|
||||||
"y18n": {
|
"y18n": {
|
||||||
"version": "5.0.5",
|
"version": "5.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.5.tgz",
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
"eta": "^1.12.1",
|
"eta": "^1.12.1",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"express-ws": "^4.0.0",
|
"express-ws": "^4.0.0",
|
||||||
|
"multer": "^1.4.3",
|
||||||
"node-fetch": "^2.6.1",
|
"node-fetch": "^2.6.1",
|
||||||
"sharp": "^0.27.0",
|
"sharp": "^0.27.0",
|
||||||
"yargs": "^16.2.0"
|
"yargs": "^16.2.0"
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,36 @@ function sendAuthCookie(res, c){
|
||||||
res.cookie('s', tokenUtils.encrypt(c), {maxAge: 315569520000}); // 10 years
|
res.cookie('s', tokenUtils.encrypt(c), {maxAge: 315569520000}); // 10 years
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function maybeGetUser(req){
|
||||||
|
|
||||||
|
if ( !req.cookies ){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we made it this far, we're eady to check for the cookie
|
||||||
|
let s = req.cookies.s;
|
||||||
|
|
||||||
|
// TODO: should probably check if the user's access has been revoked,
|
||||||
|
// but we currently don't allow deleting users anyway. A key rotation would
|
||||||
|
// be the other solution, but that would log out all users and require new tokens
|
||||||
|
// to be created.
|
||||||
|
if ( s ){
|
||||||
|
try {
|
||||||
|
s = tokenUtils.decrypt(s);
|
||||||
|
if ( s.i && s.u ){
|
||||||
|
return {
|
||||||
|
id: s.i,
|
||||||
|
name: s.u
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log(`error parsing cookie: `, err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = async (req, res, next) => {
|
module.exports = async (req, res, next) => {
|
||||||
|
|
||||||
// we will also accept the auth token in the x-api-key header
|
// we will also accept the auth token in the x-api-key header
|
||||||
|
|
@ -60,6 +90,13 @@ module.exports = async (req, res, next) => {
|
||||||
next();
|
next();
|
||||||
return;
|
return;
|
||||||
} if ( req.method == "GET" && req.originalUrl == "/login" ){
|
} if ( req.method == "GET" && req.originalUrl == "/login" ){
|
||||||
|
|
||||||
|
|
||||||
|
if ( maybeGetUser(req) ){
|
||||||
|
res.redirect("./");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
console.log("login");
|
console.log("login");
|
||||||
// res.type("html").sendFile(path.resolve('./templates/login.html'));
|
// res.type("html").sendFile(path.resolve('./templates/login.html'));
|
||||||
res.render("login", { registerEnabled: dao.getProperty("registerEnabled") });
|
res.render("login", { registerEnabled: dao.getProperty("registerEnabled") });
|
||||||
|
|
@ -135,26 +172,27 @@ module.exports = async (req, res, next) => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we made it this far, we're eady to check for the cookie
|
// // if we made it this far, we're eady to check for the cookie
|
||||||
let s = req.cookies.s;
|
// let s = req.cookies.s;
|
||||||
|
|
||||||
// TODO: should probably check if the user's access has been revoked,
|
// // TODO: should probably check if the user's access has been revoked,
|
||||||
// but we currently don't allow deleting users anyway. A key rotation would
|
// // but we currently don't allow deleting users anyway. A key rotation would
|
||||||
// be the other solution, but that would log out all users and require new tokens
|
// // be the other solution, but that would log out all users and require new tokens
|
||||||
// to be created.
|
// // to be created.
|
||||||
if ( s ){
|
// if ( s ){
|
||||||
try {
|
// try {
|
||||||
s = tokenUtils.decrypt(s);
|
// s = tokenUtils.decrypt(s);
|
||||||
if ( s.i && s.u ){
|
// if ( s.i && s.u ){
|
||||||
req.user = {
|
// req.user = {
|
||||||
id: s.i,
|
// id: s.i,
|
||||||
name: s.u
|
// name: s.u
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
} catch (err) {
|
// } catch (err) {
|
||||||
console.error(`error parsing cookie: `, err);
|
// console.error(`error parsing cookie: `, err);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
req.user = maybeGetUser(req);
|
||||||
|
|
||||||
if ( !req.user ){
|
if ( !req.user ){
|
||||||
res.redirect("/login");
|
res.redirect("/login");
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
const yargs = require('yargs');
|
const yargs = require('yargs');
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
const bodyParser = require('body-parser');
|
const bodyParser = require('body-parser');
|
||||||
|
const multer = require("multer")
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const cookieParser = require('cookie-parser');
|
const cookieParser = require('cookie-parser');
|
||||||
const tokenUtil = require('./token-utils.js');
|
const tokenUtil = require('./token-utils.js');
|
||||||
|
|
@ -10,6 +11,9 @@ const imageUtils = require('./image-utils.js');
|
||||||
var eta = require("eta");
|
var eta = require("eta");
|
||||||
const tokenUtils = require('./token-utils.js');
|
const tokenUtils = require('./token-utils.js');
|
||||||
|
|
||||||
|
// consider using temp files, but we're going to limit the size so should be ok
|
||||||
|
const upload = multer({storage:multer.memoryStorage(), limits: {fileSize: 26214400, files: 1}}); // 1 - 25MB file
|
||||||
|
|
||||||
module.exports = async () => {
|
module.exports = async () => {
|
||||||
|
|
||||||
process.on('SIGINT', () => {
|
process.on('SIGINT', () => {
|
||||||
|
|
@ -340,6 +344,7 @@ module.exports = async () => {
|
||||||
res.status(200).send({t: token});
|
res.status(200).send({t: token});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// handle raw uploads for pin creation
|
||||||
app.post("/up", async (req, res) => {
|
app.post("/up", async (req, res) => {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
@ -358,7 +363,7 @@ module.exports = async () => {
|
||||||
board = dao.createBoard(req.user.id, boardName, 0);
|
board = dao.createBoard(req.user.id, boardName, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
let pin = dao.createPin(req.user.id, board.id, null, null, null, null, image.original.height, image.original.width, image.thumbnail.height, image.thumbnailWidth);
|
let pin = dao.createPin(req.user.id, board.id, null, null, null, null, image.original.height, image.original.width, image.thumbnail.height, image.thumbnail.height);
|
||||||
|
|
||||||
await imageUtils.saveImage(req.user.id, pin.id, image);
|
await imageUtils.saveImage(req.user.id, pin.id, image);
|
||||||
|
|
||||||
|
|
@ -371,6 +376,38 @@ module.exports = async () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// handle multipart uploads for pin creation
|
||||||
|
app.post("/multiup", upload.single('file'), async(req, res) => {
|
||||||
|
try {
|
||||||
|
|
||||||
|
let image = await imageUtils.processImage(req.file.buffer); // file.buffer only works with the Memory store for multer.
|
||||||
|
|
||||||
|
let boardId = req.body.boardId;
|
||||||
|
|
||||||
|
let board = null;
|
||||||
|
|
||||||
|
if ( boardId == "new" ){
|
||||||
|
board = dao.createBoard(req.user.id, req.body.newBoardName, 0);
|
||||||
|
} else {
|
||||||
|
board = dao.getBoard(req.user.id, boardId);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(image);
|
||||||
|
|
||||||
|
let pin = dao.createPin(req.user.id, board.id, null, req.body.siteUrl, req.body.description, null, image.original.height, image.original.width, image.thumbnail.height, image.thumbnail.height);
|
||||||
|
|
||||||
|
await imageUtils.saveImage(req.user.id, pin.id, image);
|
||||||
|
|
||||||
|
broadcast(req.user.id, {updateBoard:board.id});
|
||||||
|
res.status(200).send(pin);
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
console.log(`Error creating pin via multipart upload`, err);
|
||||||
|
res.status(500).send(SERVER_ERROR);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
app.get("/api/apikey", (req,res) => {
|
app.get("/api/apikey", (req,res) => {
|
||||||
let s = req.cookies['s'];
|
let s = req.cookies['s'];
|
||||||
console.log("s=" + s);
|
console.log("s=" + s);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue