1 module app; 2 3 import dlogg.strict; 4 import daemonize.d; 5 6 // cannot use vibe.d due symbol clash for logging 7 import vibe.core.core; 8 import vibe.core.log : setLogLevel, setLogFile, VibeLogLevel = LogLevel; 9 import vibe.http.server; 10 import vibe.http.router; 11 import vibe.http.fileserver; 12 13 import std.json; 14 import std.file; 15 import std.path; 16 17 void loadRouteSpecs(string settings, URLRouter router, shared ILogger logger) { 18 auto json = parseJSON(settings); 19 if (json.type != JSON_TYPE.OBJECT) throw new Error("Settings file must contain an object"); 20 auto routes = json["routes"].array; 21 foreach(route; routes) 22 { 23 if (route.type != JSON_TYPE.OBJECT) throw new Error("Settings file: each route must be an object"); 24 auto path = route["path"].str; 25 auto type = route["type"].str; 26 auto arg = route["arg"].str; 27 28 if(type == "path") 29 { 30 auto targetPath = thisExePath.dirName.buildPath(arg); 31 router.get(path, serveStaticFiles(targetPath)); 32 logger.logInfo("Mapping path: " ~ path ~ " with " ~ targetPath); 33 } 34 else if(type == "file") 35 { 36 auto targetPath = thisExePath.dirName.buildPath(arg); 37 router.get(path, serveStaticFile(targetPath)); 38 logger.logInfo("Mapping path: " ~ path ~ " with " ~ targetPath); 39 } 40 else throw new Error("Invalid type: " ~ type); 41 } 42 } 43 44 void setupServer(shared ILogger logger, bool function() shouldExit) 45 { 46 // Default vibe initialization 47 auto settings = new HTTPServerSettings; 48 settings.port = 8080; 49 settings.useCompressionIfPossible = true; 50 settings.bindAddresses = ["::1", "127.0.0.1"]; 51 52 auto router = new URLRouter; 53 auto settingsFileName = thisExePath.dirName.buildPath("web.json"); 54 logger.logInfo("Loading settings file: " ~ settingsFileName); 55 auto settingsFileContents = readText(settingsFileName); 56 loadRouteSpecs(settingsFileContents, router, logger); 57 58 listenHTTP(settings, router); 59 } 60 61 // Simple daemon description 62 alias daemon = Daemon!( 63 "lt-web-server", // unique name 64 KeyValueList!( 65 Composition!(Signal.Terminate, Signal.Quit, Signal.Shutdown, Signal.Stop), (logger) 66 { 67 logger.logInfo("Exiting..."); 68 69 // No need to force exit here 70 // main will stop after the call 71 exitEventLoop(true); 72 return false; 73 }, 74 Signal.HangUp, (logger) 75 { 76 logger.logInfo("Hang up"); 77 return true; 78 } 79 ), 80 81 (logger, shouldExit) { 82 setupServer(logger, shouldExit); 83 84 // All exceptions are caught by daemonize 85 return runEventLoop(); 86 } 87 ); 88 89 int main(string[] args) 90 { 91 bool noService = args.length > 1 && args[1] == "--no-svc"; 92 93 // Setting vibe logger 94 // daemon closes stdout/stderr and vibe logger will crash 95 // if not suppress printing to console 96 version(Windows) auto vibeLogName = (noService ? "" : "C:\\" ) ~ "server-access.log"; 97 else enum vibeLogName = "server-access.log"; 98 99 // no stdout/stderr output 100 version(Windows) {} 101 else setLogLevel(VibeLogLevel.none); 102 103 setLogFile(vibeLogName, VibeLogLevel.info); 104 105 version(Windows) auto logFileName = (noService ? "" : "C:\\" ) ~ "logfile.log"; 106 else enum logFileName = "logfile.log"; 107 108 auto logger = new shared StrictLogger(logFileName); 109 logger.minOutputLevel = LoggingLevel.Debug; 110 111 if (noService) { 112 setupServer(logger, null); 113 return runEventLoop(); 114 } 115 116 return buildDaemon!daemon.run(logger); 117 }