Browse Source

some restructuring, deliting collections now works

master
mort 8 years ago
parent
commit
4b3b34a465

+ 1
- 0
.gitignore View File

@@ -4,4 +4,5 @@ npm-debug.log
imgs
!imgs/.placeholder
.currentRun
.sessions
favicon.ico

+ 29
- 11
lib/context.js View File

@@ -1,14 +1,35 @@
var formidable = require("formidable");
var crypto = require("crypto");
var zlib = require("zlib");
var fs = require("fs");
var preprocess = require("./preprocess.js");

var sessions = {};
var sessionTimeouts = {};

if (fs.existsSync(".sessions")) {
sessions = JSON.parse(fs.readFileSync(".sessions", "utf8"));

//Set appropriate timeouts.
Object.keys(sessions).forEach(function(i) {
sessionTimeouts[i] = setTimeout(function() {
delete sessions[i];
}, 1000*60*10);
});
}

function saveSessions() {
fs.writeFileSync(".sessions", JSON.stringify(sessions));
process.exit();
}

process.on("SIGINT", saveSessions);
process.on("SIGTERM", saveSessions);

function templatify(str, args, ctx, env) {
env.url = ctx.req.url;

str = preprocess(str, {
var env = {
session: function(key) {
ctx.cachable = false;
return ctx.session[key];
@@ -19,9 +40,11 @@ function templatify(str, args, ctx, env) {
noescape: args,
env: env,
template: function(key) {
return ctx.template(key);
return ctx.template(key, args);
}
});
}

str = preprocess(str, env);

return str;
}
@@ -63,19 +86,14 @@ module.exports = function(options) {

sessions[key] = {};
this.res.setHeader("Set-Cookie", "session="+key);

//Delete session after a while
setTimeout(function() {
delete sessions[key];
}, this.conf.session_timeout);
}
this.session = sessions[key];

//Reset session delete timer
if (this.session._timeout)
clearTimeout(this.session._timeout);
if (sessionTimeouts[key])
clearTimeout(sessionTimeouts[key]);

this.session._timeout = setTimeout(function() {
sessionTimeouts[key] = setTimeout(function() {
delete sessions[key];
}, this.conf.session_timeout);
}

+ 2
- 1
package.json View File

@@ -22,6 +22,7 @@
"pg": "^4.4.0",
"scrypt": "^4.0.7",
"uglify-js": "^2.4.24",
"uglifycss": "0.0.15"
"uglifycss": "0.0.15",
"wrench": "^1.5.8"
}
}

+ 11
- 1
server.js View File

@@ -3,6 +3,7 @@ var https = require("https");
var fs = require("fs");
var domain = require("domain");
var zlib = require("zlib");
var wrench = require("wrench");
var loader = require("./lib/loader.js");
var pg = require("pg");
var Context = require("./lib/context.js");
@@ -49,6 +50,7 @@ var endpoints = {
"/api/template": "api/template.node.js",
"/api/image_create": "api/image_create.node.js",
"/api/collection_create": "api/collection_create.node.js",
"/api/collection_delete": "api/collection_delete.node.js",
"/api/account_create": "api/account_create.node.js",
"/api/account_login": "api/account_login.node.js",
"/api/account_logout": "api/account_logout.node.js",
@@ -147,11 +149,19 @@ function purgeCollections() {
db.query(
"DELETE FROM collections "+
"WHERE user_id IS NULL "+
"AND date_created < NOW() - INTERVAL '"+timeout+"'",
"AND date_created < NOW() - INTERVAL '"+timeout+"' "+
"RETURNING id",
function(err, res) {
if (err)
throw err;

res.rows.forEach(function(row) {
wrench.rmdirSyncRecursive(
conf.dir.imgs+"/"+
row.id
);
});

if (res.rowCount > 0) {
console.log(
"Deleted "+res.rowCount+" collections "+

+ 2
- 2
templates/collection.html View File

@@ -1,5 +1,5 @@
<div class="collection">
<div class="collection" data-id="{{arg#id}}">
<span class="date-created">{{arg#date_created}}</span>
<a class="name" href="/view?{{arg#id}}">{{arg#name}}</a>
{{arg#own_profile ? '<button class="delete btn btn-default">X</button>' : ''}}
{{arg#own_profile ? template#delete-button : ''}}
</div>

+ 1
- 0
templates/delete-button.html View File

@@ -0,0 +1 @@
<button class="delete btn btn-default" data-id="{{arg#id}}">X</button>

+ 3
- 3
templates/image.html View File

@@ -1,8 +1,8 @@
<div class="image small-width bordered">
<div class="title">{{arg#title}}</div>
<a href="/i?{{arg#id}}.{{arg#extension}}">
<img class="img-rounded" src="/i?{{arg#id}}.{{arg#extension}}">
<a href="/i?{{arg#collection}}/{{arg#id}}.{{arg#extension}}">
<img class="img-rounded" src="/i?{{arg#collection}}/{{arg#id}}.{{arg#extension}}">
</a>
<div class="description">{{arg#description}}</div>
<input class="url" type="text" value="{{conf#base_url}}/i?{{arg#id}}.{{arg#extension}}" onclick="select()">
<input class="url" type="text" value="{{conf#base_url}}/i?{{arg#collection}}/{{arg#id}}.{{arg#extension}}" onclick="select()">
</div>

+ 12
- 3
web/api/collection_create.node.js View File

@@ -1,3 +1,5 @@
var fs = require("fs");

module.exports = function(ctx) {
ctx.getPostData(function(err, data) {
if (err)
@@ -16,10 +18,17 @@ module.exports = function(ctx) {
if (err)
return ctx.fail(err);

ctx.session.collectionId = res.rows[0].id;
var id = res.rows[0].id;

fs.mkdir(ctx.conf.dir.imgs+"/"+id, function(err) {
if (err)
return ctx.fail(err);

ctx.session.lastCollectionId = id;

ctx.succeed({
id: res.rows[0].id
ctx.succeed({
id: id
});
});
}
}

+ 56
- 0
web/api/collection_delete.node.js View File

@@ -0,0 +1,56 @@
var wrench = require("wrench");

module.exports = function(ctx) {
ctx.getPostData(function(err, data) {
var id = parseInt(data.id);

if (isNaN(id))
return ctx.fail("Invalid ID.");

if (id === ctx.session.lastCollectionId)
return deleteQuery();

if (!ctx.session.loggedIn)
return ctx.fail("You're not logged in.");

ctx.db.query(
"SELECT FROM collections "+
"WHERE user_id = $1",
[ctx.session.userId],
function(err, res) {
if (err)
return ctx.fail(err);

if (res.rows[0] === undefined)
return ctx.fail("You don't own that collection.");

deleteQuery();
}
);
});

function deleteQuery() {
ctx.db.query(
"DELETE FROM collections "+
"WHERE id = $1",
[ctx.postData.data.id],
queryCallback
);
}

function queryCallback(err, res) {
if (err)
return ctx.fail(err);

try {
wrench.rmdirSyncRecursive(
ctx.conf.dir.imgs+"/"+
ctx.postData.data.id
);
} catch (err) {
return ctx.fail(err);
}

ctx.succeed();
}
}

+ 9
- 3
web/api/image_create.node.js View File

@@ -10,7 +10,7 @@ module.exports = function(ctx) {

data.collectionId = parseInt(data.collectionId);

if (data.collectionId !== ctx.session.collectionId)
if (data.lastCollectionId !== ctx.session.collectionId)
return ctx.fail("You don't own that collection.");

//We want all extensions to be lower case.
@@ -19,7 +19,7 @@ module.exports = function(ctx) {
ctx.db.query(
"INSERT INTO images (name, description, extension, collection_id) "+
"VALUES ($1, $2, $3, $4) "+
"RETURNING id",
"RETURNING id, collection_id",
[data.name, data.description, data.extension, data.collectionId],
queryCallback
);
@@ -30,10 +30,16 @@ module.exports = function(ctx) {
return ctx.fail(err);

var id = res.rows[0].id;
var collectionId = res.rows[0].collection_id;
var file = ctx.postData.files.file;

var readStream = fs.createReadStream(file.path);
var writeStream = fs.createWriteStream(ctx.conf.dir.imgs+"/"+id);
var writeStream = fs.createWriteStream(
ctx.conf.dir.imgs+"/"+
collectionId+"/"+
id
);

readStream.pipe(writeStream);

readStream.on("end", function() {

+ 1
- 0
web/global.css View File

@@ -41,6 +41,7 @@ form label {
border: 1px solid #CCC;
border-radius: 4px;
padding: 10px !important;
margin-bottom: 10px;
}

.title {

+ 9
- 3
web/i/index.node.js View File

@@ -1,8 +1,10 @@
var fs = require("fs");

module.exports = function(ctx) {
var id = ctx.query.replace(/\..*/, "");
if (!id)
var q = ctx.query.replace(/\..*/, "");
var collection = parseInt(q.split("/")[0]);
var id = parseInt(q.split("/")[1]);
if (!id || !collection)
return ctx.err404();

ctx.res.setHeader(
@@ -10,7 +12,11 @@ module.exports = function(ctx) {
"public, max-age="+ctx.conf.cache_max_age_images
);

var readStream = fs.createReadStream(ctx.conf.dir.imgs+"/"+id);
var readStream = fs.createReadStream(
ctx.conf.dir.imgs+"/"+
collection+"/"+
id
);
readStream.pipe(ctx.res);

readStream.on("error", function(err){

+ 13
- 4
web/index/script.js View File

@@ -74,12 +74,21 @@ $(document).on("ready", function() {

var collectionId = res.id;

//Go to collection once files are uploaded
//Go to collection once files are uploaded, or
//delete the collection if it failed
var a = util.async(files.length, function(res) {
if (res.error)
util.redirect("/", 5000);
else
if (res.error) {
util.api("collection_delete", {
id: collectionId
}, function(err, res) {
if (err)
return util.error(err);

util.redirect("/", 5000);
});
} else {
util.redirect("/view?"+collectionId);
}
});

//Loop through files, uploading them

+ 23
- 0
web/profile/script.js View File

@@ -2,4 +2,27 @@ $(document).on("ready", function() {
$("#collections .date-created").each(function() {
this.innerHTML = util.dateToString(new Date(this.innerHTML));
});

var collections = [];
$("#collections .collection").each(function() {
collections[this.getAttribute("data-id")] = $(this);
});

$("#collections .delete").on("click", function(evt) {
var id = evt.target.getAttribute("data-id");
var collection = collections[id];
var name = collection.children(".name").html();

if (!confirm("Are you sure you want to delete collection "+name+"?"))
return;

util.api("collection_delete", {
id: id
}, function(err, res) {
if (err)
return util.error(err);

collection.remove();
});
});
});

+ 2
- 2
web/profile/style.css View File

@@ -20,8 +20,8 @@
}

.collection .date-created {
width: 45%;
width: 190px;
}
.collection .name {
width: calc(55% - 43px);
width: calc(100% - 43px - 190px);
}

+ 1
- 0
web/view/index.node.js View File

@@ -23,6 +23,7 @@ module.exports = function(ctx) {
res.rows.forEach(function(row) {
images += ctx.template("image", {
title: row.name,
collection: id,
id: row.id,
extension: row.extension,
description: row.description

Loading…
Cancel
Save