import std.experimental.logger; import std.range; import std.string; import std.stdio; import std.typecons; import vibe.d; import article; import page; import project; import watcher; /** * Internal list of articles by slug. */ Article[string] articles; Page[string] pages; Project[string] projects; immutable string articleSortPred = "a.firstPublished > b.firstPublished"; Article*[] articleList; immutable string pageSortedPred = "a.title < b.title"; Page*[] pageList; Project[] projectList; enum OutputType { HTML, MARKDOWN } const string MIME_MARKDOWN = "text/markdown"; /** * Get's the document type for the given slug based on extension * and returns the slug without extension and the document type. */ private OutputType getOutputType(ref string slug) { if (slug.endsWith(".md")) { slug = chomp(slug, ".md"); return OutputType.MARKDOWN; } else if (slug.endsWith(".html")){ // If explicitly asking for HTML, we'll return HTML slug = chomp(slug, ".html"); return OutputType.HTML; } else { // If in the future, for any reason, we no longer use HTML // this allows to us to keep the current urls with an option // to change the output in the future. return OutputType.HTML; } } void getSingle(T, string templ)(ref T[string] array, HTTPServerRequest req, HTTPServerResponse res) { string slug = req.params["slug"]; OutputType outputType = getOutputType(slug); enforceHTTP(slug in array, HTTPStatus.notFound, "Page not found"); T content = array[slug]; res.headers["Cache-Control"] = "public"; switch(outputType) with (OutputType) { case MARKDOWN: res.writeBody(content.contentSource, MIME_MARKDOWN); break; default: case HTML: res.render!(templ, content); break; } } /** * Generates response for /posts/:slug and /palen/:slug. */ void articleGetSingle(HTTPServerRequest req, HTTPServerResponse res) { getSingle!(Article, "pages/article.dt")(articles, req, res); } /** * Generates response for /posts/ and /palen/ */ void articleGetOverview(HTTPServerRequest req, HTTPServerResponse res) { res.headers["Cache-Control"] = "public"; render!("pages/article-list.dt", articleList)(res); } void projectGetOverview(HTTPServerRequest req, HTTPServerResponse res) { res.headers["Cache-Control"] = "public"; render!("pages/project-list.dt", projectList)(res); } /** * Generate response for a page */ void pageGet(HTTPServerRequest req, HTTPServerResponse res) { if (("slug" in req.params) is null) { req.params.addField("slug", "index"); } getSingle!(Page, "pages/page.dt")(pages, req, res); } /** * Generates response whenever an error occurs. */ @safe void errorPage(HTTPServerRequest req, HTTPServerResponse res, HTTPServerErrorInfo error) { render!("pages/error.dt", error)(res); } void main() { //articles["hello-world"] = new Article("hello-world.yamd"); HTTPServerSettings settings = new HTTPServerSettings; settings.bindAddresses = ["0.0.0.0"]; settings.port = 3465; settings.serverString = "zeg ik lekker niet"; settings.errorPageHandler = toDelegate(&errorPage); settings.keepAliveTimeout = dur!"seconds"(60); debug { settings.accessLogToConsole = true; } URLRouter router = new URLRouter; router.get("/posts/:slug", &articleGetSingle); router.get("/palen/:slug", &articleGetSingle); router.get("/posts/", &articleGetOverview); router.get("/palen/", &articleGetOverview); router.get("/projects/", &projectGetOverview); router.get("/projecten/", &projectGetOverview); router.get("/static/*", serveStaticFiles("./public/")); router.get("/:slug", &pageGet); router.get("/", &pageGet); listenHTTP(settings, router); runTask({ initPages!(Page, pageSortedPred)(pages, pageList, "pages"); }); runTask({ initPages!(Article, articleSortPred)(articles, articleList, "articles"); }); runApplication(); }