Started adding maintainer modes for managing duplicates, deletions, trash, etc.
Signed-off-by: James Ketrenos <james_git@ketrenos.com>
This commit is contained in:
parent
a930d27860
commit
064e228226
@ -46,7 +46,7 @@
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div class="layout vertical start">
|
<div class="layout vertical start">
|
||||||
<div>[[item.name]]</div>
|
<div>[[item.name]] ([[item.id]])</div>
|
||||||
<div>[[item.taken]]</div>
|
<div>[[item.taken]]</div>
|
||||||
<div on-tap="_pathTap">[[item.path]]</div>
|
<div on-tap="_pathTap">[[item.path]]</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -310,6 +310,8 @@
|
|||||||
<!--paper-tab tab="time"><paper-icon-button icon="date-range"></paper-icon-button></paper-tab-->
|
<!--paper-tab tab="time"><paper-icon-button icon="date-range"></paper-icon-button></paper-tab-->
|
||||||
<paper-tab tab="memories"><paper-icon-button icon="today"></paper-icon-button></paper-tab>
|
<paper-tab tab="memories"><paper-icon-button icon="today"></paper-icon-button></paper-tab>
|
||||||
<paper-tab tab="albums"><paper-icon-button icon="folder"></paper-icon-button></paper-tab>
|
<paper-tab tab="albums"><paper-icon-button icon="folder"></paper-icon-button></paper-tab>
|
||||||
|
<paper-tab hidden$="[[!user.maintainer]]" tab="duplicates"><paper-icon-button icon="compare-arrows"></paper-icon-button></paper-tab>
|
||||||
|
<paper-tab hidden$="[[!user.maintainer]]" tab="trash"><paper-icon-button icon="delete"></paper-icon-button></paper-tab>
|
||||||
</paper-tabs>
|
</paper-tabs>
|
||||||
<iron-pages id="pages" attr-for-selected="id" selected="[[mode]]" fallback-selection="loading">
|
<iron-pages id="pages" attr-for-selected="id" selected="[[mode]]" fallback-selection="loading">
|
||||||
<div id="loading"></div>
|
<div id="loading"></div>
|
||||||
@ -336,6 +338,21 @@
|
|||||||
<div>On <b>[[memoryDate]]</b>, there have been <b>[[add(thumbnails.length,pendingPhotos.length)]]</b> photos taken over <b>[[years.length]]</b> year(s).</div>
|
<div>On <b>[[memoryDate]]</b>, there have been <b>[[add(thumbnails.length,pendingPhotos.length)]]</b> photos taken over <b>[[years.length]]</b> year(s).</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div hidden$="[[!user.maintainer]]" id="trash" class="flex layout vertical">
|
||||||
|
<div><b>Trash</b></div>
|
||||||
|
<div>There are <b>[[add(thumbnails.length,pendingPhotos.length)]]</b> photos in the trash.</div>
|
||||||
|
<div>Do you want to purge the trash?</div>
|
||||||
|
</div>
|
||||||
|
<div hidden$="[[!user.maintainer]]" id="duplicates" class="flex layout vertical">
|
||||||
|
<div><b>Duplicate names</b></div>
|
||||||
|
<div>There are <b>[[add(thumbnails.length,pendingPhotos.length)]]</b> photos which may be duplicates
|
||||||
|
based on either their name.</div>
|
||||||
|
<div>Look for duplicates in each file-name pair. If they are not the same,
|
||||||
|
tap <iron-icon icon="text-format"></iron-icon> on the photo and that one will be renamed
|
||||||
|
on the server by adding four letters from the image's signature to the name.</div>
|
||||||
|
<div>If they are duplicates, you can tap <iron-icon icon="delete"></iron-icon> to move the
|
||||||
|
photo to the trash.</div>
|
||||||
|
</div>
|
||||||
<div id="albums" class="flex layout vertical">
|
<div id="albums" class="flex layout vertical">
|
||||||
<template is="dom-repeat" items="[[breadcrumb(path)]]">
|
<template is="dom-repeat" items="[[breadcrumb(path)]]">
|
||||||
<div tabindex="0" on-tap="loadPath">[[item.name]] /</div>
|
<div tabindex="0" on-tap="loadPath">[[item.name]] /</div>
|
||||||
@ -468,10 +485,6 @@
|
|||||||
type: Object,
|
type: Object,
|
||||||
value: null
|
value: null
|
||||||
},
|
},
|
||||||
order: {
|
|
||||||
type: String,
|
|
||||||
value: "by-date"
|
|
||||||
},
|
|
||||||
loading: Boolean,
|
loading: Boolean,
|
||||||
pendingPhotos: {
|
pendingPhotos: {
|
||||||
type: Array,
|
type: Array,
|
||||||
@ -486,10 +499,6 @@
|
|||||||
value: true,
|
value: true,
|
||||||
reflectToAttribute: true
|
reflectToAttribute: true
|
||||||
},
|
},
|
||||||
showAlbums: {
|
|
||||||
type: Boolean,
|
|
||||||
computed: "shouldShowAlbums(order)"
|
|
||||||
},
|
|
||||||
path: {
|
path: {
|
||||||
type: String,
|
type: String,
|
||||||
value: ""
|
value: ""
|
||||||
@ -575,10 +584,6 @@
|
|||||||
this.date = "2016-" + window.moment(Math.ceil(Math.random() * 365), "DDD").format("MM-DD");
|
this.date = "2016-" + window.moment(Math.ceil(Math.random() * 365), "DDD").format("MM-DD");
|
||||||
},
|
},
|
||||||
|
|
||||||
shouldShowAlbums: function(order) {
|
|
||||||
return order == "by-album";
|
|
||||||
},
|
|
||||||
|
|
||||||
login: function(event) {
|
login: function(event) {
|
||||||
if (this.loading) {
|
if (this.loading) {
|
||||||
return;
|
return;
|
||||||
@ -787,7 +792,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (top) {
|
if (top && (this.mode == "memories" || this.mode == "albums")) {
|
||||||
var photo = top.item;
|
var photo = top.item;
|
||||||
this.$.pager.style.opacity = 1;
|
this.$.pager.style.opacity = 1;
|
||||||
var date = window.moment(new Date((photo.taken || photo.modified || photo.added).replace(/T.*/, " GMT")));
|
var date = window.moment(new Date((photo.taken || photo.modified || photo.added).replace(/T.*/, " GMT")));
|
||||||
@ -1091,58 +1096,117 @@
|
|||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
var dateBlock = this.root.querySelector("#date-" + datetime), thumbnails;
|
if (this.mode == "duplicates") {
|
||||||
if (!dateBlock) {
|
var name = photo.filename.replace(/\./g, "_"),
|
||||||
dateBlock = document.createElement("div");
|
nameBlock = this.root.querySelector("#name-" + name), thumbnails;
|
||||||
dateBlock.id = "date-" + datetime;
|
if (!nameBlock) {
|
||||||
dateBlock.classList.add("date-line");
|
nameBlock = document.createElement("div");
|
||||||
dateBlock.classList.add("layout");
|
nameBlock.id = "name-" + name;
|
||||||
dateBlock.classList.add("vertical");
|
nameBlock.classList.add("date-line");
|
||||||
var header = document.createElement("div");
|
nameBlock.classList.add("layout");
|
||||||
header.classList.add("header");
|
nameBlock.classList.add("vertical");
|
||||||
header.classList.add("layout");
|
var header = document.createElement("div");
|
||||||
header.classList.add("center");
|
header.classList.add("header");
|
||||||
header.classList.add("horizontal");
|
header.classList.add("layout");
|
||||||
var div = document.createElement("div");
|
header.classList.add("center");
|
||||||
div.classList.add("date");
|
header.classList.add("horizontal");
|
||||||
if (this.mode == "memories") {
|
var div = document.createElement("div");
|
||||||
var ago = window.moment(datetime, "YYYY-MM-DD").fromNow();
|
div.classList.add("date");
|
||||||
ago = ago.charAt(0).toUpperCase() + ago.substr(1);
|
div.textContent = photo.filename;
|
||||||
div.innerHTML = "<b>" + ago + "</b><br><span style='font-size: 0.8em;font-weight: normal'>" + window.moment(datetime, "YYYY-MM-DD").calendar(null, { sameElse: "MMM DD, YYYY" }).replace(/ at.*/, "") + "</span>";
|
Polymer.dom(nameBlock).appendChild(header);
|
||||||
|
Polymer.dom(header).appendChild(div);
|
||||||
|
thumbnails = document.createElement("div");
|
||||||
|
thumbnails.classList.add("thumbnails");
|
||||||
|
thumbnails.classList.add("layout");
|
||||||
|
thumbnails.classList.add("horizontal");
|
||||||
|
thumbnails.classList.add("wrap");
|
||||||
|
Polymer.dom(nameBlock).appendChild(thumbnails);
|
||||||
|
Polymer.dom(this.$.thumbnails).appendChild(nameBlock);
|
||||||
} else {
|
} else {
|
||||||
div.textContent = window.moment(datetime, "YYYY-MM-DD").calendar(null, { sameElse: "MMM DD, YYYY" }).replace(/ at.*/, "");
|
thumbnails = Polymer.dom(nameBlock).querySelector(".thumbnails");
|
||||||
|
}
|
||||||
|
} else if (this.mode == "trash") {
|
||||||
|
var trashBlock = this.root.querySelector("#trash-images"), thumbnails;
|
||||||
|
if (!trashBlock) {
|
||||||
|
trashBlock = document.createElement("div");
|
||||||
|
trashBlock.id = "trash-images";
|
||||||
|
trashBlock.classList.add("date-line");
|
||||||
|
trashBlock.classList.add("layout");
|
||||||
|
trashBlock.classList.add("vertical");
|
||||||
|
var header = document.createElement("div");
|
||||||
|
header.classList.add("header");
|
||||||
|
header.classList.add("layout");
|
||||||
|
header.classList.add("center");
|
||||||
|
header.classList.add("horizontal");
|
||||||
|
var div = document.createElement("div");
|
||||||
|
div.classList.add("date");
|
||||||
|
div.textContent = "Trash";
|
||||||
|
Polymer.dom(trashBlock).appendChild(header);
|
||||||
|
Polymer.dom(header).appendChild(div);
|
||||||
|
thumbnails = document.createElement("div");
|
||||||
|
thumbnails.classList.add("thumbnails");
|
||||||
|
thumbnails.classList.add("layout");
|
||||||
|
thumbnails.classList.add("horizontal");
|
||||||
|
thumbnails.classList.add("wrap");
|
||||||
|
Polymer.dom(trashBlock).appendChild(thumbnails);
|
||||||
|
Polymer.dom(this.$.thumbnails).appendChild(trashBlock);
|
||||||
|
} else {
|
||||||
|
thumbnails = Polymer.dom(trashBlock).querySelector(".thumbnails");
|
||||||
}
|
}
|
||||||
Polymer.dom(dateBlock).appendChild(header);
|
|
||||||
Polymer.dom(header).appendChild(div);
|
|
||||||
thumbnails = document.createElement("div");
|
|
||||||
thumbnails.classList.add("thumbnails");
|
|
||||||
thumbnails.classList.add("layout");
|
|
||||||
thumbnails.classList.add("horizontal");
|
|
||||||
thumbnails.classList.add("wrap");
|
|
||||||
Polymer.dom(dateBlock).appendChild(thumbnails);
|
|
||||||
Polymer.dom(this.$.thumbnails).appendChild(dateBlock);
|
|
||||||
} else {
|
} else {
|
||||||
thumbnails = Polymer.dom(dateBlock).querySelector(".thumbnails");
|
var dateBlock = this.root.querySelector("#date-" + datetime), thumbnails;
|
||||||
}
|
if (!dateBlock) {
|
||||||
|
dateBlock = document.createElement("div");
|
||||||
|
dateBlock.id = "date-" + datetime;
|
||||||
|
dateBlock.classList.add("date-line");
|
||||||
|
dateBlock.classList.add("layout");
|
||||||
|
dateBlock.classList.add("vertical");
|
||||||
|
var header = document.createElement("div");
|
||||||
|
header.classList.add("header");
|
||||||
|
header.classList.add("layout");
|
||||||
|
header.classList.add("center");
|
||||||
|
header.classList.add("horizontal");
|
||||||
|
var div = document.createElement("div");
|
||||||
|
div.classList.add("date");
|
||||||
|
if (this.mode == "memories") {
|
||||||
|
var ago = window.moment(datetime, "YYYY-MM-DD").fromNow();
|
||||||
|
ago = ago.charAt(0).toUpperCase() + ago.substr(1);
|
||||||
|
div.innerHTML = "<b>" + ago + "</b><br><span style='font-size: 0.8em;font-weight: normal'>" + window.moment(datetime, "YYYY-MM-DD").calendar(null, { sameElse: "MMM DD, YYYY" }).replace(/ at.*/, "") + "</span>";
|
||||||
|
} else {
|
||||||
|
div.textContent = window.moment(datetime, "YYYY-MM-DD").calendar(null, { sameElse: "MMM DD, YYYY" }).replace(/ at.*/, "");
|
||||||
|
}
|
||||||
|
Polymer.dom(dateBlock).appendChild(header);
|
||||||
|
Polymer.dom(header).appendChild(div);
|
||||||
|
thumbnails = document.createElement("div");
|
||||||
|
thumbnails.classList.add("thumbnails");
|
||||||
|
thumbnails.classList.add("layout");
|
||||||
|
thumbnails.classList.add("horizontal");
|
||||||
|
thumbnails.classList.add("wrap");
|
||||||
|
Polymer.dom(dateBlock).appendChild(thumbnails);
|
||||||
|
Polymer.dom(this.$.thumbnails).appendChild(dateBlock);
|
||||||
|
} else {
|
||||||
|
thumbnails = Polymer.dom(dateBlock).querySelector(".thumbnails");
|
||||||
|
}
|
||||||
|
|
||||||
if (this.order == "by-album") {
|
if (this.mode == "albums") {
|
||||||
if (lastPath != photo.path) {
|
if (lastPath != photo.path) {
|
||||||
lastPath = photo.path;
|
lastPath = photo.path;
|
||||||
var albumBlock = document.createElement("div");
|
var albumBlock = document.createElement("div");
|
||||||
albumBlock.classList.add("album-line");
|
albumBlock.classList.add("album-line");
|
||||||
albumBlock.classList.add("layout");
|
albumBlock.classList.add("layout");
|
||||||
albumBlock.classList.add("horizontal");
|
albumBlock.classList.add("horizontal");
|
||||||
var trail = this.breadcrumb(lastPath);
|
var trail = this.breadcrumb(lastPath);
|
||||||
trail.forEach(function(crumb) {
|
trail.forEach(function(crumb) {
|
||||||
var div = document.createElement("div");
|
var div = document.createElement("div");
|
||||||
div.path = crumb.path;
|
div.path = crumb.path;
|
||||||
div.textContent = crumb.name + " /";
|
div.textContent = crumb.name + " /";
|
||||||
div.addEventListener("tap", this.pathTapped.bind(this));
|
div.addEventListener("tap", this.pathTapped.bind(this));
|
||||||
albumBlock.appendChild(div);
|
albumBlock.appendChild(div);
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
|
|
||||||
var header = dateBlock.querySelector(".header");
|
var header = dateBlock.querySelector(".header");
|
||||||
Polymer.dom(header).appendChild(albumBlock);
|
Polymer.dom(header).appendChild(albumBlock);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1188,9 +1252,6 @@
|
|||||||
if (start) {
|
if (start) {
|
||||||
params.next = start;
|
params.next = start;
|
||||||
}
|
}
|
||||||
if (this.sortOrder) {
|
|
||||||
params.sort = this.sortOrder;
|
|
||||||
}
|
|
||||||
for (var key in params) {
|
for (var key in params) {
|
||||||
if (query == "") {
|
if (query == "") {
|
||||||
query = "?";
|
query = "?";
|
||||||
@ -1205,7 +1266,7 @@
|
|||||||
path = mode;
|
path = mode;
|
||||||
if (mode == "time") {
|
if (mode == "time") {
|
||||||
path = "";
|
path = "";
|
||||||
} else {
|
} else if (mode == "memories") {
|
||||||
path = "memories/" + (this.date.replace(/2016-/, "") || "");
|
path = "memories/" + (this.date.replace(/2016-/, "") || "");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1323,6 +1384,7 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
userChanged: function(user) {
|
userChanged: function(user) {
|
||||||
|
console.log("User: ", user);
|
||||||
if (!this.firstRequest) {
|
if (!this.firstRequest) {
|
||||||
this.mode = "loading";
|
this.mode = "loading";
|
||||||
return;
|
return;
|
||||||
@ -1394,6 +1456,7 @@
|
|||||||
this.$.toast.setAttribute("error", true);
|
this.$.toast.setAttribute("error", true);
|
||||||
this.$.toast.updateStyles();
|
this.$.toast.updateStyles();
|
||||||
this.$.toast.show();
|
this.$.toast.show();
|
||||||
|
console.log(xhr.responseText);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ config.get("smtp.sender");
|
|||||||
|
|
||||||
let basePath = config.get("basePath");
|
let basePath = config.get("basePath");
|
||||||
|
|
||||||
let photoDB = null, userDB = null
|
let photoDB = null, userDB = null;
|
||||||
|
|
||||||
basePath = "/" + basePath.replace(/^\/+/, "").replace(/\/+$/, "") + "/";
|
basePath = "/" + basePath.replace(/^\/+/, "").replace(/\/+$/, "") + "/";
|
||||||
if (basePath == "//") {
|
if (basePath == "//") {
|
||||||
@ -53,6 +53,26 @@ app.use(bodyParser.urlencoded({
|
|||||||
extended: false
|
extended: false
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
/* ******************************************************************************* */
|
||||||
|
/* Logging */
|
||||||
|
/* This runs before after cookie parsing, but before routes. If we set
|
||||||
|
* immediate: true on the morgan options, it happens before cookie parsing */
|
||||||
|
morgan.token('remote-user', function (req) {
|
||||||
|
return req.user ? req.user.username : "N/A";
|
||||||
|
});
|
||||||
|
|
||||||
|
const logSkipPaths = new RegExp("^" + basePath + "(" + [
|
||||||
|
"bower_components",
|
||||||
|
].join(")|(") + ")");
|
||||||
|
console.log(logSkipPaths);
|
||||||
|
app.use(morgan('common', {
|
||||||
|
skip: function (req) {
|
||||||
|
return logSkipPaths.exec(req.originalUrl);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
/* Logging */
|
||||||
|
/* ******************************************************************************* */
|
||||||
|
|
||||||
/* body-parser does not support text/*, so add support for that here */
|
/* body-parser does not support text/*, so add support for that here */
|
||||||
app.use(function(req, res, next){
|
app.use(function(req, res, next){
|
||||||
if (!req.is('text/*')) {
|
if (!req.is('text/*')) {
|
||||||
@ -110,7 +130,8 @@ const templates = {
|
|||||||
].join("\n")
|
].join("\n")
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Allow loading of the app w/out being logged in */
|
/* Look for action-token URLs and process; this does not require a user to be logged
|
||||||
|
* in */
|
||||||
app.use(basePath, function(req, res, next) {
|
app.use(basePath, function(req, res, next) {
|
||||||
let match = req.url.match(/^\/([0-9a-f]+)$/);
|
let match = req.url.match(/^\/([0-9a-f]+)$/);
|
||||||
if (!match) {
|
if (!match) {
|
||||||
@ -197,9 +218,12 @@ app.use(basePath, function(req, res, next) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/* Allow loading of the app w/out being logged in */
|
||||||
app.use(basePath, index);
|
app.use(basePath, index);
|
||||||
|
|
||||||
app.use(basePath + "api/v1/users", require("./routes/users"));
|
/* Allow access to the 'users' API w/out being logged in */
|
||||||
|
const users = require("./routes/users");
|
||||||
|
app.use(basePath + "api/v1/users", users.router);
|
||||||
|
|
||||||
app.use(function(err, req, res, next) {
|
app.use(function(err, req, res, next) {
|
||||||
res.status(err.status || 500).json({
|
res.status(err.status || 500).json({
|
||||||
@ -208,68 +232,19 @@ app.use(function(err, req, res, next) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
/* Everything below here requires a successful authentication */
|
/* Check authentication */
|
||||||
|
|
||||||
app.use(basePath, function(req, res, next) {
|
app.use(basePath, function(req, res, next) {
|
||||||
if (!req.session || !req.session.userId) {
|
return users.getSessionUser(req).then(function(user) {
|
||||||
return res.status(401).send("Unauthorized");
|
req.user = user;
|
||||||
}
|
return next();
|
||||||
|
}).catch(function(error) {
|
||||||
if (req.session.userId == "LDAP") {
|
return res.status(401).send(error);
|
||||||
if (req.session.ldapUser) {
|
|
||||||
req.user = req.session.ldapUser;
|
|
||||||
return next();
|
|
||||||
}
|
|
||||||
req.session.userId = null;
|
|
||||||
req.session.ldapUser = null;
|
|
||||||
return res.status(401).send("Invalid LDAP session");
|
|
||||||
}
|
|
||||||
|
|
||||||
let query = "SELECT uid AS username,displayName,mailVerified,authenticated,memberSince AS name,mail " +
|
|
||||||
"FROM users WHERE id=:id";
|
|
||||||
|
|
||||||
return userDB.sequelize.query(query, {
|
|
||||||
replacements: {
|
|
||||||
id: req.session.userId
|
|
||||||
},
|
|
||||||
type: userDB.Sequelize.QueryTypes.SELECT,
|
|
||||||
raw: true
|
|
||||||
}).then(function(results) {
|
|
||||||
if (results.length != 1) {
|
|
||||||
return res.status(401).send("Invalid account");
|
|
||||||
}
|
|
||||||
|
|
||||||
req.user = results[0];
|
|
||||||
if (!req.user.authenticated) {
|
|
||||||
return res.status(401).send("Accout not authenticated.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!req.user.mailVerified) {
|
|
||||||
return res.status(401).send("Account mail not verified.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!config.has("restrictions")) {
|
|
||||||
return next();
|
|
||||||
}
|
|
||||||
|
|
||||||
let allowed = config.get("restrictions");
|
|
||||||
if (!Array.isArray(allowed)) {
|
|
||||||
allowed = [ allowed ];
|
|
||||||
}
|
|
||||||
for (let i = 0; i < allowed.length; i++) {
|
|
||||||
if (allowed[i] == req.user.username) {
|
|
||||||
return next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
console.log("Unauthorized (logged in) access by user: " + req.user.username);
|
|
||||||
return res.status(401).send("Unauthorized");
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/* Everything below here requires a successful authentication */
|
||||||
app.use(basePath, express.static(picturesPath, { index: false }));
|
app.use(basePath, express.static(picturesPath, { index: false }));
|
||||||
|
|
||||||
app.use(morgan("common"));
|
|
||||||
|
|
||||||
app.use(basePath + "api/v1/photos", require("./routes/photos"));
|
app.use(basePath + "api/v1/photos", require("./routes/photos"));
|
||||||
app.use(basePath + "api/v1/days", require("./routes/days"));
|
app.use(basePath + "api/v1/days", require("./routes/days"));
|
||||||
app.use(basePath + "api/v1/albums", require("./routes/albums"));
|
app.use(basePath + "api/v1/albums", require("./routes/albums"));
|
||||||
|
@ -60,6 +60,7 @@ function init() {
|
|||||||
taken: Sequelize.DATE,
|
taken: Sequelize.DATE,
|
||||||
width: Sequelize.INTEGER,
|
width: Sequelize.INTEGER,
|
||||||
height: Sequelize.INTEGER,
|
height: Sequelize.INTEGER,
|
||||||
|
size: Sequelize.INTEGER,
|
||||||
duplicate: {
|
duplicate: {
|
||||||
type: Sequelize.BOOLEAN,
|
type: Sequelize.BOOLEAN,
|
||||||
defaultValue: 0
|
defaultValue: 0
|
||||||
|
@ -23,7 +23,6 @@ const router = express.Router();
|
|||||||
* photo info
|
* photo info
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
router.get("/memories/:date?", function(req, res/*, next*/) {
|
router.get("/memories/:date?", function(req, res/*, next*/) {
|
||||||
let limit = parseInt(req.query.limit) || 50,
|
let limit = parseInt(req.query.limit) || 50,
|
||||||
id, cursor, index;
|
id, cursor, index;
|
||||||
@ -96,6 +95,54 @@ router.get("/memories/:date?", function(req, res/*, next*/) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
router.get("/duplicates", function(req, res/*, next*/) {
|
||||||
|
let replacements = {};
|
||||||
|
|
||||||
|
return photoDB.sequelize.query(
|
||||||
|
"SELECT filename,COUNT(*) AS count FROM photos WHERE photos.duplicate!=1 AND photos.deleted!=1 GROUP BY filename HAVING count > 1", {
|
||||||
|
replacements: replacements,
|
||||||
|
type: photoDB.Sequelize.QueryTypes.SELECT,
|
||||||
|
raw: true
|
||||||
|
}).then(function(duplicates) {
|
||||||
|
let filenames = [];
|
||||||
|
duplicates.forEach(function(duplicate) {
|
||||||
|
filenames.push(duplicate.filename);
|
||||||
|
});
|
||||||
|
|
||||||
|
replacements.filenames = filenames;
|
||||||
|
|
||||||
|
return photoDB.sequelize.query(
|
||||||
|
"SELECT photos.*,albums.path AS path,photohashes.hash,(albums.path || photos.filename) AS filepath FROM photos " +
|
||||||
|
"LEFT JOIN albums ON albums.id=photos.albumId " +
|
||||||
|
"LEFT JOIN photohashes ON photohashes.photoId=photos.id " +
|
||||||
|
"WHERE filename IN (:filenames) ORDER BY photos.filename", {
|
||||||
|
replacements: replacements,
|
||||||
|
type: photoDB.Sequelize.QueryTypes.SELECT,
|
||||||
|
raw: true
|
||||||
|
}).then(function(photos) {
|
||||||
|
return res.status(200).json({
|
||||||
|
items: photos
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}).catch(function(error) {
|
||||||
|
return Promise.reject(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get("/trash", function(req, res/*, next*/) {
|
||||||
|
return photoDB.sequelize.query(
|
||||||
|
"SELECT photos.*,albums.path AS path,(albums.path || photos.filename) AS filepath FROM photos " +
|
||||||
|
"LEFT JOIN albums ON albums.id=photos.albumId " +
|
||||||
|
"WHERE deleted=1 ORDER BY photos.filename", {
|
||||||
|
type: photoDB.Sequelize.QueryTypes.SELECT,
|
||||||
|
raw: true
|
||||||
|
}).then(function(photos) {
|
||||||
|
return res.status(200).json({
|
||||||
|
items: photos
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
router.get("/*", function(req, res/*, next*/) {
|
router.get("/*", function(req, res/*, next*/) {
|
||||||
let limit = parseInt(req.query.limit) || 50,
|
let limit = parseInt(req.query.limit) || 50,
|
||||||
id, cursor, index;
|
id, cursor, index;
|
||||||
@ -167,4 +214,5 @@ router.get("/*", function(req, res/*, next*/) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
@ -31,8 +31,10 @@ require("../db/users").then(function(db) {
|
|||||||
router.get("/", function(req, res/*, next*/) {
|
router.get("/", function(req, res/*, next*/) {
|
||||||
console.log("/users/");
|
console.log("/users/");
|
||||||
return getSessionUser(req).then(function(user) {
|
return getSessionUser(req).then(function(user) {
|
||||||
req.user = user;
|
return res.status(200).send(user);
|
||||||
return res.status(200).send(req.user);
|
}).catch(function(error) {
|
||||||
|
console.log("User not logged in: " + error);
|
||||||
|
return res.status(200).send({});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -185,33 +187,87 @@ router.post("/create", function(req, res) {
|
|||||||
return res.status(200).send(user);
|
return res.status(200).send(user);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
}).catch(function(error) {
|
||||||
|
console.log("Error creating account: ", error);
|
||||||
|
return res.status(401).send(error);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const getSessionUser = function(req) {
|
const getSessionUser = function(req) {
|
||||||
if (!req.session.userId) {
|
return Promise.resolve().then(function() {
|
||||||
return Promise.resolve({});
|
if (!req.session || !req.session.userId) {
|
||||||
}
|
throw "Unauthorized. You must be logged in.";
|
||||||
|
|
||||||
if (req.session.userId == "LDAP") {
|
|
||||||
return Promise.resolve(req.session.ldapUser);
|
|
||||||
}
|
|
||||||
|
|
||||||
let query = "SELECT " +
|
|
||||||
"uid AS username,displayName,mailVerified,authenticated,memberSince AS name,mail " +
|
|
||||||
"FROM users WHERE id=:id";
|
|
||||||
return userDB.sequelize.query(query, {
|
|
||||||
replacements: {
|
|
||||||
id: req.session.userId
|
|
||||||
},
|
|
||||||
type: userDB.Sequelize.QueryTypes.SELECT,
|
|
||||||
raw: true
|
|
||||||
}).then(function(results) {
|
|
||||||
if (results.length != 1) {
|
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return results[0];
|
if (req.session.userId == "LDAP") {
|
||||||
|
if (req.session.ldapUser) {
|
||||||
|
return req.session.ldapUser;
|
||||||
|
}
|
||||||
|
req.session.userId = null;
|
||||||
|
req.session.ldapUser = null;
|
||||||
|
throw "Invalid LDAP session";
|
||||||
|
}
|
||||||
|
|
||||||
|
let query = "SELECT " +
|
||||||
|
"uid AS username,displayName,mailVerified,authenticated,memberSince AS name,mail " +
|
||||||
|
"FROM users WHERE id=:id";
|
||||||
|
|
||||||
|
return userDB.sequelize.query(query, {
|
||||||
|
replacements: {
|
||||||
|
id: req.session.userId
|
||||||
|
},
|
||||||
|
type: userDB.Sequelize.QueryTypes.SELECT,
|
||||||
|
raw: true
|
||||||
|
}).then(function(results) {
|
||||||
|
if (results.length != 1) {
|
||||||
|
throw "Invalid account.";
|
||||||
|
}
|
||||||
|
|
||||||
|
req.user = results[0];
|
||||||
|
if (!req.user.authenticated) {
|
||||||
|
throw "Accout not authenticated.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!req.user.mailVerified) {
|
||||||
|
throw "Account mail not verified.";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}).then(function(user) {
|
||||||
|
if (!config.has("restrictions")) {
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
let allowed = config.get("restrictions");
|
||||||
|
if (!Array.isArray(allowed)) {
|
||||||
|
allowed = [ allowed ];
|
||||||
|
}
|
||||||
|
for (let i = 0; i < allowed.length; i++) {
|
||||||
|
if (allowed[i] == user.username) {
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log("Unauthorized (logged in) access by user: " + req.user.username);
|
||||||
|
throw "Unauthorized access attempt to restricted album.";
|
||||||
|
}).then(function(user) {
|
||||||
|
if (config.has("maintainers")) {
|
||||||
|
let maintainers = config.get("maintainers");
|
||||||
|
if (maintainers.indexOf(user.username) != -1) {
|
||||||
|
user.maintainer = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return user;
|
||||||
|
}).then(function(user) {
|
||||||
|
/* Strip out any fields that shouldn't be there. The allowed fields are: */
|
||||||
|
let allowed = [
|
||||||
|
"maintainer", "username", "displayName", "mailVerified", "authenticated", "name", "mail"
|
||||||
|
];
|
||||||
|
for (let field in user) {
|
||||||
|
if (allowed.indexOf(field) == -1) {
|
||||||
|
delete user[field];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return user;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -293,4 +349,7 @@ router.get("/logout", function(req, res) {
|
|||||||
res.status(200).send({});
|
res.status(200).send({});
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = {
|
||||||
|
router,
|
||||||
|
getSessionUser
|
||||||
|
};
|
||||||
|
@ -198,7 +198,7 @@ function processBlock(items) {
|
|||||||
|
|
||||||
/* Sort to newest files to be processed first */
|
/* Sort to newest files to be processed first */
|
||||||
processing.sort(function(a, b) {
|
processing.sort(function(a, b) {
|
||||||
return a.stats.mtime - b.stats.mtime;
|
return b.stats.mtime - a.stats.mtime;
|
||||||
});
|
});
|
||||||
|
|
||||||
let toProcess = processing.length, lastMessage = moment();
|
let toProcess = processing.length, lastMessage = moment();
|
||||||
@ -349,7 +349,7 @@ function processBlock(items) {
|
|||||||
});
|
});
|
||||||
}).then(function() {
|
}).then(function() {
|
||||||
return photoDB.sequelize.query("UPDATE photos SET " +
|
return photoDB.sequelize.query("UPDATE photos SET " +
|
||||||
"added=:added,modified=:modified,taken=:taken,width=:width,height=:height,scanned=CURRENT_TIMESTAMP " +
|
"added=:added,modified=:modified,taken=:taken,width=:width,height=:height,size=:size,scanned=CURRENT_TIMESTAMP " +
|
||||||
"WHERE id=:id", {
|
"WHERE id=:id", {
|
||||||
replacements: asset,
|
replacements: asset,
|
||||||
});
|
});
|
||||||
@ -520,6 +520,7 @@ function scanDir(parent, path) {
|
|||||||
mtime: stats.mtime,
|
mtime: stats.mtime,
|
||||||
ctime: stats.ctime
|
ctime: stats.ctime
|
||||||
},
|
},
|
||||||
|
size: stats.size,
|
||||||
album: album
|
album: album
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -588,8 +589,8 @@ function findOrUpdateDBAsset(transaction, asset) {
|
|||||||
}).then(function(results) {
|
}).then(function(results) {
|
||||||
if (results.length == 0) {
|
if (results.length == 0) {
|
||||||
return photoDB.sequelize.query("INSERT INTO photos " +
|
return photoDB.sequelize.query("INSERT INTO photos " +
|
||||||
"(albumId,filename,name) " +
|
"(albumId,filename,name,size) " +
|
||||||
"VALUES(:albumId,:filename,:name)", {
|
"VALUES(:albumId,:filename,:name,:size)", {
|
||||||
replacements: asset,
|
replacements: asset,
|
||||||
transaction: transaction
|
transaction: transaction
|
||||||
}).spread(function(results, metadata) {
|
}).spread(function(results, metadata) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user