Browse Source

can now delete and create slides. Auth tokens now also use cookies instead of headers.

master
mortie 7 years ago
parent
commit
6fc9762241

exampleSlides/1/index.md → exampleSlides/00001/index.md View File


exampleSlides/1/meta.json → exampleSlides/00001/meta.json View File


exampleSlides/2/index.md → exampleSlides/00002/index.md View File


exampleSlides/3/index.md → exampleSlides/00003/index.md View File


exampleSlides/4/image.svg → exampleSlides/00004/image.svg View File


exampleSlides/4/index.md → exampleSlides/00004/index.md View File


+ 95
- 1
js/admin.js View File

@@ -5,9 +5,25 @@ var formidable = require("formidable");
var crypto = require("crypto");

var basepath = "/admin/api/";
var slideshow;

exports.init = function(_slideshow) {
slideshow = _slideshow;
}

var tokens = {};

function pad(str, n) {
if (str.length >= n)
return str;

var missing = str.length;
for (var i = 0; i < n - missing; ++i)
str = "0" + str;

return str;
}

// Used in every method handler to make sure the correct arguments are provided
function hasargs(query, respond, expected) {
var missing = [];
@@ -130,12 +146,79 @@ var methods = {
});

form.parse(req, (err, fields, files) => {
if (err)
return respond(err);
});

form.on("error", err => {
respond(err);
});

form.on("end", () => {
respond();
});
},

// Create a slide
// Lots of synchronous fs stuff, we don't want races
slide_create: function(query, conf, req, respond) {
var dirs;
try {
dirs = fs.readdirSync(conf.slides);
} catch (err) {
return respond(err);
}

dirs = dirs.sort();
var biggest = dirs[dirs.length - 1];
var newId = pad((parseInt(biggest) + 1).toString(), biggest.length);

var path = pathlib.join(conf.slides, newId);
try {
fs.mkdirSync(path);
fs.writeFileSync(pathlib.join(path, "index.md"), "");
} catch (err) {
return respond(err);
}

slideshow.updateSlides();

respond(null, newId);
},

// Delete a slide
// Also synchronous fs stuff
slide_delete: function(query, conf, req, respond) {
if (!hasargs(query, respond, [ "slide" ])) return;

var path = pathlib.join(conf.slides, query.slide);
var files;
try {
files = fs.readdirSync(path);
} catch (err) {
return respond(err);
}

for (var f of files) {
try {
fs.unlinkSync(pathlib.join(path, f));
} catch (err) {
return respond(err);
}
}

try {
fs.rmdirSync(path);
} catch (err) {
return respond(err);
}

respond();
}
}

exports.canServe = function(parts) {

// Temporary, while working on stuff
var name = parts.pathname.replace(basepath, "");
return methods[name] !== undefined || name === "login";
@@ -163,7 +246,18 @@ function loginHandler(conf, req, respond) {
respond(null, token);
}
function validateToken(req) {
var token = req.headers["session-token"];
var cookie = req.headers.cookie;
if (!cookie)
return false;

var token;
for (var c of cookie.split(/;\s*/)) {
var parts = c.split("=");
if (parts[0] === "token") {
token = parts[1];
break;
}
}
if (!token)
return false;


+ 22
- 3
js/slideshow.js View File

@@ -205,12 +205,31 @@ function Slideshow(dir, changeInterval) {
}
}

// This function starts the slideshow and goes through the slides
// one by one. When done, it starts again by calling this function again.
function init() {
self.updateSlides = updateSlides;
function updateSlides() {
slides = fs.readdirSync(dir)
.sort()
.map(file => Slide(pathlib.join(dir, file)));
}

self.serve = serve;
function serve(parts, res) {
for (var slide of slides) {
if (slide.name === parts.pathname.substr(1, slide.name.length)) {
slide.serveFiles(parts, res);
return;
}
}

// We haven't found any matching slides
res.writeHead(404);
res.end("404");
}

// This function starts the slideshow and goes through the slides
// one by one. When done, it starts again by calling this function again.
function init() {
updateSlides();

slideIndex = 0;
currentSlide = slides[slideIndex];

+ 2
- 16
server.js View File

@@ -14,6 +14,7 @@ var index = fs.readFileSync("web/index.html", "utf-8")
.replace(/<<transition_time>>/g, conf.transition_time);

var slideshow = Slideshow(conf.slides, conf.interval);
admin.init(slideshow);

function onexit(code) {
console.log("exiting", code);
@@ -53,22 +54,7 @@ function handler(req, res) {

// Serve slide files
} else {
var served = false;

for (var slide of slideshow.getSlides()) {

// If client requests /{slide-name}/*
if (slide.name === pathname.substr(1, slide.name.length)) {
slide.serveFiles(parts, res);
served = true;
break;
}
}

if (!served) {
res.writeHead(404);
res.end("404");
}
slideshow.serve(parts, res);
}
}


+ 3
- 5
web/admin/lib.js View File

@@ -9,14 +9,12 @@
window.error = error;
window.$$ = $$;

var sessToken = "";

function apiLogin(pass, cb) {
var extraHeads = [ [ "Session-Pass", pass ] ];

api("login", {}, (err, token) => {
if (token) {
sessToken = token;
document.cookie = "token = " + token;
cb(true);
} else {
cb(false);
@@ -32,13 +30,13 @@

function api(method, args, cb, extraHeads) {
var heads = new Headers();
heads.append("Session-Token", sessToken);
if (extraHeads)
extraHeads.forEach(h => heads.append(h[0], h[1]));

var opts = {
method: "POST",
headers: heads
headers: heads,
credentials: "same-origin"
};
fetch("/admin/api/"+method+argstr(args), opts)
.then(response => response.json())

+ 1
- 0
web/admin/style.css View File

@@ -89,6 +89,7 @@

#root.edit #fileList,
#root.edit #slide > .uploader,
#root.edit #slide > .delete,
#root.edit #html {
margin-bottom: 24px;
}

+ 55
- 15
web/admin/view.js View File

@@ -18,27 +18,53 @@
}

function viewLogin(root) {
function login(first) {
var pass;
do {
if (first)
pass = prompt("Password?");
else
pass = prompt("Incorrect password.");
} while (!pass);
var msg;
var pwd;
elem("form", {}, [
msg = elem("div", {
className: "msg",
innerHTML: "Log In"
}),

pwd = elem("input", {
type: "password",
placeholder: "Password"
}),

elem("button", {
innerHTML: "Log In"
})
]).on("submit", evt => {
evt.preventDefault();

apiLogin(pass, success => {
apiLogin(pwd.value, success => {
if (success)
setView("main");
else
login(false);
msg.innerHTML = "Incorrect password."
});
}

login(true);
}).appendTo(root);
}

function viewMain(root) {

// New slide button
elem("button", {
className: "newSlide",
innerHTML: "New Slide"
}).on("click", () => {
api("slide_create", {}, (err, id) => {
if (err)
return error(err);

setView("edit", [id]);
});
}).appendTo(root);

var slidesEl = elem("div", {
className: "slides"
}).appendTo(root);

// Get a list of the slides
api("list_slides", {}, (err, slides) => {
if (err)
@@ -64,7 +90,7 @@
elem("div", {
className: "overlay"
})
]).appendTo(root);
]).appendTo(slidesEl);

// Add 'disabled' to the class if it's disabled
api("slide_meta", { slide: s }, (err, res) => {
@@ -155,6 +181,21 @@
slide: slide
}, populateFileList),

elem("button", {
className: "delete",
innerHTML: "Delete"
}).on("click", () => {
if (!confirm("Are you sure you want to delete slide "+slide+"?"))
return;

api("slide_delete", { slide: slide }, err => {
if (err)
return error(err);

setView("main");
});
}),

htmlTextEl = elem("textarea", {
id: "html"
}).on("keydown", debounce(() => {
@@ -213,7 +254,6 @@
root.clear();
root.className = name;


views[name].apply(null, args);
}


Loading…
Cancel
Save