diff options
| author | stone <jules+okfprojz@okfoc.us> | 2017-02-18 19:59:09 -0500 |
|---|---|---|
| committer | stone <jules+okfprojz@okfoc.us> | 2017-02-18 19:59:09 -0500 |
| commit | 794bc930903a836329aff47481d109df10f81069 (patch) | |
| tree | 160499d95dac5d1df01b45490b735d2b60e814e1 /lib | |
| parent | 765527e34208a6e1b83f51eba4a32aa5c239fbc7 (diff) | |
| parent | cf85cc2b75b9c3ead3a693b6fa0feeca5b9e70ba (diff) | |
Merge branch 'cms' of ghghgh.us:stone-island into cms
Diffstat (limited to 'lib')
20 files changed, 1221 insertions, 0 deletions
diff --git a/lib/okpush/.gitignore b/lib/okpush/.gitignore new file mode 100644 index 00000000..e314c5fe --- /dev/null +++ b/lib/okpush/.gitignore @@ -0,0 +1,3 @@ + +node_modules + diff --git a/lib/okpush/apn.js b/lib/okpush/apn.js new file mode 100644 index 00000000..febd03bb --- /dev/null +++ b/lib/okpush/apn.js @@ -0,0 +1,59 @@ + +var apn = require('apn') +var db = require('./db') +var apnProvider, apnFeedback + +function init (config) { + var apn_config = config.production ? config.apn_production : config.apn_development + config.apn = apn_config + config.apn.connection.key = apn_config.key + config.apn.connection.cert = apn_config.cert + apnProvider = new apn.Provider(config.apn.connection) +} + +function push (note) { + db.getAllDevices(function(err, tokens){ + if (err) { + console.error("Error fetching devices:", err) + return + } + connection.send(note, tokens).then( function (response) { + // response.sent.forEach( function (token) { + // notificationSent(user, token) + // }) + response.failed.forEach( function (failure) { + if (failure.error) { + // A transport-level error occurred (e.g. network problem) + // notificationError(user, token, failure.error); + } else if (failure.status == 410) { + // `failure.status` is the HTTP status code + // `failure.response` is the JSON payload + // notificationFailed(token, failure.status, failure.response); + db.removeDevice(token) + } + }) + }) + }) +} + +function buildPayload (options, bundleId) { + var note = new apn.Notification() + note.topic = bundleId + if (options.expiry) + note.expiry = Math.floor(Date.now() / 1000) + options.expiry + if (options.alert) + note.alert = options.alert + if (options.badge) + note.badge = options.badge + if (options.payload) + note.payload = options.payload + if (options.sound) + note.sound = options.sound + return note +} + +module.exports = { + init: init, + push: push, + buildPayload: buildPayload +} diff --git a/lib/okpush/certs/aps_development_cert.pem b/lib/okpush/certs/aps_development_cert.pem new file mode 100644 index 00000000..fdfc2ba0 --- /dev/null +++ b/lib/okpush/certs/aps_development_cert.pem @@ -0,0 +1,32 @@ +-----BEGIN CERTIFICATE----- +MIIFjTCCBHWgAwIBAgIIcP///sIUbaEwDQYJKoZIhvcNAQEFBQAwgZYxCzAJBgNV +BAYTAlVTMRMwEQYDVQQKDApBcHBsZSBJbmMuMSwwKgYDVQQLDCNBcHBsZSBXb3Js +ZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9uczFEMEIGA1UEAww7QXBwbGUgV29ybGR3 +aWRlIERldmVsb3BlciBSZWxhdGlvbnMgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw +HhcNMTcwMjE4MTYwNDAxWhcNMTgwMjE4MTYwNDAxWjCBjDEkMCIGCgmSJomT8ixk +AQEMFHVzLm9rZm9jLnN0b25laXNsYW5kMUIwQAYDVQQDDDlBcHBsZSBEZXZlbG9w +bWVudCBJT1MgUHVzaCBTZXJ2aWNlczogdXMub2tmb2Muc3RvbmVpc2xhbmQxEzAR +BgNVBAsMCkM2SlFUUEdKOTcxCzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAtO/TPmNPgqPw6FNIQsg6IU1htEaUsfG7hvMWMxmhLfIz +YaRaoN++rb+yFHLx9v9LuAox+CBsMjUVeHR0vI2rHf9oQ0fBaxf+hkaWISp6hgpp +tPahQEsNRk56wFxaPh4m1ybLidYhWCJzPDJUNJDQX7Bk4YZ+tzQIDsVobwYyZtFI +NtSxi04mZrGy7s5tJKQ8pE++AF7Fo8QJoGfTw/eJIiu2V2khYgt3k1ieXvl0+o+j +B+zuvHO9fuLfOWa1Bo6Xy1gc0Qz1HALytkx6NYkTJ/vAJamegcKhjH2Z6vukha5U +RcSybpE2cEjg6I/2LK5ypRiGYtQJWACxRUAdTeYCAQIDAQABo4IB5TCCAeEwHQYD +VR0OBBYEFFqpdB90hmc8EisSBphK5Q/b1nb3MAkGA1UdEwQCMAAwHwYDVR0jBBgw +FoAUiCcXCam2GGCL7Ou69kdZxVJUo7cwggEPBgNVHSAEggEGMIIBAjCB/wYJKoZI +hvdjZAUBMIHxMIHDBggrBgEFBQcCAjCBtgyBs1JlbGlhbmNlIG9uIHRoaXMgY2Vy +dGlmaWNhdGUgYnkgYW55IHBhcnR5IGFzc3VtZXMgYWNjZXB0YW5jZSBvZiB0aGUg +dGhlbiBhcHBsaWNhYmxlIHN0YW5kYXJkIHRlcm1zIGFuZCBjb25kaXRpb25zIG9m +IHVzZSwgY2VydGlmaWNhdGUgcG9saWN5IGFuZCBjZXJ0aWZpY2F0aW9uIHByYWN0 +aWNlIHN0YXRlbWVudHMuMCkGCCsGAQUFBwIBFh1odHRwOi8vd3d3LmFwcGxlLmNv +bS9hcHBsZWNhLzBNBgNVHR8ERjBEMEKgQKA+hjxodHRwOi8vZGV2ZWxvcGVyLmFw +cGxlLmNvbS9jZXJ0aWZpY2F0aW9uYXV0aG9yaXR5L3d3ZHJjYS5jcmwwCwYDVR0P +BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMCMBAGCiqGSIb3Y2QGAwEEAgUAMA0G +CSqGSIb3DQEBBQUAA4IBAQAGstXORHB9X0+RhRDmbTgfz9iTv7JJnRXDxD294rxG +Z/CeRDYE+gedxayTieKdcLn7bl+NjyIo2LsX3vr2i7la5CdWt1r1V7Y4mJ1fu3w1 +MTEYEHF7ejd8PI9n2M68p5/qTaqhJ4zI9zKsVXGJUmi9Ta5FNfJiud4OVZbfurN5 +rnyFFCk/kd+5sHgU/KqdnxtKR1DVjGbFqh6YI63Lv3druNJl8SpmTRuK9Vz6wQw3 +idXEj18jbqoADBnjiQuLhyxDxDM8lOjVUAsmALG1t20dFJeSUw61a+8DLoqLJvRg +X0A8Ssh8FawGy9sSl5s3K9bjFrxLBHyOqZ8dVWeFXZPR +-----END CERTIFICATE----- diff --git a/lib/okpush/certs/aps_development_key.pem b/lib/okpush/certs/aps_development_key.pem new file mode 100644 index 00000000..a1663eef --- /dev/null +++ b/lib/okpush/certs/aps_development_key.pem @@ -0,0 +1,68 @@ +Bag Attributes + friendlyName: Apple Development IOS Push Services: us.okfoc.stoneisland + localKeyID: 5A A9 74 1F 74 86 67 3C 12 2B 12 06 98 4A E5 0F DB D6 76 F7 +subject=/UID=us.okfoc.stoneisland/CN=Apple Development IOS Push Services: us.okfoc.stoneisland/OU=C6JQTPGJ97/C=US +issuer=/C=US/O=Apple Inc./OU=Apple Worldwide Developer Relations/CN=Apple Worldwide Developer Relations Certification Authority +-----BEGIN CERTIFICATE----- +MIIFjTCCBHWgAwIBAgIIcP///sIUbaEwDQYJKoZIhvcNAQEFBQAwgZYxCzAJBgNV +BAYTAlVTMRMwEQYDVQQKDApBcHBsZSBJbmMuMSwwKgYDVQQLDCNBcHBsZSBXb3Js +ZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9uczFEMEIGA1UEAww7QXBwbGUgV29ybGR3 +aWRlIERldmVsb3BlciBSZWxhdGlvbnMgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw +HhcNMTcwMjE4MTYwNDAxWhcNMTgwMjE4MTYwNDAxWjCBjDEkMCIGCgmSJomT8ixk +AQEMFHVzLm9rZm9jLnN0b25laXNsYW5kMUIwQAYDVQQDDDlBcHBsZSBEZXZlbG9w +bWVudCBJT1MgUHVzaCBTZXJ2aWNlczogdXMub2tmb2Muc3RvbmVpc2xhbmQxEzAR +BgNVBAsMCkM2SlFUUEdKOTcxCzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAtO/TPmNPgqPw6FNIQsg6IU1htEaUsfG7hvMWMxmhLfIz +YaRaoN++rb+yFHLx9v9LuAox+CBsMjUVeHR0vI2rHf9oQ0fBaxf+hkaWISp6hgpp +tPahQEsNRk56wFxaPh4m1ybLidYhWCJzPDJUNJDQX7Bk4YZ+tzQIDsVobwYyZtFI +NtSxi04mZrGy7s5tJKQ8pE++AF7Fo8QJoGfTw/eJIiu2V2khYgt3k1ieXvl0+o+j +B+zuvHO9fuLfOWa1Bo6Xy1gc0Qz1HALytkx6NYkTJ/vAJamegcKhjH2Z6vukha5U +RcSybpE2cEjg6I/2LK5ypRiGYtQJWACxRUAdTeYCAQIDAQABo4IB5TCCAeEwHQYD +VR0OBBYEFFqpdB90hmc8EisSBphK5Q/b1nb3MAkGA1UdEwQCMAAwHwYDVR0jBBgw +FoAUiCcXCam2GGCL7Ou69kdZxVJUo7cwggEPBgNVHSAEggEGMIIBAjCB/wYJKoZI +hvdjZAUBMIHxMIHDBggrBgEFBQcCAjCBtgyBs1JlbGlhbmNlIG9uIHRoaXMgY2Vy +dGlmaWNhdGUgYnkgYW55IHBhcnR5IGFzc3VtZXMgYWNjZXB0YW5jZSBvZiB0aGUg +dGhlbiBhcHBsaWNhYmxlIHN0YW5kYXJkIHRlcm1zIGFuZCBjb25kaXRpb25zIG9m +IHVzZSwgY2VydGlmaWNhdGUgcG9saWN5IGFuZCBjZXJ0aWZpY2F0aW9uIHByYWN0 +aWNlIHN0YXRlbWVudHMuMCkGCCsGAQUFBwIBFh1odHRwOi8vd3d3LmFwcGxlLmNv +bS9hcHBsZWNhLzBNBgNVHR8ERjBEMEKgQKA+hjxodHRwOi8vZGV2ZWxvcGVyLmFw +cGxlLmNvbS9jZXJ0aWZpY2F0aW9uYXV0aG9yaXR5L3d3ZHJjYS5jcmwwCwYDVR0P +BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMCMBAGCiqGSIb3Y2QGAwEEAgUAMA0G +CSqGSIb3DQEBBQUAA4IBAQAGstXORHB9X0+RhRDmbTgfz9iTv7JJnRXDxD294rxG +Z/CeRDYE+gedxayTieKdcLn7bl+NjyIo2LsX3vr2i7la5CdWt1r1V7Y4mJ1fu3w1 +MTEYEHF7ejd8PI9n2M68p5/qTaqhJ4zI9zKsVXGJUmi9Ta5FNfJiud4OVZbfurN5 +rnyFFCk/kd+5sHgU/KqdnxtKR1DVjGbFqh6YI63Lv3druNJl8SpmTRuK9Vz6wQw3 +idXEj18jbqoADBnjiQuLhyxDxDM8lOjVUAsmALG1t20dFJeSUw61a+8DLoqLJvRg +X0A8Ssh8FawGy9sSl5s3K9bjFrxLBHyOqZ8dVWeFXZPR +-----END CERTIFICATE----- +Bag Attributes + friendlyName: Julian Laplace + localKeyID: 5A A9 74 1F 74 86 67 3C 12 2B 12 06 98 4A E5 0F DB D6 76 F7 +Key Attributes: <No Attributes> +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAtO/TPmNPgqPw6FNIQsg6IU1htEaUsfG7hvMWMxmhLfIzYaRa +oN++rb+yFHLx9v9LuAox+CBsMjUVeHR0vI2rHf9oQ0fBaxf+hkaWISp6hgpptPah +QEsNRk56wFxaPh4m1ybLidYhWCJzPDJUNJDQX7Bk4YZ+tzQIDsVobwYyZtFINtSx +i04mZrGy7s5tJKQ8pE++AF7Fo8QJoGfTw/eJIiu2V2khYgt3k1ieXvl0+o+jB+zu +vHO9fuLfOWa1Bo6Xy1gc0Qz1HALytkx6NYkTJ/vAJamegcKhjH2Z6vukha5URcSy +bpE2cEjg6I/2LK5ypRiGYtQJWACxRUAdTeYCAQIDAQABAoIBAQCbKszDRRGr3II+ +CyX+N446fw0aOClOzRrgKVnRatph98J+qx8Ztaflg27pwIWnJ9s8smmV++At0n5e +NajjBq9H43/7qu7Bs+sQJjqbqL04dHegvJ0e8P4qXCVDEDBH8z2l51xIEb24wpqG +djbmeYZpeXGaQugqntGGMV9VT0AsET3sA+LbhO3klIE5j3C+cWOZWKccMjppCSD+ +iA9c8hGdAt/wOWLQ7R15mvqlZZuSd4mIxgprClPN9/SUnk/w3PM8gXBNV1Vz79Cm +xxiv4BMbobGUtJF/d8vzpSP/M2J5sCc8PhIHjuzhgZTg1pitFdIcxcZxKSW2xYH7 +F51JX1ABAoGBAOnnHNFfj7uZzRqqOzD8De1oLhIJ+Ua1HaoyLcn9YIGD56p+AxCi +BgjIIAgz0SctbeSQeo7vVspwSR4kxrQsvTfAaZgvxWSAJc3eHkrucgT/qdAWs0fk +YgWtNXmvmDNo4ZQqfX3rvXPt+ILPB2/pLg/2qfZ5HZRpu355VNO09tzBAoGBAMYH +vgn/MEd02WGonvCTJO7RTvficJkThP1Z4TDBttfjCvbcFalvHM+0nOhRwTCZcS6H +oLXkse6wAVNzQP+2GUT/gmvyqfYM18C2BQm6InqnBBfd2bgH1WP2j76XWuOhw5q/ ++AjtPnFszIhpusR1PSrsA86+ZuIHzd2DGfpshDVBAoGAdYRfbzZjAa9pZE9JA/hv +iur/0qJeMs9Ggz9I0CZUeV3rZDaBlbbB72GU7moSWGuzvdV1LiFD6tv1cIarqtSb +kMpEg2jk9PSJieBiOeWJNce56sms857kPNPkfSni/1lflNCwUjbB7eAPLrGTm/98 +3BpjFGmPmgRxZj7RhrhyG0ECgYB2PxdF5uAQOwC2pLZLVsGd7NOnhsnPTe2CTpH9 +yB4C6SqZ3y78HkHo8KMkwfSGrYj8zLW1BxsMOIKCLvbn+fc60QsypBLCvkZyZv25 +IjEGZgY2rh0cG2lbKFYK61u1QtmG5QwZYHiByAKVjfSc7JiasZVQdTb/zsMpeugf +NX74QQKBgD4441+lWTadLqDFxE/DQ29QPC5JTwjvCs9O0Aq+7StQ+yXQHSVWP8i1 +t/mUkDIntVUu0b75ZfjR55EvbkEOvPj1rtaVBUaPA5hk/6jAvHwJJWr/Dmu7oBbu +ELmFAk8FmoL3RIeEHzqbJZ/FR184TGWi8YmooDDMOOv8pYDRsytj +-----END RSA PRIVATE KEY----- diff --git a/lib/okpush/certs/aps_production_cert.pem b/lib/okpush/certs/aps_production_cert.pem new file mode 100644 index 00000000..e3023909 --- /dev/null +++ b/lib/okpush/certs/aps_production_cert.pem @@ -0,0 +1,35 @@ +-----BEGIN CERTIFICATE----- +MIIGLDCCBRSgAwIBAgIIGH+Bvy4cPa8wDQYJKoZIhvcNAQELBQAwgZYxCzAJBgNV +BAYTAlVTMRMwEQYDVQQKDApBcHBsZSBJbmMuMSwwKgYDVQQLDCNBcHBsZSBXb3Js +ZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9uczFEMEIGA1UEAww7QXBwbGUgV29ybGR3 +aWRlIERldmVsb3BlciBSZWxhdGlvbnMgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw +HhcNMTcwMjE4MTYwNDI1WhcNMTgwMzIwMTYwNDI1WjCBnTEkMCIGCgmSJomT8ixk +AQEMFHVzLm9rZm9jLnN0b25laXNsYW5kMTIwMAYDVQQDDClBcHBsZSBQdXNoIFNl +cnZpY2VzOiB1cy5va2ZvYy5zdG9uZWlzbGFuZDETMBEGA1UECwwKQzZKUVRQR0o5 +NzEfMB0GA1UECgwWU3BvcnRzd2VhciBDb21wYW55IFNwYTELMAkGA1UEBhMCVVMw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC079M+Y0+Co/DoU0hCyDoh +TWG0RpSx8buG8xYzGaEt8jNhpFqg376tv7IUcvH2/0u4CjH4IGwyNRV4dHS8jasd +/2hDR8FrF/6GRpYhKnqGCmm09qFASw1GTnrAXFo+HibXJsuJ1iFYInM8MlQ0kNBf +sGThhn63NAgOxWhvBjJm0Ug21LGLTiZmsbLuzm0kpDykT74AXsWjxAmgZ9PD94ki +K7ZXaSFiC3eTWJ5e+XT6j6MH7O68c71+4t85ZrUGjpfLWBzRDPUcAvK2THo1iRMn ++8AlqZ6BwqGMfZnq+6SFrlRFxLJukTZwSODoj/YsrnKlGIZi1AlYALFFQB1N5gIB +AgMBAAGjggJzMIICbzAdBgNVHQ4EFgQUWql0H3SGZzwSKxIGmErlD9vWdvcwDAYD +VR0TAQH/BAIwADAfBgNVHSMEGDAWgBSIJxcJqbYYYIvs67r2R1nFUlSjtzCCARwG +A1UdIASCARMwggEPMIIBCwYJKoZIhvdjZAUBMIH9MIHDBggrBgEFBQcCAjCBtgyB +s1JlbGlhbmNlIG9uIHRoaXMgY2VydGlmaWNhdGUgYnkgYW55IHBhcnR5IGFzc3Vt +ZXMgYWNjZXB0YW5jZSBvZiB0aGUgdGhlbiBhcHBsaWNhYmxlIHN0YW5kYXJkIHRl +cm1zIGFuZCBjb25kaXRpb25zIG9mIHVzZSwgY2VydGlmaWNhdGUgcG9saWN5IGFu +ZCBjZXJ0aWZpY2F0aW9uIHByYWN0aWNlIHN0YXRlbWVudHMuMDUGCCsGAQUFBwIB +FilodHRwOi8vd3d3LmFwcGxlLmNvbS9jZXJ0aWZpY2F0ZWF1dGhvcml0eTAwBgNV +HR8EKTAnMCWgI6Ahhh9odHRwOi8vY3JsLmFwcGxlLmNvbS93d2RyY2EuY3JsMA4G +A1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAjAQBgoqhkiG92NkBgMB +BAIFADAQBgoqhkiG92NkBgMCBAIFADCBgwYKKoZIhvdjZAYDBgR1MHMMFHVzLm9r +Zm9jLnN0b25laXNsYW5kMAUMA2FwcAwZdXMub2tmb2Muc3RvbmVpc2xhbmQudm9p +cDAGDAR2b2lwDCF1cy5va2ZvYy5zdG9uZWlzbGFuZC5jb21wbGljYXRpb24wDgwM +Y29tcGxpY2F0aW9uMA0GCSqGSIb3DQEBCwUAA4IBAQA/fUg+Q375bqS1fziJt8jw ++b8R7LLARQkMYKsq6/6x5bZJklCllgFR+uy5ahmEvNy+iFDnDW1BL+kkN8rxiwNk +QJDuxFB1cwmeLw5Yw4XF2XRhnmHNuUOA6Q/no84IeBttlFQWnFdWyUUO3dwqWQZf +dvpPesXL6XYmNtFUVMwrtBPjvrVBYsfqbnmPdcr+XETtl7+pv7+A5HaTVhF/eEP3 +q6C1YA412IuccQLJTcTBYUbaGfk5eOcJzRwbfetXW+qE0n/fxiw1esoOoiv22yof +ojLbDM9OPF/zqNfU7+nY5juw5whXDI7lSwUvoYXC80tFf7IdivwPy2DbBonlr0ye +-----END CERTIFICATE----- diff --git a/lib/okpush/certs/aps_production_key.pem b/lib/okpush/certs/aps_production_key.pem new file mode 100644 index 00000000..3fe17b37 --- /dev/null +++ b/lib/okpush/certs/aps_production_key.pem @@ -0,0 +1,71 @@ +Bag Attributes + friendlyName: Apple Push Services: us.okfoc.stoneisland + localKeyID: 5A A9 74 1F 74 86 67 3C 12 2B 12 06 98 4A E5 0F DB D6 76 F7 +subject=/UID=us.okfoc.stoneisland/CN=Apple Push Services: us.okfoc.stoneisland/OU=C6JQTPGJ97/O=Sportswear Company Spa/C=US +issuer=/C=US/O=Apple Inc./OU=Apple Worldwide Developer Relations/CN=Apple Worldwide Developer Relations Certification Authority +-----BEGIN CERTIFICATE----- +MIIGLDCCBRSgAwIBAgIIGH+Bvy4cPa8wDQYJKoZIhvcNAQELBQAwgZYxCzAJBgNV +BAYTAlVTMRMwEQYDVQQKDApBcHBsZSBJbmMuMSwwKgYDVQQLDCNBcHBsZSBXb3Js +ZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9uczFEMEIGA1UEAww7QXBwbGUgV29ybGR3 +aWRlIERldmVsb3BlciBSZWxhdGlvbnMgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw +HhcNMTcwMjE4MTYwNDI1WhcNMTgwMzIwMTYwNDI1WjCBnTEkMCIGCgmSJomT8ixk +AQEMFHVzLm9rZm9jLnN0b25laXNsYW5kMTIwMAYDVQQDDClBcHBsZSBQdXNoIFNl +cnZpY2VzOiB1cy5va2ZvYy5zdG9uZWlzbGFuZDETMBEGA1UECwwKQzZKUVRQR0o5 +NzEfMB0GA1UECgwWU3BvcnRzd2VhciBDb21wYW55IFNwYTELMAkGA1UEBhMCVVMw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC079M+Y0+Co/DoU0hCyDoh +TWG0RpSx8buG8xYzGaEt8jNhpFqg376tv7IUcvH2/0u4CjH4IGwyNRV4dHS8jasd +/2hDR8FrF/6GRpYhKnqGCmm09qFASw1GTnrAXFo+HibXJsuJ1iFYInM8MlQ0kNBf +sGThhn63NAgOxWhvBjJm0Ug21LGLTiZmsbLuzm0kpDykT74AXsWjxAmgZ9PD94ki +K7ZXaSFiC3eTWJ5e+XT6j6MH7O68c71+4t85ZrUGjpfLWBzRDPUcAvK2THo1iRMn ++8AlqZ6BwqGMfZnq+6SFrlRFxLJukTZwSODoj/YsrnKlGIZi1AlYALFFQB1N5gIB +AgMBAAGjggJzMIICbzAdBgNVHQ4EFgQUWql0H3SGZzwSKxIGmErlD9vWdvcwDAYD +VR0TAQH/BAIwADAfBgNVHSMEGDAWgBSIJxcJqbYYYIvs67r2R1nFUlSjtzCCARwG +A1UdIASCARMwggEPMIIBCwYJKoZIhvdjZAUBMIH9MIHDBggrBgEFBQcCAjCBtgyB +s1JlbGlhbmNlIG9uIHRoaXMgY2VydGlmaWNhdGUgYnkgYW55IHBhcnR5IGFzc3Vt +ZXMgYWNjZXB0YW5jZSBvZiB0aGUgdGhlbiBhcHBsaWNhYmxlIHN0YW5kYXJkIHRl +cm1zIGFuZCBjb25kaXRpb25zIG9mIHVzZSwgY2VydGlmaWNhdGUgcG9saWN5IGFu +ZCBjZXJ0aWZpY2F0aW9uIHByYWN0aWNlIHN0YXRlbWVudHMuMDUGCCsGAQUFBwIB +FilodHRwOi8vd3d3LmFwcGxlLmNvbS9jZXJ0aWZpY2F0ZWF1dGhvcml0eTAwBgNV +HR8EKTAnMCWgI6Ahhh9odHRwOi8vY3JsLmFwcGxlLmNvbS93d2RyY2EuY3JsMA4G +A1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAjAQBgoqhkiG92NkBgMB +BAIFADAQBgoqhkiG92NkBgMCBAIFADCBgwYKKoZIhvdjZAYDBgR1MHMMFHVzLm9r +Zm9jLnN0b25laXNsYW5kMAUMA2FwcAwZdXMub2tmb2Muc3RvbmVpc2xhbmQudm9p +cDAGDAR2b2lwDCF1cy5va2ZvYy5zdG9uZWlzbGFuZC5jb21wbGljYXRpb24wDgwM +Y29tcGxpY2F0aW9uMA0GCSqGSIb3DQEBCwUAA4IBAQA/fUg+Q375bqS1fziJt8jw ++b8R7LLARQkMYKsq6/6x5bZJklCllgFR+uy5ahmEvNy+iFDnDW1BL+kkN8rxiwNk +QJDuxFB1cwmeLw5Yw4XF2XRhnmHNuUOA6Q/no84IeBttlFQWnFdWyUUO3dwqWQZf +dvpPesXL6XYmNtFUVMwrtBPjvrVBYsfqbnmPdcr+XETtl7+pv7+A5HaTVhF/eEP3 +q6C1YA412IuccQLJTcTBYUbaGfk5eOcJzRwbfetXW+qE0n/fxiw1esoOoiv22yof +ojLbDM9OPF/zqNfU7+nY5juw5whXDI7lSwUvoYXC80tFf7IdivwPy2DbBonlr0ye +-----END CERTIFICATE----- +Bag Attributes + friendlyName: Julian Laplace + localKeyID: 5A A9 74 1F 74 86 67 3C 12 2B 12 06 98 4A E5 0F DB D6 76 F7 +Key Attributes: <No Attributes> +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAtO/TPmNPgqPw6FNIQsg6IU1htEaUsfG7hvMWMxmhLfIzYaRa +oN++rb+yFHLx9v9LuAox+CBsMjUVeHR0vI2rHf9oQ0fBaxf+hkaWISp6hgpptPah +QEsNRk56wFxaPh4m1ybLidYhWCJzPDJUNJDQX7Bk4YZ+tzQIDsVobwYyZtFINtSx +i04mZrGy7s5tJKQ8pE++AF7Fo8QJoGfTw/eJIiu2V2khYgt3k1ieXvl0+o+jB+zu +vHO9fuLfOWa1Bo6Xy1gc0Qz1HALytkx6NYkTJ/vAJamegcKhjH2Z6vukha5URcSy +bpE2cEjg6I/2LK5ypRiGYtQJWACxRUAdTeYCAQIDAQABAoIBAQCbKszDRRGr3II+ +CyX+N446fw0aOClOzRrgKVnRatph98J+qx8Ztaflg27pwIWnJ9s8smmV++At0n5e +NajjBq9H43/7qu7Bs+sQJjqbqL04dHegvJ0e8P4qXCVDEDBH8z2l51xIEb24wpqG +djbmeYZpeXGaQugqntGGMV9VT0AsET3sA+LbhO3klIE5j3C+cWOZWKccMjppCSD+ +iA9c8hGdAt/wOWLQ7R15mvqlZZuSd4mIxgprClPN9/SUnk/w3PM8gXBNV1Vz79Cm +xxiv4BMbobGUtJF/d8vzpSP/M2J5sCc8PhIHjuzhgZTg1pitFdIcxcZxKSW2xYH7 +F51JX1ABAoGBAOnnHNFfj7uZzRqqOzD8De1oLhIJ+Ua1HaoyLcn9YIGD56p+AxCi +BgjIIAgz0SctbeSQeo7vVspwSR4kxrQsvTfAaZgvxWSAJc3eHkrucgT/qdAWs0fk +YgWtNXmvmDNo4ZQqfX3rvXPt+ILPB2/pLg/2qfZ5HZRpu355VNO09tzBAoGBAMYH +vgn/MEd02WGonvCTJO7RTvficJkThP1Z4TDBttfjCvbcFalvHM+0nOhRwTCZcS6H +oLXkse6wAVNzQP+2GUT/gmvyqfYM18C2BQm6InqnBBfd2bgH1WP2j76XWuOhw5q/ ++AjtPnFszIhpusR1PSrsA86+ZuIHzd2DGfpshDVBAoGAdYRfbzZjAa9pZE9JA/hv +iur/0qJeMs9Ggz9I0CZUeV3rZDaBlbbB72GU7moSWGuzvdV1LiFD6tv1cIarqtSb +kMpEg2jk9PSJieBiOeWJNce56sms857kPNPkfSni/1lflNCwUjbB7eAPLrGTm/98 +3BpjFGmPmgRxZj7RhrhyG0ECgYB2PxdF5uAQOwC2pLZLVsGd7NOnhsnPTe2CTpH9 +yB4C6SqZ3y78HkHo8KMkwfSGrYj8zLW1BxsMOIKCLvbn+fc60QsypBLCvkZyZv25 +IjEGZgY2rh0cG2lbKFYK61u1QtmG5QwZYHiByAKVjfSc7JiasZVQdTb/zsMpeugf +NX74QQKBgD4441+lWTadLqDFxE/DQ29QPC5JTwjvCs9O0Aq+7StQ+yXQHSVWP8i1 +t/mUkDIntVUu0b75ZfjR55EvbkEOvPj1rtaVBUaPA5hk/6jAvHwJJWr/Dmu7oBbu +ELmFAk8FmoL3RIeEHzqbJZ/FR184TGWi8YmooDDMOOv8pYDRsytj +-----END RSA PRIVATE KEY----- diff --git a/lib/okpush/db.js b/lib/okpush/db.js new file mode 100644 index 00000000..f64acf69 --- /dev/null +++ b/lib/okpush/db.js @@ -0,0 +1,140 @@ +var mongoose = require('mongoose') +var findOrCreate = require('mongoose-findorcreate') +var _ = require('lodash') +var db, PushToken, Notification, Channel + +mongoose.Promise = require('bluebird') + +function init (config) { + db = mongoose.connect(config.mongodbUrl) + mongoose.connection.on('error', errorHandler) + + var pushTokenSchema = new db.Schema({ + platform: { + type: 'String', + required: true, + lowercase: true, + enum: ['ios', 'android'], + }, + token: { + type: 'String', + required: true, + }, + channel: { + type: 'String', + required: true, + }, + }) + + var channelSchema = new db.Schema({ + channel: { + type: 'String', + required: true, + lowercase: true, + }, + last_push: { + type: 'Date', + required: true, + } + }) + channelSchema.plugin(findOrCreate); + + var notificationSchema = new db.Schema({ + channel: { + type: 'String', + required: true, + lowercase: true, + }, + date: { + type: 'Date', + required: true, + } + }) + + PushToken = db.model('PushToken', pushTokenSchema) + Notification = db.model('Notification', notificationSchema) + Channel = db.model('Channel', channelSchema) +} + +function errorHandler (error) { + console.error('ERROR: ' + error) +} + +/* devices / tokens */ + +function addToken (data) { + return new PushToken(data).save() +} +function getAllTokens (channel, cb) { + PushToken.find({ channel: channel }, function (err, items) { + if (err) return cb(err, null) + var items = _.map(items, function (item) { + return _.pick(item, ['type', 'token']) + }) + return cb(null, items) + }) +} +function removeToken (data) { + PushToken.find(data).remove().exec() +} +function getDeviceCount (channels, cb) { + var countz = {} + get_next() + function get_next() { + var channel = channels.pop() + PushToken.count({ channel: channel }, function(err, count){ + countz[channel] = count + if (channels.length) { + return get_next() + } + else { + cb(countz) + } + }) + } +} + +/* notifications */ + +function addNotification (channel, cb) { + var now = new Date + Channel.findOrCreate({channel: channel}, {last_push: now}, function(err, note, created) { + if (err) { + console.error("Error finding/creating notification", err) + cb(err, false) + return + } + else if (! created) { + note.last_push = now + note.save() + } + cb(null, note) + }) + new Notification ({ + channel: channel, + date: now, + }).save() +} +function getNotifications (cb) { + Notification.find(function (err, items) { + if (err) return cb(err, null) + + var items = _.map(items, function (item) { + return _.pick(item, ['channel', 'last_push']) + }) + + return cb(null, items) + }) +} + +/* wrap functions for some reason */ + +module.exports = { + init: init, + addToken: addToken, + removeToken: removeToken, + getAllTokens: getAllTokens, + getDeviceCount: getDeviceCount, + addNotification: addNotification, + getNotifications: getNotifications, +}
\ No newline at end of file diff --git a/lib/okpush/index.js b/lib/okpush/index.js new file mode 100644 index 00000000..df9c8265 --- /dev/null +++ b/lib/okpush/index.js @@ -0,0 +1,127 @@ +/** + * OKPush - Handles basic broadcast push notifications, as well as keeping track of tokens. + */ + +var path = require('path') +var passport = require('passport') +var DigestStrategy = require('passport-http').DigestStrategy; +var bodyParser = require('body-parser') +var OKTemplate = require('../../node_modules/okcms/app/node_modules/oktemplate') +var apn = require('./apn') +var db = require('./db') + +passport.use(new DigestStrategy({qop: 'auth'}, function authenticate(username, done) { + if (!process.env.OK_USER || !process.env.OK_PASS) { + return done(new Error('No user or pass configured on server')) + } else { + return done(null, process.env.OK_USER, process.env.OK_PASS) + } +})) + +function OKPush (options) { + if (!(this instanceof OKPush)) return new OKPush(options) + options = options || {} + if (!options.express) + throw new Error('Express not provided to OKPush') + if (!options.config) + throw new Error('Configuration not provided to OKPush') + if (!options.config.notifications) + throw new Error('Notifications not provided to OKPush') + if (!options.config.bundleId) + throw new Error('bundleId not provided to OKPush') + if (!options.config.mongodbUrl) + throw new Error('mongodbUrl not provided to OKPush') + + var express = options.express + var router = express.Router() + var config = options.config + var meta = options.meta + var error = options.errorHandler + // var okcms_db = options.db + + var templateProvider = this._templateProvider = new OKTemplate({ + root: path.join(__dirname, './templates'), + debug: meta.debug + }) + + var templates = {} + templates['index'] = templateProvider.getTemplate('index') + + apn.init(config) + db.init(config) + +// router.use('/admin/', passport.initialize()) + router.use('/public/', express.static(path.join(__dirname, './public'))); + + // monkeypatch because of app.use(router) .. obnoxious +// router.all('/admin/(:path*)?', function (req, res, next) { +// console.log(req.url) +// req.newUrl = req.url +// req.url = req.originalUrl +// next() +// }) +// router.all('/admin/(:path*)?', passport.authenticate('digest', { +// session: false +// })) +// router.all('/admin/(:path*)?', function (req, res, next) { +// req.url = req.newUrl +// next() +// }) + + // pass in admin middleware! + router.get('/admin', function (req, res) { + db.getNotifications(function(err, notes){ + var channels = Object.keys(config.notifications) + db.getDeviceCount(channels, function(count){ + var data = { + meta: meta, + notifications: config.notifications, + } + notes.forEach(function(note){ + if (note.key in data.notifications) { + data.notifications[ note.key ].last_push = note.last_push + } + if (note.key in count) { + data.notifications[ note.key ].count = count[note.key] + } + }) + templates['index'].render(data).then(function(rendered) { + res.send(rendered); + }).fail(error(req, res, 500)) + }) + }) + }) + + router.post('/send', bodyParser.urlencoded({ extended: false }), function (req, res) { + var key = req.body.key + var opt = options.config.notifications[key] + var note = apn.buildPayload(opt, options.config.bundleId) + // apn.push(note) + db.updateNotification(key, function(){ + res.send(200) + }) + }) + + // should work without middleware + router.post('/add', function (req, res) { + db.addToken({ + token: req.body.registrationId, + channel: req.body.channel, + platform: req.body.platform, + }) + }) + router.post('/remove', function (req, res) { + db.removeToken({ + token: req.body.registrationId, + channel: req.body.channel, + }) + }) + + this._router = router +} + +OKPush.prototype.middleware = function () { + return this._router +} + +module.exports = OKPush diff --git a/lib/okpush/package.json b/lib/okpush/package.json new file mode 100644 index 00000000..87ca92ca --- /dev/null +++ b/lib/okpush/package.json @@ -0,0 +1,21 @@ +{ + "name": "okpush", + "version": "1.0.0", + "description": "push notification service", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "okfocus <frontdesk@okfoc.us>", + "license": "LNT", + "dependencies": { + "apn": "^2.1.1", + "bluebird": "^3.4.6", + "body-parser": "^1.15.2", + "lodash": "^4.16.3", + "mongoose": "^4.6.2", + "mongoose-findorcreate": "^0.1.2", + "passport": "^0.3.2", + "passport-http": "^0.3.0" + } +} diff --git a/lib/okpush/public/push.js b/lib/okpush/public/push.js new file mode 100644 index 00000000..d369c903 --- /dev/null +++ b/lib/okpush/public/push.js @@ -0,0 +1,31 @@ +$(function(){ + var count = $(".device-count").data("count"); + var confirm_msg = "This will send the notification {{key}} to {{count}} people. Click OK to confirm."; + $(".notifications button").click(function(){ + var $el = $(this) + var data = $el.data() + var msg = confirm_msg.replace("{{key}}", data.key).replace("{{count}}", count) + if (! confirm(msg)) return + $.ajax({ + type: "POST", + url: "/_services/push/send", + data: { key: data.key }, + success: function(){ + alert("Push notification sent.") + var now = new Date() + // "%a %d-%b-%Y %H:%M" + var months = "Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" ") + var days = "Sun Mon Tue Wed Thu Fri Sat".split(" ") + var date = days[ now.getDay() ] + date += " " + now.getDate() + date += "-" + months[now.getMonth()] + date += "-" + now.getFullYear() + date += " " + now.getHours() + var mins = now.getMinutes() + if (mins < 10) mins = "0" + mins + date += ":" + mins + $el.closest("tr").find(".notification-date").html(date) + } + }) + }) +})
\ No newline at end of file diff --git a/lib/okpush/templates/index.liquid b/lib/okpush/templates/index.liquid new file mode 100644 index 00000000..1db77a58 --- /dev/null +++ b/lib/okpush/templates/index.liquid @@ -0,0 +1,76 @@ +{% include 'partials/head' %} + +{% include 'partials/flash' %} + +<style> +th { + min-width: 100px; + text-align: left; + background: #ddd; +} +th:nth-child(2) { + min-width: 300px; +} +th:nth-child(3) { + min-width: 170px; +} +table,tr,th,td { + margin: 0; + padding: 0; +} +th,td { + padding: 5px; +} +tr:nth-child(2n+1) { + background: #f8f8f8; +} +</style> + +<nav class="resource-nav"> + <a href="/admin/" class="btn">Back</a> +</nav> + +<section class="main resource"> + + <h2>Push Notifications</h2> + + <table class="notifications" cellpadding="0" cellspacing="0"> + <tr> + <th>Key</th> + <th>Message</th> + <th>Last Push</th> + <th>Registrations</th> + <th></th> + </tr> + {% for pair in notifications %} + {% assign name = pair[0] %} + {% assign spec = pair[1] %} + <tr> + <td> + {{name | escape}} + </td> + <td> + {{spec.alert}} + </td> + <td class="notification-date"> + {% unless spec.last_push %} + Never + {% else %} + {{ spec.last_push | date: "%a %d-%b-%Y %H:%M" }} + {% endunless %} + </td> + <td> + {{spec.count}} + </td> + <td> + <button data-key="{{name}}" class="btn">send</button> + </td> + </tr> + {% endfor %} + </table> + +</section> + +{% include 'partials/tail' %} + +<script src="public/push.js"></script>
\ No newline at end of file diff --git a/lib/okpush/templates/partials/flash.liquid b/lib/okpush/templates/partials/flash.liquid new file mode 100644 index 00000000..e51a86b7 --- /dev/null +++ b/lib/okpush/templates/partials/flash.liquid @@ -0,0 +1,20 @@ +{% if success.length > 0 %} +<div class="success"> + <div class="message">Changes saved.</div> + <!-- + {% for info in success %} + <div class="message">{{info.action}}</div> + {% endfor %} + --> +</div> +{% endif %} + +{% if errors.length > 0 %} +<div class="errors"> + {% for error in errors %} + <div class="error"> + <div class="message">{{error.message}}</div> + </div> + {% endfor %} +</div> +{% endif %}
\ No newline at end of file diff --git a/lib/okpush/templates/partials/head.liquid b/lib/okpush/templates/partials/head.liquid new file mode 100644 index 00000000..e9c27dc0 --- /dev/null +++ b/lib/okpush/templates/partials/head.liquid @@ -0,0 +1,13 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf8"> + <title>{{meta.project}} Admin</title> + <link rel="stylesheet" href="{{meta.static}}/css/main.css"> + </head> + <body> + <header class="admin-header"> + <span class="breadcrumb"><b>{{meta.project}}</b> Admin</span> + <a class="site-link" href="/">View Site</a> + </header> + <div class="container">
\ No newline at end of file diff --git a/lib/okpush/templates/partials/inputs.liquid b/lib/okpush/templates/partials/inputs.liquid new file mode 100644 index 00000000..60466de9 --- /dev/null +++ b/lib/okpush/templates/partials/inputs.liquid @@ -0,0 +1,395 @@ +{% for pair in resource.spec %} + {% assign name = pair[0] %} + {% assign spec = pair[1] %} + {% assign type = spec.type %} + + <div class="property {{type}} {% if spec.hidden %}hidden{% endif %}" + data-name="{{name}}"> + <label for="{{name}}">{{name | capitalize}}</label> + + {% if type == 'string' %} + <input + name="{{name}}" type="text" value="{{spec.value | escape}}"> + {% elsif type == 'text' %} + <textarea + name="{{name}}">{{spec.value | escape}}</textarea> + {% elsif type == 'number' %} + <input + type="number" + name="{{name}}" value="{{spec.value | escape}}"> + {% elsif type == 'enum' or type == 'foreign-key' %} + <select + name="{{name}}"> + {% for option in spec.options %} + {% if option.length == 2 %} + <option value="{{option[0]}}" {% if option[0] == spec.value %}selected{% endif %}>{{option[1] | capitalize}}</option> + {% else %} + <option value="{{option}}" {% if option == spec.value %}selected{% endif %}>{{option | capitalize}}</option> + {% endif %} + {% endfor %} + </select> + {% elsif type == 'video' %} + <div class="video group {% if spec.value.url %}loaded{% endif %}"> + <input name="{{name}}[url]" type="text" value="{{spec.value.url}}" class="url" placeholder="Enter a video URL"> + <input name="{{name}}[type]" type="hidden" value="{{spec.value.type}}" class="video-type" hidden> + <input name="{{name}}[token]" type="hidden" value="{{spec.value.token}}" class="video-token" hidden> + <input name="{{name}}[width]" value="{{spec.value.width}}" type="hidden" class="video-width" hidden> + <input name="{{name}}[height]" value="{{spec.value.height}}" type="hidden" class="video-height" hidden> + <label>Title</label> + <input name="{{name}}[title]" type="text" value="{{spec.value.title | escape}}" class="video-title"> + <label>Thumbnail</label> + <input name="{{name}}[thumb]" type="text" value="{{spec.value.thumb | escape}}" class="video-thumb"> + </div> + {% elsif type == 'image' %} + <div class="image group {% if spec.value.uri %}loaded{% endif %}"> + <div class="fields"> + <div class="add-image-button"> + <input type="file" accept="image/*"> + <button>+ Add image</button> + </div> + <input class="add-url" type="text" placeholder="+ Add URL"> + </div> + <div class="image-element"> + <input class="uri" type="hidden" name="{{name}}[uri]" value="{{spec.value.uri}}"> + <textarea class="caption" name="{{name}}[caption]">{{spec.value.caption | escape}}</textarea> + <input type="hidden" name="{{name}}[width]" value="{{spec.value.width}}" class="image-width"> + <input type="hidden" name="{{name}}[height]" value="{{spec.value.height}}" class="image-height"> + <img src="{{spec.value.uri}}" alt="{{spec.value.caption | escape}}"> + <button class="remove">x</button> + </div> + </div> + + {% elsif type == 'date' %} + + <div class="date"> + <input name="{{name}}" + type="date" + {% if spec.value %} + value="{{spec.value | date: '%Y-%m-%d'}}" + {% endif %} + > + </div> + + {% elsif type == 'flag' %} + + <div class="flag"> + <input name="{{name}}" + type="checkbox" + {% if spec.value %} + checked="true" + {% endif %} + value="true"> + </div> + + {% elsif type == 'tag-list' %} + <div class="tag-list"> + <input name="{{name}}" + value="{{spec.value | escape}}" + placeholder="Enter a comma separated list of tags."> + </div> + + {% elsif type == 'link-list' %} + <div class="link-list group"> + <ol class="links"> + {% for link in spec.value %} + <li> + <div class="handle"></div> + <input + name="{{name}}[{{forloop.index0}}][text]" + value="{{link.text | escape}}" + type="text" + class="link-input link-text"> + <input + name="{{name}}[{{forloop.index0}}][uri]" + value="{{link.uri | escape}}" + type="text" + class="link-input link-uri"> + <button class="remove-link-btn btn"> + - + </button> + </li> + {% endfor %} + </ol> + + <div class="handle"></div> + <input type="text" + class="link-input-new link-text" + placeholder="{% if spec.textLabel %}{{ spec.textLabel }}{% else %}Link text{% endif %}"> + <input type="text" + class="link-input-new link-uri" + placeholder="{% if spec.linkLabel %}{{ spec.linkLabel }}{% else %}http://www.example.com{% endif %}"> + <button class="add-link-btn btn">+</button> + + <script type="text/html" class="link-template"> + <li> + <div class="handle"></div> + <input + name="{{name}}[][text]" + value="" + type="text" + placeholder="{% if spec.textLabel %}{{ spec.textLabel }}{% else %}Link text{% endif %}" + class="link-input link-text"> + <input + name="{{name}}[][uri]" + value="" + type="text" + placeholder="{% if spec.linkLabel %}{{ spec.linkLabel }}{% else %}http://www.example.com{% endif %}" + class="link-input link-uri"> + <button class="remove-link-btn btn"> + - + </button> + </li> + </script> + + </div> + + {% elsif type == 'media-list' or type == 'media' %} + <div class="media-list group loaded"> + <div class="fields"> + <div class="add-image-button"> + <input type="file" multiple> + <button>+ Add media</button> + </div> + <input class="add-url" type="text" placeholder="+ Add Image/Video/Link URL"> + </div> + + <script type="text/html" class="image-template"> + <li class="image-element"> + <label>Caption</label> + <input class="uri" type="hidden" name="{{name}}[][uri]" value=""> + <textarea class="caption" name="{{name}}[][caption]"></textarea> + <input type="hidden" name="{{name}}[][type]" value="image"> + <input type="hidden" name="{{name}}[][width]" class="image-width" hidden> + <input type="hidden" name="{{name}}[][height]" class="image-height" hidden> + <img> + <button class="remove">x</button> + </li> + </script> + + <script type="text/html" class="video-template"> + <li class="video-element"> + <div style="float: left"> + <input name="{{name}}[][type]" type="hidden" class="video-type" hidden> + <input name="{{name}}[][token]" type="hidden" class="video-token" hidden> + <input name="{{name}}[][uri]" type="hidden" class="video-uri" hidden> + <input name="{{name}}[][width]" type="hidden" class="video-width" hidden> + <input name="{{name}}[][height]" type="hidden" class="video-height" hidden> + <label>Caption</label> + <input name="{{name}}[][title]" type="text" class="video-title"> + <label>Thumbnail</label> + <input name="{{name}}[][thumb]" type="text" class="video-thumb"> + <span class="checkboxes"> + <input name="{{name}}[][autoplay]" value="{{image.autoplay}}" type="checkbox" class="video-autoplay flag"> + <label>Autoplay</label> + <input name="{{name}}[][loop]" value="{{image.loop}}" type="checkbox" class="video-loop flag"> + <label>Loop</label> + </span> + </div> + <img> + <button class="remove">x</button> + </li> + </script> + + <script type="text/html" class="audio-template"> + <li class="audio-element"> + <div style="float: left"> + <input name="{{name}}[][type]" type="hidden" class="audio-type" hidden> + <input name="{{name}}[][token]" type="hidden" class="audio-token" hidden> + <input name="{{name}}[][uri]" type="hidden" class="audio-uri" hidden> + <input name="{{name}}[][duration]" value="{{image.duration}}" type="hidden" class="audio-duration" hidden> + <label>Caption</label> + <input name="{{name}}[][title]" type="text" class="audio-title"> + <label>Thumbnail</label> + <input name="{{name}}[][thumb]" type="text" class="audio-thumb"> + </div> + <img> + <button class="remove">x</button> + </li> + </script> + + <script type="text/html" class="link-template"> + <li class="link-element"> + <input class="uri" type="text" name="{{name}}[][uri]" value=""> + <textarea class="caption" name="{{name}}[][caption]" placeholder="Caption"></textarea> + <input type="hidden" name="{{name}}[][type]" value="link"> + <button class="remove">x</button> + </li> + </script> + + <ol> + {% for image in spec.value %} + {% if image.type and (image.type == "vimeo" or image.type == "youtube" or image.type == "video") %} + <li class="video-element"> + <div style="float: left"> + <input name="{{name}}[{{forloop.index0}}][type]" value="{{image.type}}" type="hidden" class="video-type" hidden> + <input name="{{name}}[{{forloop.index0}}][token]" value="{{image.token}}" type="hidden" class="video-token" hidden> + <input name="{{name}}[{{forloop.index0}}][uri]" value="{{image.uri}}" type="hidden" class="video-uri" hidden> + <input name="{{name}}[{{forloop.index0}}][width]" value="{{image.width}}" type="hidden" class="video-width" hidden> + <input name="{{name}}[{{forloop.index0}}][height]" value="{{image.height}}" type="hidden" class="video-height" hidden> + <label>Caption</label> + <input name="{{name}}[{{forloop.index0}}][title]" value="{{image.title | escape}}" type="text" class="video-title"> + <label>Thumbnail</label> + <input name="{{name}}[{{forloop.index0}}][thumb]" value="{{image.thumb}}" type="text" class="video-thumb"> + <span class="checkboxes"> + <input name="{{name}}[{{forloop.index0}}][autoplay]" value="true" {% if image.autoplay == "true" %}checked="true"{% endif %} type="checkbox" class="flag video-autoplay"> + <label>Autoplay</label> + <input name="{{name}}[{{forloop.index0}}][loop]" value="true" {% if image.loop == "true" %}checked="true"{% endif %} type="checkbox" class="flag video-loop"> + <label>Loop</label> + </span> + </div> + <img src="{{image.thumb}}"> + <button class="remove">x</button> + </li> + {% elsif image.type and (image.type == "audio" or image.type == "soundcloud") %} + <li class="audio-element"> + <div style="float: left"> + <input name="{{name}}[{{forloop.index0}}][type]" value="{{image.type}}" type="hidden" class="audio-type" hidden> + <input name="{{name}}[{{forloop.index0}}][token]" value="{{image.token}}" type="hidden" class="audio-token" hidden> + <input name="{{name}}[{{forloop.index0}}][uri]" value="{{image.uri}}" type="hidden" class="audio-uri" hidden> + <input name="{{name}}[{{forloop.index0}}][duration]" value="{{image.duration}}" type="hidden" class="audio-duration" hidden> + <label>Caption</label> + <input name="{{name}}[{{forloop.index0}}][title]" value="{{image.title | escape}}" type="text" class="audio-title"> + <label>Thumbnail</label> + <input name="{{name}}[{{forloop.index0}}][thumb]" value="{{image.thumb}}" type="text" class="audio-thumb"> + </div> + <img src="{{image.thumb}}"> + <button class="remove">x</button> + </li> + {% elsif image.type and image.type == "link" %} + <li class="link-element"> + <input class="uri" type="text" name="{{name}}[{{forloop.index0}}][uri]" value="{{image.uri}}"> + <textarea class="caption" name="{{name}}[{{forloop.index0}}][caption]" placeholder="Caption">{{image.caption | escape}}</textarea> + <input type="hidden" name="{{name}}[{{forloop.index0}}][type]" value="link"> + <button class="remove">x</button> + </li> + {% else %} + <li class="image-element"> + <label>Caption</label> + <input type="hidden" name="{{name}}[{{forloop.index0}}][uri]" value="{{image.uri}}"> + <input name="{{name}}[{{forloop.index0}}][width]" value="{{image.width}}" type="hidden" class="image-width" hidden> + <input name="{{name}}[{{forloop.index0}}][height]" value="{{image.height}}" type="hidden" class="image-height" hidden> + <textarea class="caption" name="{{name}}[{{forloop.index0}}][caption]">{{image.caption | escape}}</textarea> + <input type="hidden" name="{{name}}[{{forloop.index0}}][type]" value="image"> + <img src="{{image.uri}}" alt="{{image.caption | strip_html}}"> + <button class="remove">x</button> + </li> + {% endif %} + {% endfor %} + </ol> + </div> + {% elsif type == 'captioned-image-list' or type == 'gallery' %} + <div class="image-list group loaded"> + <div class="fields"> + <div class="add-image-button"> + <input type="file" accept="image/*" multiple> + <button>+ Add images</button> + </div> + <input class="add-url" type="text" placeholder="+ Add URL"> + </div> + + <script type="text/html" class="image-template"> + <li class="image-element"> + <input class="uri" type="hidden" name="{{name}}[][uri]" value=""> + <input type="hidden" name="{{name}}[][width]" class="image-width" hidden> + <input type="hidden" name="{{name}}[][height]" class="image-height" hidden> + <textarea class="caption" name="{{name}}[][caption]"></textarea> + <img> + <button class="remove">x</button> + </li> + </script> + + <ol> + {% for image in spec.value %} + <li class="image-element"> + <input type="hidden" name="{{name}}[{{forloop.index0}}][uri]" value="{{image.uri}}"> + <input type="hidden" name="{{name}}[{{forloop.index0}}][width]" value="{{image.width}}" class="image-width"> + <input type="hidden" name="{{name}}[{{forloop.index0}}][height]" value="{{image.height}}" class="image-height"> + <textarea class="caption" name="{{name}}[{{forloop.index0}}][caption]">{{image.caption | escape}}</textarea> + <img src="{{image.uri}}" alt="{{image.caption | strip_html}}"> + <button class="remove">x</button> + </li> + {% endfor %} + </ol> + </div> + {% elsif type == 'double-captioned-image-list' %} + <div class="image-list group loaded"> + <div class="fields"> + <div class="add-image-button"> + <input type="file" accept="image/*" multiple> + <button>+ Add images</button> + </div> + <input class="add-url" type="text" placeholder="+ Add URL"> + </div> + + <script type="text/html" class="image-template"> + <li class="image-element"> + <img> + <button class="remove">x</button> + <input class="uri" type="hidden" name="{{name}}[][uri]" value=""> + <input type="hidden" name="{{name}}[][width]" class="image-width"> + <input type="hidden" name="{{name}}[][height]" class="image-height"> + <input class="caption" name="{{name}}[][label]" placeholder="Name"> + <input class="caption" name="{{name}}[][caption]" placeholder="Email"> + </li> + </script> + + <ol> + {% for image in spec.value %} + <li class="image-element"> + <img src="{{image.uri}}" alt="{{image.caption | strip_html}}"> + <button class="remove">x</button> + <input type="hidden" name="{{name}}[{{forloop.index0}}][uri]" value="{{image.uri}}"> + <input type="hidden" name="{{name}}[{{forloop.index0}}][width]" value="{{image.width}}" class="image-width"> + <input type="hidden" name="{{name}}[{{forloop.index0}}][height]" value="{{image.height}}" class="image-height"> + <input class="caption" name="{{name}}[{{forloop.index0}}][label]" value="{{image.label | escape}}" placeholder="Name"> + <input class="caption" name="{{name}}[{{forloop.index0}}][caption]" value="{{image.caption | escape}}" placeholder="Email"> + </li> + {% endfor %} + </ol> + </div> + {% elsif type == 'triple-captioned-image-list' %} + <div class="image-list group loaded"> + <div class="fields"> + <div class="add-image-button"> + <input type="file" accept="image/*" multiple> + <button>+ Add images</button> + </div> + <input class="add-url" type="text" placeholder="+ Add URL"> + </div> + + <script type="text/html" class="image-template"> + <li class="image-element"> + <img> + <button class="remove">x</button> + <input class="uri" type="hidden" name="{{name}}[][uri]" value=""> + <input type="hidden" name="{{name}}[][width]" class="image-width"> + <input type="hidden" name="{{name}}[][height]" class="image-height"> + <input class="caption" name="{{name}}[][label]" placeholder="Label"> + <input class="caption" name="{{name}}[][caption]" placeholder="Caption"> + <input class="caption" name="{{name}}[][code]" placeholder="Code"> + </li> + </script> + + <ol> + {% for image in spec.value %} + <li class="image-element"> + <img src="{{image.uri}}" alt="{{image.caption | strip_html}}"> + <button class="remove">x</button> + <input type="hidden" name="{{name}}[{{forloop.index0}}][uri]" value="{{image.uri}}"> + <input type="hidden" name="{{name}}[{{forloop.index0}}][width]" value="{{image.width}}" class="image-width"> + <input type="hidden" name="{{name}}[{{forloop.index0}}][height]" value="{{image.height}}" class="image-height"> + <input class="caption" name="{{name}}[{{forloop.index0}}][label]" value="{{image.label | escape}}" placeholder="Label"> + <input class="caption" name="{{name}}[{{forloop.index0}}][caption]" value="{{image.caption | escape}}" placeholder="Caption"> + <input class="caption" name="{{name}}[{{forloop.index0}}][code]" value="{{image.code | escape}}" placeholder="Code"> + </li> + {% endfor %} + </ol> + </div> + {% elsif type == 'meta' %} + <input class="hidden" type="hidden" name="{{name}}" value="{{spec.value}}"> + {% else %} + <p><pre style="color: red">Admin template doesn't support '{{type}}' properties!</pre></p> + {% endif %} + </div> + +{% endfor %} diff --git a/lib/okpush/templates/partials/tail.liquid b/lib/okpush/templates/partials/tail.liquid new file mode 100644 index 00000000..522023b5 --- /dev/null +++ b/lib/okpush/templates/partials/tail.liquid @@ -0,0 +1,14 @@ + </div> {% comment %} closes container tag {% endcomment %} + <div id="progress"></div> + <div id="uploadConfig" + data-image-maxbytes="{{meta.services.s3.image.maxbytes}}" + data-audio-maxbytes="{{meta.services.s3.audio.maxbytes}}" + data-video-maxbytes="{{meta.services.s3.video.maxbytes}}"></div> + </body> + <script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> + <script src="//cdnjs.cloudflare.com/ajax/libs/lodash.js/3.6.0/lodash.min.js"></script> + <script src="/admin/js/jqueryui-draggable.js"></script> + <script src="/admin/js/upload.js"></script> + <script src="/admin/js/parser.js"></script> + <script src="/admin/js/app.js"></script> +</html> diff --git a/lib/okpush/test_certs/CertificateSigningRequest.certSigningRequest b/lib/okpush/test_certs/CertificateSigningRequest.certSigningRequest new file mode 100644 index 00000000..3a4074ae --- /dev/null +++ b/lib/okpush/test_certs/CertificateSigningRequest.certSigningRequest @@ -0,0 +1,16 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIICkjCCAXoCAQAwTTEdMBsGCSqGSIb3DQEJARYOanVsZXNAb2tmb2MudXMxHzAd +BgNVBAMMFkp1bGlhbiBMYXBsYWNlIERFViBLRVkxCzAJBgNVBAYTAlVTMIIBIjAN +BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArrpXz8B0q/tJ2+N6gjK2zLNPIIlA +lzrE46TIkY10xo9jW3vBRgZL9V5xKtmCWf3KfR2I7KioyqqnZvz6++TV/M3rZptJ +XN8yMmG4mjTqgC1V2SubYyUZqpqkfyLK1ePimsg09UUNxwNcIyBJun+f8wXqz7HK +Kieiq+d7oTs+WJbWdkIftxe7hjL2n93PI1LLCF15Cx8s7XP7R7QsTGMvHUzYYHiL +4bc+r54jpZ28onVAwdYx+p3WkKzdFGjkrIl++IXmyJr0E7jilfrKwMWi4tvCKgXV +KH6VmWLIKOtfKVMdIK0WQ9ij3kRHfEwgN/djC3M2UWFEGLx1d7FuL5c+nQIDAQAB +oAAwDQYJKoZIhvcNAQELBQADggEBAIGOwvuW7HaOb1h+nkpIDTHNRN6Lw3MTg8nU +68skeSFatC8lC4e//Gsl1z8iyR7amhoMrZ29f4Tgs5w8NFgcVmzdp5CL126WA3qV +Qu+xZxhgwUnLRxtpDUVtTdJGaYNmowpm4mqs5YFg6WCIhWg+kuEJJ8MSOkYHtyAx +HIpbfjeSW69VGelr+vGZ6Jf07UCquwJCJR4WmsIyruJhvf2CQMPsT3bXKrhu/sWg +WyjjCjXVLeDOO/tiRMnPteCcDZPsXKyi9PPkSF4u591YoUL0UxjlzlignBT4FSyq +s8kdmrEHWIW1BAscmNVny2YTuGjbFos/GBSMDeJvoXdvPS2kRiQ= +-----END CERTIFICATE REQUEST----- diff --git a/lib/okpush/test_certs/Certificates.p12 b/lib/okpush/test_certs/Certificates.p12 Binary files differnew file mode 100644 index 00000000..6e61158f --- /dev/null +++ b/lib/okpush/test_certs/Certificates.p12 diff --git a/lib/okpush/test_certs/aps_development.cer b/lib/okpush/test_certs/aps_development.cer Binary files differnew file mode 100644 index 00000000..8d55dd44 --- /dev/null +++ b/lib/okpush/test_certs/aps_development.cer diff --git a/lib/okpush/test_certs/overlayer_dev_cert.pem b/lib/okpush/test_certs/overlayer_dev_cert.pem new file mode 100644 index 00000000..3758dd23 --- /dev/null +++ b/lib/okpush/test_certs/overlayer_dev_cert.pem @@ -0,0 +1,32 @@ +-----BEGIN CERTIFICATE----- +MIIFiTCCBHGgAwIBAgIIaNpEjSUbbWwwDQYJKoZIhvcNAQEFBQAwgZYxCzAJBgNV +BAYTAlVTMRMwEQYDVQQKDApBcHBsZSBJbmMuMSwwKgYDVQQLDCNBcHBsZSBXb3Js +ZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9uczFEMEIGA1UEAww7QXBwbGUgV29ybGR3 +aWRlIERldmVsb3BlciBSZWxhdGlvbnMgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw +HhcNMTYxMDA0MjAxMTM3WhcNMTcxMDA0MjAxMTM3WjCBiDEiMCAGCgmSJomT8ixk +AQEMEnVzLm9rZm9jLm92ZXJsYXllcjFAMD4GA1UEAww3QXBwbGUgRGV2ZWxvcG1l +bnQgSU9TIFB1c2ggU2VydmljZXM6IHVzLm9rZm9jLm92ZXJsYXllcjETMBEGA1UE +CwwKNUVIOVc1WDVTSjELMAkGA1UEBhMCVVMwggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQCuulfPwHSr+0nb43qCMrbMs08giUCXOsTjpMiRjXTGj2Nbe8FG +Bkv1XnEq2YJZ/cp9HYjsqKjKqqdm/Pr75NX8zetmm0lc3zIyYbiaNOqALVXZK5tj +JRmqmqR/IsrV4+KayDT1RQ3HA1wjIEm6f5/zBerPscoqJ6Kr53uhOz5YltZ2Qh+3 +F7uGMvaf3c8jUssIXXkLHyztc/tHtCxMYy8dTNhgeIvhtz6vniOlnbyidUDB1jH6 +ndaQrN0UaOSsiX74hebImvQTuOKV+srAxaLi28IqBdUofpWZYsgo618pUx0grRZD +2KPeREd8TCA392MLczZRYUQYvHV3sW4vlz6dAgMBAAGjggHlMIIB4TAdBgNVHQ4E +FgQUckSbISzH7PTE7R9gsIb/sxKzHrgwCQYDVR0TBAIwADAfBgNVHSMEGDAWgBSI +JxcJqbYYYIvs67r2R1nFUlSjtzCCAQ8GA1UdIASCAQYwggECMIH/BgkqhkiG92Nk +BQEwgfEwgcMGCCsGAQUFBwICMIG2DIGzUmVsaWFuY2Ugb24gdGhpcyBjZXJ0aWZp +Y2F0ZSBieSBhbnkgcGFydHkgYXNzdW1lcyBhY2NlcHRhbmNlIG9mIHRoZSB0aGVu +IGFwcGxpY2FibGUgc3RhbmRhcmQgdGVybXMgYW5kIGNvbmRpdGlvbnMgb2YgdXNl +LCBjZXJ0aWZpY2F0ZSBwb2xpY3kgYW5kIGNlcnRpZmljYXRpb24gcHJhY3RpY2Ug +c3RhdGVtZW50cy4wKQYIKwYBBQUHAgEWHWh0dHA6Ly93d3cuYXBwbGUuY29tL2Fw +cGxlY2EvME0GA1UdHwRGMEQwQqBAoD6GPGh0dHA6Ly9kZXZlbG9wZXIuYXBwbGUu +Y29tL2NlcnRpZmljYXRpb25hdXRob3JpdHkvd3dkcmNhLmNybDALBgNVHQ8EBAMC +B4AwEwYDVR0lBAwwCgYIKwYBBQUHAwIwEAYKKoZIhvdjZAYDAQQCBQAwDQYJKoZI +hvcNAQEFBQADggEBAEuQKbpqxQPnazN4A0NVB7YRr2M2Hk5DtV4M/QT8OFDY1IsA +s4U8NmI9AMgoszIdLG3n4x2Y4mm6yTYDEMQpaJRbir9Fx6WcZQ4GB72iZ1M/1fkC +79Yq17yzxM9Awf2igx1EkZhzy6Oq+7cUX9Jbz5IQrPyfXe5hpaOmY4jnWgSlaiwJ +6bBhFHwhJY9ekAFOyRnZS7hQD4mLOBZLti/lH4Z2zadd21DbM/uhUvLJgYpIRplP +hoFxV5bev5vv9A0dbYw6ERa3+aG8HHmP9N4u1/JxHIX2VlYzzLj1l7Tlps6QG6/r +4CsJBIfpGjxwznrYJBN5ox1KlFxNLwD3UNpLhOQ= +-----END CERTIFICATE----- diff --git a/lib/okpush/test_certs/overlayer_dev_key.pem b/lib/okpush/test_certs/overlayer_dev_key.pem new file mode 100644 index 00000000..30cb696d --- /dev/null +++ b/lib/okpush/test_certs/overlayer_dev_key.pem @@ -0,0 +1,68 @@ +Bag Attributes + friendlyName: Apple Development IOS Push Services: us.okfoc.overlayer + localKeyID: 72 44 9B 21 2C C7 EC F4 C4 ED 1F 60 B0 86 FF B3 12 B3 1E B8 +subject=/UID=us.okfoc.overlayer/CN=Apple Development IOS Push Services: us.okfoc.overlayer/OU=5EH9W5X5SJ/C=US +issuer=/C=US/O=Apple Inc./OU=Apple Worldwide Developer Relations/CN=Apple Worldwide Developer Relations Certification Authority +-----BEGIN CERTIFICATE----- +MIIFiTCCBHGgAwIBAgIIaNpEjSUbbWwwDQYJKoZIhvcNAQEFBQAwgZYxCzAJBgNV +BAYTAlVTMRMwEQYDVQQKDApBcHBsZSBJbmMuMSwwKgYDVQQLDCNBcHBsZSBXb3Js +ZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9uczFEMEIGA1UEAww7QXBwbGUgV29ybGR3 +aWRlIERldmVsb3BlciBSZWxhdGlvbnMgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw +HhcNMTYxMDA0MjAxMTM3WhcNMTcxMDA0MjAxMTM3WjCBiDEiMCAGCgmSJomT8ixk +AQEMEnVzLm9rZm9jLm92ZXJsYXllcjFAMD4GA1UEAww3QXBwbGUgRGV2ZWxvcG1l +bnQgSU9TIFB1c2ggU2VydmljZXM6IHVzLm9rZm9jLm92ZXJsYXllcjETMBEGA1UE +CwwKNUVIOVc1WDVTSjELMAkGA1UEBhMCVVMwggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQCuulfPwHSr+0nb43qCMrbMs08giUCXOsTjpMiRjXTGj2Nbe8FG +Bkv1XnEq2YJZ/cp9HYjsqKjKqqdm/Pr75NX8zetmm0lc3zIyYbiaNOqALVXZK5tj +JRmqmqR/IsrV4+KayDT1RQ3HA1wjIEm6f5/zBerPscoqJ6Kr53uhOz5YltZ2Qh+3 +F7uGMvaf3c8jUssIXXkLHyztc/tHtCxMYy8dTNhgeIvhtz6vniOlnbyidUDB1jH6 +ndaQrN0UaOSsiX74hebImvQTuOKV+srAxaLi28IqBdUofpWZYsgo618pUx0grRZD +2KPeREd8TCA392MLczZRYUQYvHV3sW4vlz6dAgMBAAGjggHlMIIB4TAdBgNVHQ4E +FgQUckSbISzH7PTE7R9gsIb/sxKzHrgwCQYDVR0TBAIwADAfBgNVHSMEGDAWgBSI +JxcJqbYYYIvs67r2R1nFUlSjtzCCAQ8GA1UdIASCAQYwggECMIH/BgkqhkiG92Nk +BQEwgfEwgcMGCCsGAQUFBwICMIG2DIGzUmVsaWFuY2Ugb24gdGhpcyBjZXJ0aWZp +Y2F0ZSBieSBhbnkgcGFydHkgYXNzdW1lcyBhY2NlcHRhbmNlIG9mIHRoZSB0aGVu +IGFwcGxpY2FibGUgc3RhbmRhcmQgdGVybXMgYW5kIGNvbmRpdGlvbnMgb2YgdXNl +LCBjZXJ0aWZpY2F0ZSBwb2xpY3kgYW5kIGNlcnRpZmljYXRpb24gcHJhY3RpY2Ug +c3RhdGVtZW50cy4wKQYIKwYBBQUHAgEWHWh0dHA6Ly93d3cuYXBwbGUuY29tL2Fw +cGxlY2EvME0GA1UdHwRGMEQwQqBAoD6GPGh0dHA6Ly9kZXZlbG9wZXIuYXBwbGUu +Y29tL2NlcnRpZmljYXRpb25hdXRob3JpdHkvd3dkcmNhLmNybDALBgNVHQ8EBAMC +B4AwEwYDVR0lBAwwCgYIKwYBBQUHAwIwEAYKKoZIhvdjZAYDAQQCBQAwDQYJKoZI +hvcNAQEFBQADggEBAEuQKbpqxQPnazN4A0NVB7YRr2M2Hk5DtV4M/QT8OFDY1IsA +s4U8NmI9AMgoszIdLG3n4x2Y4mm6yTYDEMQpaJRbir9Fx6WcZQ4GB72iZ1M/1fkC +79Yq17yzxM9Awf2igx1EkZhzy6Oq+7cUX9Jbz5IQrPyfXe5hpaOmY4jnWgSlaiwJ +6bBhFHwhJY9ekAFOyRnZS7hQD4mLOBZLti/lH4Z2zadd21DbM/uhUvLJgYpIRplP +hoFxV5bev5vv9A0dbYw6ERa3+aG8HHmP9N4u1/JxHIX2VlYzzLj1l7Tlps6QG6/r +4CsJBIfpGjxwznrYJBN5ox1KlFxNLwD3UNpLhOQ= +-----END CERTIFICATE----- +Bag Attributes + friendlyName: Julian Laplace DEV KEY + localKeyID: 72 44 9B 21 2C C7 EC F4 C4 ED 1F 60 B0 86 FF B3 12 B3 1E B8 +Key Attributes: <No Attributes> +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEArrpXz8B0q/tJ2+N6gjK2zLNPIIlAlzrE46TIkY10xo9jW3vB +RgZL9V5xKtmCWf3KfR2I7KioyqqnZvz6++TV/M3rZptJXN8yMmG4mjTqgC1V2Sub +YyUZqpqkfyLK1ePimsg09UUNxwNcIyBJun+f8wXqz7HKKieiq+d7oTs+WJbWdkIf +txe7hjL2n93PI1LLCF15Cx8s7XP7R7QsTGMvHUzYYHiL4bc+r54jpZ28onVAwdYx ++p3WkKzdFGjkrIl++IXmyJr0E7jilfrKwMWi4tvCKgXVKH6VmWLIKOtfKVMdIK0W +Q9ij3kRHfEwgN/djC3M2UWFEGLx1d7FuL5c+nQIDAQABAoIBAQCdKIiBGccXRa/a +MxJm9zyKhCGAz1MkczzS0M2Cdax6SFCC4Z4m4NUDfw3lD7z6Opcd0CnvS4h+ud5W +eOc7GEoM2B6KrNPgz/Szckf7k+r0nVJhQqLCG4WpeOzKWb39grq4o6lWjuTkgzYl +dg46zD5O9ZyNcYm4PuxHGK+Phtz+CjkEx5Y6WqoBvgA10A+pHBSMAZ2uRznGHJmQ +xF9HHdLLKwtZemRGEdZaC510fpBDbgapLzZqy495/2I3zlOFSW9thWhJ/PO45AH1 +bqtRRq0z0CU94QynulrcvZlyFVF5msDyzBI6B7oZAumC5y9s/v6/jE2rIr1uNx2W +e3Zbh+21AoGBAOAUhkD932XhDKtatDpBDQ8va9ZiiQCuO8VFP1wck2U18ovy6DQn +A3JJUoC/d4K7brCQ5CgzACI0tgen99u78qN3F3caapwJMbQ3LAdZA6U5tZq/7poA +FrvEs6vAToe+HhLkWxvctO0sEGtZEDUptRRa5cGoMAV2FyLvtSHgPk+jAoGBAMee +GgcunO+XBI8HVgC9GTLxJWUuXpwYslI9uDXTVFsJ4jqEAk0R4qA8wGa6VpXS7yea +3Vd8qG9C8yVIuSoajyH4FYQ6rzfbUDoYHsKkJUabJWTK08VPb2RVae6vDQmXqyw3 +X6QH+no33inyD5bEqaSg7lTcQ03kX3shVj+kTBy/AoGBAL0slqsHqTI5bJi9WqpT +gNLxFflH9qG8dnl0nKkG9ujbQokj6SUeSqqRfDV1b0/PLQOjTvshE1vNZF/STk4T +viSCxiynAJn6qRub+G+7lnhG4CplHuqkaIzc41J8Z8/xkTIh10kOazBPowz9g9Fe +BKHKm22CsAfsSlwUQrb2uaRBAoGAI660UmJXtVBWhUa3bexfi8ale5+9U8PqZF8a +Ba0gICH1KaadTq+Kxj/12KcogSGylG7PcCsdZL4f/qMwTkFvIpBZMEjZN2/huHDF +Vt/GtgdXuNu03UlkzRejlwH9n6BX/dBsLUMr2BsSgIb/mGDRPldyIwM4mLzhAH6m +DzxMSrsCgYB9LP/AsmUY67lajzYO18Xu9YTNzIKwovD/RhRHRz0kOI7QQRSXNtGZ +RwzV9OCeRXGLrC2n6w9KnGx8d6pZKElzDkN7+Ql9y2ALmzaInmugTyoNxBPeU3GB +DxN5IIkt2G1rexC/SaQ9WOokiQSKJIZGRHR/WHKpXC/mBHxq7CENkQ== +-----END RSA PRIVATE KEY----- |
