1 module app;
2 
3 // cannot use vibe.d due symbol clash for logging
4 import vibe.core.core;
5 import vibe.core.log;
6 import vibe.http.server;
7 import vibe.http.router;
8 
9 import jsonizer;
10 
11 import std.json;
12 import std.file;
13 import std.path;
14 import std.getopt;
15 import std.datetime;
16 
17 import fileCache;
18 
19 immutable string settingsFileName = "web.json";
20 
21 void loadRouteSpecs(HTTPFileServerSettings settings, RouteSettings[] routes, string basePath, URLRouter router)
22 {
23     foreach(route; routes)
24     {
25         auto targetPath = route.arg.absolutePath(basePath);
26         if(route.type == "path")
27         {
28             router.get(route.path, serveStaticFiles(targetPath, settings)); 
29         }
30         else if(route.type == "file")
31         {
32             router.get(route.path, serveStaticFile(targetPath, settings));
33         }
34         else throw new Error("Invalid type: " ~ route.type);
35         logInfo("Mapping path: " ~ route.path ~ " with " ~ targetPath);
36     }
37 }
38 
39 int runServer(string settingsFileName)
40 {
41     settingsFileName = settingsFileName.absolutePath(thisExePath.dirName);
42     auto basePath = settingsFileName.dirName;
43 
44     // Load settings from file
45     auto settingsFileContents = readText(settingsFileName);
46     auto json = parseJSON(settingsFileContents);
47     auto settings = json.fromJSON!Settings;
48 
49     settings.logFileName = settings.logFileName.absolutePath(basePath);
50 
51     // Setup loggers
52     setLogFile(settings.logFileName, settings.logLevel);
53 
54     // Default vibe initialization
55     auto svrSettings = new HTTPServerSettings;
56     svrSettings.port = cast(ushort)settings.port;
57     svrSettings.useCompressionIfPossible = settings.useCompressionIfPossible;
58     svrSettings.bindAddresses = settings.bindAddresses;
59     //svrSettings.options |= HTTPServerOption.distribute;
60 
61     auto fileServerSettings = new HTTPFileServerSettings;
62     fileServerSettings.cache = settings.useServerCache ? new FileServerCache : null;
63     logInfo("Using cache: " ~ (settings.useServerCache ? "true" : "false"));
64     fileServerSettings.maxAge = settings.clientCacheMaxAge.seconds;
65 
66     auto router = new URLRouter;
67     logInfo("Loading routes from settings");
68     loadRouteSpecs(fileServerSettings, settings.routes, basePath, router);
69 
70     listenHTTP(svrSettings, router);
71 
72     return runEventLoop();
73 }
74 
75 struct RouteSettings 
76 {
77     mixin JsonizeMe;
78 
79     @jsonize string type;
80     @jsonize string path;
81     @jsonize string arg;
82 }
83 
84 struct Settings 
85 {
86     mixin JsonizeMe;
87 
88     @jsonize(JsonizeOptional.yes) int port = 3000;
89     @jsonize(JsonizeOptional.yes) string[] bindAddresses = ["::", "0.0.0.0"];
90     @jsonize(JsonizeOptional.yes) bool useCompressionIfPossible = true;
91     @jsonize(JsonizeOptional.yes) string logFileName = "vibe.log";
92     @jsonize(JsonizeOptional.yes) LogLevel logLevel = LogLevel.none;
93     @jsonize(JsonizeOptional.yes) bool useServerCache = false;
94 	@jsonize(JsonizeOptional.yes) uint clientCacheMaxAge = 0;
95     @jsonize RouteSettings[] routes;
96 }
97 
98 int main(string[] args)
99 {
100     string settingsFileNameOpt = settingsFileName;
101     auto helpInformation = getopt(args, 
102         "settings", "JSON settings file name.", &settingsFileNameOpt);
103 
104     if (helpInformation.helpWanted)
105     {
106         defaultGetoptPrinter(
107             "Help about this program. "
108             "Settings file path is relative to this executable",
109             helpInformation.options);
110         return 0;
111     }
112 
113     logInfo("Loading info from " ~ settingsFileNameOpt);
114 
115     return runServer(settingsFileNameOpt);
116 }