Files
mao1/shenyan-app/bridge-local/apns.js

65 lines
1.8 KiB
JavaScript
Raw Normal View History

const fs = require('fs');
const path = require('path');
const jwt = require('jsonwebtoken');
const http2 = require('http2');
const TEAM_ID = 'ZZMLW32PHH';
const KEY_ID = '3VJ5X6V9Q8';
const BUNDLE_ID = 'com.shenyan.room';
const KEY_PATH = path.join(__dirname, 'AuthKey_3VJ5X6V9Q8.p8');
// cache JWT for ~50min (max 1hr)
let cachedToken = null;
let tokenExpiry = 0;
function getJWT() {
if (cachedToken && Date.now() < tokenExpiry) return cachedToken;
const key = fs.readFileSync(KEY_PATH, 'utf-8');
const now = Math.floor(Date.now() / 1000);
const payload = { iss: TEAM_ID, iat: now };
const headers = { alg: 'ES256', kid: KEY_ID };
cachedToken = jwt.sign(payload, key, { algorithm: 'ES256', header: headers, expiresIn: '50m' });
tokenExpiry = now + 3000; // 50 min
return cachedToken;
}
function sendAPNs(deviceToken, title, body) {
return new Promise((resolve, reject) => {
const payload = JSON.stringify({
aps: {
alert: { title: title || '沈晏', body: body || '' },
sound: 'default',
badge: 1,
},
});
const client = http2.connect('https://api.sandbox.push.apple.com:443');
client.on('error', err => reject(err));
const req = client.request({
':method': 'POST',
':path': `/3/device/${deviceToken}`,
'authorization': `bearer ${getJWT()}`,
'apns-topic': BUNDLE_ID,
'apns-push-type': 'alert',
'apns-expiration': '0',
});
req.on('response', (headers) => {
const status = headers[':status'];
let data = '';
req.on('data', c => data += c);
req.on('end', () => {
client.close();
if (status === 200) resolve({ ok: true, status });
else reject(new Error(`APNs ${status}: ${data}`));
});
});
req.on('error', err => { client.close(); reject(err); });
req.end(payload);
});
}
module.exports = { sendAPNs };