mirror of
https://github.com/marcogll/passkit-generator.git
synced 2026-03-15 14:25:17 +00:00
Replaced the creation of one new folder in tmpDir for each manifest to the creation in tmpDir of one manifest with unique generated uuid
Added some comments; Renamed some parameters
This commit is contained in:
162
index.js
162
index.js
@@ -1,11 +1,11 @@
|
|||||||
const express = require("express");
|
const os = require("os");
|
||||||
|
const fs = require("fs");
|
||||||
|
const path = require("path");
|
||||||
const crypto = require("crypto");
|
const crypto = require("crypto");
|
||||||
const { spawn } = require("child_process");
|
const { spawn } = require("child_process");
|
||||||
const os = require("os");
|
|
||||||
const path = require("path");
|
|
||||||
const archiver = require("archiver");
|
const archiver = require("archiver");
|
||||||
|
const express = require("express");
|
||||||
const async = require("async");
|
const async = require("async");
|
||||||
const fs = require("fs");
|
|
||||||
|
|
||||||
const _configuration = Object.freeze(require("./config.json"));
|
const _configuration = Object.freeze(require("./config.json"));
|
||||||
|
|
||||||
@@ -23,9 +23,7 @@ const Certificates = _configuration.certificates;
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
function removeDotFiles(from) {
|
function removeDotFiles(from) {
|
||||||
return from.filter(e => {
|
return from.filter(e => e.charAt(0) !== ".");
|
||||||
return e.charAt(0) !== "."
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function capitalizeFirst(str) {
|
function capitalizeFirst(str) {
|
||||||
@@ -51,6 +49,13 @@ function checkSignatureRequirements() {
|
|||||||
return Promise.all([checkCertificate, checkKey]);
|
return Promise.all([checkCertificate, checkKey]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Generates a unique UUID
|
||||||
|
From Github Gist: https://gist.github.com/jed/982883
|
||||||
|
*/
|
||||||
|
|
||||||
|
function UUIDGen(a){return a?(a^Math.random()*16>>a/4).toString(16):([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,UUIDGen)}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Generates the cryptografic signature for the manifest file.
|
Generates the cryptografic signature for the manifest file.
|
||||||
Spawns Openssl process since Node.js has no support for PKCSs.
|
Spawns Openssl process since Node.js has no support for PKCSs.
|
||||||
@@ -60,7 +65,7 @@ function checkSignatureRequirements() {
|
|||||||
@returns {Object} Promise
|
@returns {Object} Promise
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function generateManifestSignature(manifestPath) {
|
function generateManifestSignature(manifestUUID) {
|
||||||
return new Promise(function(done, rejected) {
|
return new Promise(function(done, rejected) {
|
||||||
checkSignatureRequirements()
|
checkSignatureRequirements()
|
||||||
.then(function() {
|
.then(function() {
|
||||||
@@ -74,7 +79,7 @@ function generateManifestSignature(manifestPath) {
|
|||||||
"-certfile", path.resolve(Certificates.dir, Certificates.files["wwdr_pem"]),
|
"-certfile", path.resolve(Certificates.dir, Certificates.files["wwdr_pem"]),
|
||||||
"-signer", path.resolve(Certificates.dir, Certificates.files["certificate"]),
|
"-signer", path.resolve(Certificates.dir, Certificates.files["certificate"]),
|
||||||
"-inkey", path.resolve(Certificates.dir, Certificates.files["key"]),
|
"-inkey", path.resolve(Certificates.dir, Certificates.files["key"]),
|
||||||
"-in", path.resolve(`${manifestPath}/manifest.json`),
|
"-in", path.resolve(`${os.tmpdir()}/manifest-${manifestUUID}.json`),
|
||||||
// "-out", path.resolve("passCreator", "event.pass", "./signature"),
|
// "-out", path.resolve("passCreator", "event.pass", "./signature"),
|
||||||
"-outform", "DER",
|
"-outform", "DER",
|
||||||
"-passin", `pass:${Certificates.credentials["dev_pem_key"]}`
|
"-passin", `pass:${Certificates.credentials["dev_pem_key"]}`
|
||||||
@@ -103,18 +108,28 @@ function generateManifestSignature(manifestPath) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateManifest(fromObject, tempFolderPath) {
|
/**
|
||||||
|
Generates a Buffer of JSON file (manifest)
|
||||||
|
@function generateManifest
|
||||||
|
@params {Object} fromObject - Manifest content
|
||||||
|
@params {String} manifestUUID
|
||||||
|
@return {Promise} - Promise with the manifest buffer
|
||||||
|
@see https://apple.co/2IhJr0Q (PassKit Package Structure)
|
||||||
|
@see https://apple.co/2K2aY3v (Passes Are Cryptographically Signed and Compressed)
|
||||||
|
*/
|
||||||
|
|
||||||
|
function generateManifest(fromObject, manifestUUID) {
|
||||||
return new Promise(function(done, failed) {
|
return new Promise(function(done, failed) {
|
||||||
if (!fromObject || typeof fromObject !== "object" && typeof fromObject !== "string") {
|
if (!fromObject || typeof fromObject !== "object" && typeof fromObject !== "string") {
|
||||||
return failed("generateManifest: Argument 0 is required and must be of an object or a string (source object)");
|
return failed("generateManifest: Argument 0 is required and must be of an object or a string (source object)");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!tempFolderPath || typeof tempFolderPath !== "string") {
|
if (!manifestUUID || typeof manifestUUID !== "string") {
|
||||||
return failed("generateManifest: Argument 1 is required and must be a string (temporary folder path for manifest)");
|
return failed("generateManifest: Argument 1 is required and must be a string (unique uuid).");
|
||||||
}
|
}
|
||||||
|
|
||||||
const source = typeof fromObject === "object" ? JSON.stringify(fromObject) : fromObject;
|
const source = typeof fromObject === "object" ? JSON.stringify(fromObject) : fromObject;
|
||||||
let manifestWS = fs.createWriteStream(`${tempFolderPath}/manifest.json`);
|
let manifestWS = fs.createWriteStream(`${os.tmpdir()}/manifest-${manifestUUID}.json`);
|
||||||
|
|
||||||
manifestWS.write(source);
|
manifestWS.write(source);
|
||||||
manifestWS.end();
|
manifestWS.end();
|
||||||
@@ -164,86 +179,75 @@ instance.get("/gen/:type/", function (req, res) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// Manifest dictionary
|
||||||
* Creating a temporary directory to keep the manifest.json of each pass.
|
let manifestRaw = {};
|
||||||
* This is done to pass the file as openssl parameter.
|
let archive = archiver("zip");
|
||||||
* It would be better to pass openssl a buffer but sadly it seems not possible.
|
|
||||||
* Feel free to contribute if you think there's a better way to achieve this.
|
|
||||||
*/
|
|
||||||
|
|
||||||
fs.mkdtemp(path.join(os.tmpdir(), "passkitWebServer-"), function(err, tempFolder) {
|
async.each(list, function getHashAndArchive(file, callback) {
|
||||||
if (err) {
|
if (file !== "manifest.json" && file !== "signature") {
|
||||||
throw err;
|
let passFileStream = fs.createReadStream(`${passModelsDir}/${req.params.type}.pass/${file}`);
|
||||||
|
let hashFlow = crypto.createHash("sha1");
|
||||||
|
|
||||||
|
// adding the files to the zip - i'm not using .directory method because it adds also hidden files like .DS_Store on macOS
|
||||||
|
archive.file(`${passModelsDir}/${req.params.type}.pass/${file}`, { name: file });
|
||||||
|
|
||||||
|
passFileStream.on("data", function(data) {
|
||||||
|
hashFlow.update(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
passFileStream.on("error", function(e) {
|
||||||
|
return callback(e);
|
||||||
|
});
|
||||||
|
|
||||||
|
passFileStream.on("end", function() {
|
||||||
|
manifestRaw[file] = hashFlow.digest("hex").trim();
|
||||||
|
return callback();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// skipping files
|
||||||
|
return callback();
|
||||||
|
}
|
||||||
|
}, function end(error) {
|
||||||
|
if (error) {
|
||||||
|
throw new Error(`Unable to compile manifest. ${error}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Manifest dictionary
|
let uuid = UUIDGen();
|
||||||
let manifestRaw = {};
|
|
||||||
let archive = archiver("zip");
|
|
||||||
|
|
||||||
async.each(list, function getHashAndArchive(file, callback) {
|
generateManifest(manifestRaw, uuid)
|
||||||
if (file !== "manifest.json" && file !== "signature") {
|
.then(function(manifestBuffer) {
|
||||||
let passFileStream = fs.createReadStream(`${passModelsDir}/${req.params.type}.pass/${file}`);
|
|
||||||
let hashFlow = crypto.createHash("sha1");
|
|
||||||
|
|
||||||
// adding the files to the zip - i'm not using .directory method because it adds also hidden files like .DS_Store on macOS
|
archive.append(manifestBuffer, { name: "manifest.json" });
|
||||||
archive.file(`${passModelsDir}/${req.params.type}.pass/${file}`, { name: file });
|
|
||||||
|
|
||||||
passFileStream.on("data", function(data) {
|
generateManifestSignature(uuid)
|
||||||
hashFlow.update(data);
|
.then(function(signatureBuffer) {
|
||||||
});
|
|
||||||
|
|
||||||
passFileStream.on("error", function(e) {
|
if (!fs.existsSync("output")) {
|
||||||
return callback(e);
|
fs.mkdirSync("output");
|
||||||
});
|
}
|
||||||
|
|
||||||
passFileStream.on("end", function() {
|
archive.append(signatureBuffer, { name: "signature" });
|
||||||
manifestRaw[file] = hashFlow.digest("hex").trim();
|
let outputWS = fs.createWriteStream(`${outputDir}/${req.params.type}.pkpass`);
|
||||||
return callback();
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// skipping files
|
|
||||||
return callback();
|
|
||||||
}
|
|
||||||
}, function end(error) {
|
|
||||||
if (error) {
|
|
||||||
throw new Error(`Unable to compile manifest. ${error}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
generateManifest(manifestRaw, tempFolder)
|
archive.pipe(outputWS);
|
||||||
.then(function(manifestBuffer) {
|
archive.finalize();
|
||||||
|
|
||||||
archive.append(manifestBuffer, { name: "manifest.json" });
|
outputWS.on("close", function() {
|
||||||
|
res.status(201).download(`${outputDir}/${req.params.type}.pkpass`, `${req.params.type}.pkpass`, {
|
||||||
generateManifestSignature(tempFolder)
|
cacheControl: false,
|
||||||
.then(function(signatureBuffer) {
|
headers: {
|
||||||
|
"Content-type": "application/vnd.apple.pkpass",
|
||||||
if (!fs.existsSync("output")) {
|
"Content-length": fs.statSync(`${outputDir}/${req.params.type}.pkpass`).size
|
||||||
fs.mkdirSync("output");
|
}
|
||||||
}
|
|
||||||
|
|
||||||
archive.append(signatureBuffer, { name: "signature" });
|
|
||||||
let outputWS = fs.createWriteStream(`${outputDir}/${req.params.type}.pkpass`);
|
|
||||||
|
|
||||||
archive.pipe(outputWS);
|
|
||||||
archive.finalize();
|
|
||||||
|
|
||||||
outputWS.on("close", function() {
|
|
||||||
res.status(201).download(`${outputDir}/${req.params.type}.pkpass`, `${req.params.type}.pkpass`, {
|
|
||||||
cacheControl: false,
|
|
||||||
headers: {
|
|
||||||
"Content-type": "application/vnd.apple.pkpass",
|
|
||||||
"Content-length": fs.statSync(`${outputDir}/${req.params.type}.pkpass`).size
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
})
|
|
||||||
.catch(function(buffer) {
|
|
||||||
throw buffer.toString();
|
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch(function(error) {
|
.catch(function(buffer) {
|
||||||
throw error;
|
throw buffer.toString();
|
||||||
});
|
});
|
||||||
|
})
|
||||||
|
.catch(function(error) {
|
||||||
|
throw error;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user