Browse Source

Initial version

Noah 7 years ago
parent
commit
64fcbc8148

+ 1 - 1
audiogram/index.js View File

@@ -2,7 +2,7 @@ var path = require("path"),
2 2
     queue = require("d3").queue,
3 3
     mkdirp = require("mkdirp"),
4 4
     rimraf = require("rimraf"),
5
-    serverSettings = require("../settings/"),
5
+    serverSettings = require("../lib/settings/"),
6 6
     transports = require("../lib/transports/"),
7 7
     logger = require("../lib/logger/"),
8 8
     Profiler = require("../lib/profiler.js"),

+ 3 - 0
bin/server View File

@@ -1,5 +1,8 @@
1 1
 #!/usr/bin/env node
2 2
 
3
+// Require settings first, for validation
4
+require("../lib/settings/");
5
+
3 6
 var dotenv = require("dotenv").config({silent: true}),
4 7
     logger = require("../lib/logger/"),
5 8
     server = require("../server/");

+ 4 - 0
bin/worker View File

@@ -1,4 +1,8 @@
1 1
 #!/usr/bin/env node
2
+
3
+// Require settings first, for validation
4
+require("../lib/settings/");
5
+
2 6
 var dotenv = require("dotenv").config({silent: true}),
3 7
     Audiogram = require("../audiogram/"),
4 8
     transports = require("../lib/transports/");

+ 0 - 10
lib/register-fonts.js View File

@@ -1,10 +0,0 @@
1
-var fonts = require("../settings/").fonts,
2
-    _ = require("underscore"),
3
-    Canvas = require("canvas");
4
-
5
-// Register custom fonts one time
6
-if (Array.isArray(fonts)) {
7
-  fonts.forEach(function(font){
8
-    Canvas.registerFont(font.file, _.pick(font, "family", "weight", "style"));
9
-  });
10
-}

+ 39 - 0
lib/settings/index.js View File

@@ -0,0 +1,39 @@
1
+var _ = require("underscore"),
2
+    path = require("path"),
3
+    fs = require("fs");
4
+
5
+var settings = tryToLoad("settings/index.js"),
6
+    themes = tryToLoad("settings/themes.json");
7
+
8
+// Validate settings
9
+require("./validate-settings.js")(settings);
10
+
11
+// Validate themes
12
+require("./validate-themes.js")(themes);
13
+
14
+module.exports = settings;
15
+
16
+// Try to load modules
17
+function tryToLoad(filename) {
18
+
19
+  var loaded;
20
+
21
+  try {
22
+    loaded = require(path.join(__dirname, "..", "..", filename));
23
+    if (!loaded) {
24
+      throw new Error("Couldn't load contents of " + filename + ".");
25
+    }
26
+  } catch(e) {
27
+    if (e.code === "MODULE_NOT_FOUND") {
28
+      throw new Error("No " + filename + " file found.");
29
+    } else if (e instanceof SyntaxError) {
30
+      console.warn("Error parsing " + filename);
31
+      throw e;
32
+    } else {
33
+      throw e;
34
+    }
35
+  }
36
+
37
+  return loaded;
38
+
39
+}

+ 92 - 0
lib/settings/validate-settings.js View File

@@ -0,0 +1,92 @@
1
+var fs = require("fs"),
2
+    path = require("path"),
3
+    Canvas = require("canvas");
4
+
5
+module.exports = function(settings) {
6
+
7
+  // Check paths
8
+  if (typeof settings.workingDirectory !== "string") {
9
+    throw new Error("settings.workingDirectory is required. See https://github.com/nypublicradio/audiogram/blob/master/SERVER.md#all-settings for details.").
10
+  }
11
+
12
+  if (typeof settings.storagePath !== "string" && typeof settings.s3Bucket !== "string") {
13
+    throw new Error("settings.storagePath or settings.s3Bucket is required. See https://github.com/nypublicradio/audiogram/blob/master/SERVER.md#all-settings for details.");
14
+  }
15
+
16
+  // TODO normalize workingDirectory, s3Bucket, and storagePath
17
+  settings.workingDirectory = normalize(settings.workingDirectory);
18
+  tryToCreate("workingDirectory");
19
+
20
+  if (typeof settings.s3Bucket === "string") {
21
+
22
+    // Remove s3:// and trailing slash, bucket name only
23
+    settings.s3Bucket = settings.s3Bucket.replace(/^(s3[:]\/\/)|\/$/g, "");
24
+
25
+    if (settings.storagePath) {
26
+
27
+      // No leading slash, one trailing slash
28
+      settings.storagePath = settings.storagePath.replace(/^\/|\/$/g, "");
29
+
30
+      if (settings.storagePath) {
31
+        settings.storagePath += "/";
32
+      }
33
+
34
+    } else {
35
+      settings.storagePath = "";
36
+    }
37
+
38
+  } else {
39
+    settings.storagePath = normalize(settings.storagePath);
40
+    tryToCreate("storagePath");
41
+  }
42
+
43
+  // Check maxUploadSize
44
+  if ("maxUploadSize" in settings && typeof settings.maxUploadSize !== "number") {
45
+    throw new TypeError("settings.maxUploadSize must be an integer. See https://github.com/nypublicradio/audiogram/blob/master/SERVER.md#all-settings for details.");
46
+  }
47
+
48
+  // Check fonts
49
+  if ("fonts" in settings) {
50
+    if (!Array.isArray(settings.fonts)) {
51
+      throw new TypeError("settings.fonts must be an array of fonts. See https://github.com/nypublicradio/audiogram/blob/master/THEMES.md#a-note-about-fonts for details.")
52
+    }
53
+
54
+    settings.fonts.forEach(function(font){
55
+
56
+      if (!font || typeof font.family !== "string" || typeof font.file !== "string") {
57
+        return console.warn("Custom font in settings.fonts missing a 'family' or 'file'. See https://github.com/nypublicradio/audiogram/blob/master/THEMES.md#a-note-about-fonts for details.");
58
+      }
59
+
60
+      font.file = normalize(font.file);
61
+
62
+      try {
63
+        fs.accessSync(font.file);
64
+        Canvas.registerFont(font.file, _.pick(font, "family", "weight", "style"));
65
+      } catch(e) {
66
+        // TODO catch font registration error
67
+        console.warn(e);
68
+        return console.warn("Font file " + font.file + " does not exist or is not readable.");
69
+      }
70
+
71
+    });
72
+  }
73
+
74
+};
75
+
76
+function tryToCreate(key) {
77
+  try {
78
+    mkdirp.sync(settings[key]);
79
+    fs.accessSync(settings[key]);
80
+  } catch(e) {
81
+    // TODO more helpful msg
82
+    console.warn(e);
83
+    throw e;
84
+  }
85
+}
86
+
87
+function normalize(p) {
88
+  if (path.isAbsolute(p)) {
89
+    return p;
90
+  }
91
+  return path.join(__dirname, "..", "..", p);
92
+}

+ 42 - 0
lib/settings/validate-themes.js View File

@@ -0,0 +1,42 @@
1
+var path = require("path"),
2
+    _ = require("underscore"),
3
+    fs = require("fs");
4
+
5
+module.exports = function(themes) {
6
+
7
+  if (!themes || !_.keys(_.omit(themes, "default")).length) {
8
+    throw new Error("No themes found in settings/themes.json. See https://github.com/nypublicradio/audiogram/blob/master/THEMES.md for details.");
9
+  }
10
+
11
+  for (var key in themes) {
12
+    if (key !== "default") {
13
+      validateTheme(key, _.extend({}, themes.default || {}, themes[key]));
14
+    }
15
+  }
16
+
17
+};
18
+
19
+function validateTheme(name, options) {
20
+
21
+  var fullBackgroundImagePath;
22
+
23
+  if (!options || !_.keys(options).length) {
24
+    return console.warn("Theme '" + name + "' is not defined.");
25
+  }
26
+
27
+  ["width", "height", "framesPerSecond", "samplesPerFrame"].forEach(function(key){
28
+    if (typeof options[key] !== "number") {
29
+      console.warn("The required property '" + key +"' is missing from theme '" + name + "' or invalid.");
30
+    }
31
+  });
32
+
33
+  if (typeof options.backgroundImage === "string") {
34
+    fullBackgroundImagePath = path.join(__dirname, "..", "..", "settings/backgrounds/", options.backgroundImage);
35
+
36
+    try {
37
+      fs.accessSync(fullBackgroundImagePath);
38
+    } catch(e) {
39
+      console.warn("Background image for theme '" + name + "' (" + fullBackgroundImagePath + ") does not exist or is not readable.");
40
+    }
41
+  }
42
+}

+ 1 - 1
lib/transports/index.js View File

@@ -1,5 +1,5 @@
1 1
 var extend = require("underscore").extend,
2
-    serverSettings = require("../../settings/"),
2
+    serverSettings = require("../settings/"),
3 3
     s3 = require("./s3")(serverSettings),
4 4
     redis = require("./redis")(serverSettings);
5 5
 

+ 0 - 6
lib/transports/s3/fake.js View File

@@ -5,12 +5,6 @@ var fs = require("fs"),
5 5
 
6 6
 module.exports = function(storagePath) {
7 7
 
8
-  storagePath = storagePath || "";
9
-
10
-  if (!path.isAbsolute(storagePath)) {
11
-    storagePath = path.join(__dirname, "..", "..", "..", storagePath);
12
-  }
13
-
14 8
   function copy(source, destination, cb) {
15 9
 
16 10
     if (!path.isAbsolute(source)) {

+ 0 - 15
lib/transports/s3/remote.js View File

@@ -3,21 +3,6 @@ var AWS = require("aws-sdk"),
3 3
 
4 4
 module.exports = function(bucket, storagePath) {
5 5
 
6
-  storagePath = storagePath || "";
7
-
8
-  // Normalize slashes in path
9
-  if (storagePath.length) {
10
-    storagePath = storagePath.replace(/^\/|\/$/g, "") + "/";
11
-  }
12
-
13
-  // Catch single slash path
14
-  if (storagePath === "/") {
15
-    storagePath = "";
16
-  }
17
-
18
-  // Remove s3:// just in case
19
-  bucket = bucket.replace(/^(s3[:]\/\/)|\/$/g, "");
20
-
21 6
   var s3 = new AWS.S3({ params: { Bucket: bucket } });
22 7
 
23 8
   // Test credentials

+ 1 - 1
server/error.js View File

@@ -1,4 +1,4 @@
1
-var serverSettings = require("../settings/");
1
+var serverSettings = require("../lib/settings/");
2 2
 
3 3
 module.exports = function(err, req, res, next) {
4 4
 

+ 2 - 10
server/index.js View File

@@ -13,7 +13,7 @@ var logger = require("../lib/logger/"),
13 13
     errorHandlers = require("./error.js");
14 14
 
15 15
 // Settings
16
-var serverSettings = require("../settings/");
16
+var serverSettings = require("../lib/settings/");
17 17
 
18 18
 var app = express();
19 19
 
@@ -43,20 +43,12 @@ if (serverSettings.maxUploadSize) {
43 43
   };
44 44
 }
45 45
 
46
-if (typeof serverSettings.workingDirectory !== "string") {
47
-  throw new TypeError("No workingDirectory set in settings/index.js");
48
-}
49
-
50 46
 // On submission, check upload, validate input, and start generating a video
51 47
 app.post("/submit/", [multer(fileOptions).single("audio"), render.validate, render.route]);
52 48
 
53 49
 // If not using S3, serve videos locally
54 50
 if (!serverSettings.s3Bucket) {
55
-  if (typeof serverSettings.storagePath !== "string") {
56
-    throw new TypeError("No storagePath set in settings/index.js");
57
-  }
58
-  var storagePath =  path.isAbsolute(serverSettings.storagePath) ? serverSettings.storagePath : path.join(__dirname, "..", serverSettings.storagePath);
59
-  app.use("/video/", express.static(path.join(storagePath, "video")));
51
+  app.use("/video/", express.static(path.join(serverSettings.storagePath, "video")));
60 52
 }
61 53
 
62 54
 // Check the status of a current video

+ 1 - 1
server/render.js View File

@@ -1,4 +1,4 @@
1
-var serverSettings = require("../settings/"),
1
+var serverSettings = require("../lib/settings/"),
2 2
     spawn = require("child_process").spawn,
3 3
     path = require("path"),
4 4
     _ = require("underscore"),

+ 1 - 1
test/patch-settings.js View File

@@ -1,4 +1,4 @@
1
-var serverSettings = require("../settings/");
1
+var serverSettings = require("../lib/settings/");
2 2
 
3 3
 module.exports = function(newSettings) {
4 4
   for (var key in serverSettings) {