mirror of
https://github.com/samuelclay/NewsBlur.git
synced 2025-08-31 21:41:33 +00:00
Switching to key-based APNS cert.
This commit is contained in:
parent
b32201a570
commit
9cd40a71e8
4 changed files with 70 additions and 32 deletions
4
Makefile
4
Makefile
|
@ -77,6 +77,10 @@ lint:
|
|||
docker exec -it newsblur_web isort --profile black .
|
||||
docker exec -it newsblur_web black --line-length 110 .
|
||||
docker exec -it newsblur_web flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics --exclude=venv
|
||||
|
||||
deps:
|
||||
docker exec -t newsblur_web pip install -U uv
|
||||
docker exec -t newsblur_web uv pip install -r requirements.txt
|
||||
|
||||
jekyll_build:
|
||||
cd blog && JEKYLL_ENV=production bundle exec jekyll build
|
||||
|
|
|
@ -7,9 +7,8 @@ import urllib.parse
|
|||
|
||||
import mongoengine as mongo
|
||||
import redis
|
||||
from apns2.client import APNsClient
|
||||
from apns2.errors import BadDeviceToken, DeviceTokenNotForTopic, Unregistered
|
||||
from apns2.payload import Payload
|
||||
from pyapns_client import APNSClient, IOSPayloadAlert, IOSPayload, IOSNotification
|
||||
from pyapns_client import APNSDeviceException, APNSServerException, APNSProgrammingException, UnregisteredException
|
||||
from bs4 import BeautifulSoup
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import User
|
||||
|
@ -306,7 +305,7 @@ class MUserFeedNotification(mongo.Document):
|
|||
# 4. Save the key file to secrets/certificates/ios/apns_key.p8
|
||||
# 5. Note your Team ID and Key ID
|
||||
# 6. Deploy: aps -l work -t apns,repo,celery
|
||||
|
||||
|
||||
# Legacy certificate method (kept for reference):
|
||||
# 0. Upgrade to latest openssl: brew install openssl
|
||||
# 1. Create certificate signing request in Keychain Access
|
||||
|
@ -321,22 +320,24 @@ class MUserFeedNotification(mongo.Document):
|
|||
# 7. cat aps.pem aps_key.noenc.pem > aps.p12.pem
|
||||
# 8. Verify: openssl s_client -connect gateway.push.apple.com:2195 -cert aps.p12.pem
|
||||
# 9. Deploy: aps -l work -t apns,repo,celery
|
||||
|
||||
# Using token-based authentication (modern method)
|
||||
|
||||
# Using token-based authentication (modern method with pyapns-client)
|
||||
key_file_path = "/srv/newsblur/config/certificates/apns_key.p8"
|
||||
apns = APNsClient(
|
||||
team_id=settings.APNS_TEAM_ID,
|
||||
auth_key_id=settings.APNS_KEY_ID,
|
||||
auth_key_path=key_file_path,
|
||||
use_sandbox=tokens.use_sandbox
|
||||
)
|
||||
|
||||
notification_title_only = is_true(user.profile.preference_value("notification_title_only"))
|
||||
title, subtitle, body = self.title_and_body(story, usersub, notification_title_only)
|
||||
image_url = None
|
||||
if len(story["image_urls"]):
|
||||
image_url = story["image_urls"][0]
|
||||
# print image_url
|
||||
|
||||
# Create APNS client
|
||||
apns = APNSClient(
|
||||
mode=APNSClient.MODE_DEV if tokens.use_sandbox else APNSClient.MODE_PROD,
|
||||
root_cert_path=None,
|
||||
auth_key_path=key_file_path,
|
||||
auth_key_id=settings.APNS_KEY_ID,
|
||||
team_id=settings.APNS_TEAM_ID
|
||||
)
|
||||
|
||||
confirmed_ios_tokens = []
|
||||
for token in tokens.ios_tokens:
|
||||
|
@ -345,27 +346,44 @@ class MUserFeedNotification(mongo.Document):
|
|||
"~BMStory notification by iOS: ~FY~SB%s~SN~BM~FY/~SB%s"
|
||||
% (story["story_title"][:50], usersub.feed.feed_title[:50]),
|
||||
)
|
||||
payload = Payload(
|
||||
alert={"title": title, "subtitle": subtitle, "body": body},
|
||||
|
||||
# Create payload using helper classes
|
||||
alert = IOSPayloadAlert(title=title, subtitle=subtitle, body=body)
|
||||
custom_data = {
|
||||
"story_hash": story["story_hash"],
|
||||
"story_feed_id": story["story_feed_id"],
|
||||
}
|
||||
if image_url:
|
||||
custom_data["image_url"] = image_url
|
||||
|
||||
payload = IOSPayload(
|
||||
alert=alert,
|
||||
custom=custom_data,
|
||||
category="STORY_CATEGORY",
|
||||
mutable_content=True,
|
||||
custom={
|
||||
"story_hash": story["story_hash"],
|
||||
"story_feed_id": story["story_feed_id"],
|
||||
"image_url": image_url,
|
||||
},
|
||||
mutable_content=image_url is not None
|
||||
)
|
||||
notification = IOSNotification(payload=payload, topic="com.newsblur.NewsBlur")
|
||||
|
||||
try:
|
||||
apns.send_notification(token, payload, topic="com.newsblur.NewsBlur")
|
||||
except (BadDeviceToken, Unregistered, DeviceTokenNotForTopic):
|
||||
logging.user(user, "~BMiOS token expired: ~FR~SB%s" % (token[:50]))
|
||||
else:
|
||||
apns.push(notification=notification, device_token=token)
|
||||
confirmed_ios_tokens.append(token)
|
||||
if settings.DEBUG:
|
||||
logging.user(
|
||||
user,
|
||||
"~BMiOS token good: ~FB~SB%s / %s" % (token[:50], len(confirmed_ios_tokens)),
|
||||
)
|
||||
except UnregisteredException as e:
|
||||
logging.user(user, "~BMiOS token unregistered: ~FR~SB%s (since %s)" % (token[:50], e.timestamp_datetime))
|
||||
except APNSDeviceException as e:
|
||||
logging.user(user, "~BMiOS token invalid: ~FR~SB%s" % (token[:50]))
|
||||
except APNSServerException as e:
|
||||
logging.user(user, "~BMiOS notification server error: ~FR~SB%s - %s" % (token[:50], str(e)))
|
||||
except APNSProgrammingException as e:
|
||||
logging.user(user, "~BMiOS notification programming error: ~FR~SB%s - %s" % (token[:50], str(e)))
|
||||
except Exception as e:
|
||||
logging.user(user, "~BMiOS notification error: ~FR~SB%s - %s" % (token[:50], str(e)))
|
||||
finally:
|
||||
apns.close()
|
||||
|
||||
if len(confirmed_ios_tokens) < len(tokens.ios_tokens):
|
||||
tokens.ios_tokens = confirmed_ios_tokens
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
amqp==2.6.1
|
||||
apns2==0.7.2
|
||||
appdirs==1.4.4
|
||||
asgiref==3.3.4
|
||||
attrs==21.1.0
|
||||
|
@ -46,13 +45,8 @@ Flask-BasicAuth==0.2.0
|
|||
future==0.18.2
|
||||
gunicorn==21.2.0
|
||||
gevent==22.10.2
|
||||
h2==2.6.2
|
||||
hiredis==1.1.0
|
||||
hpack==3.0.0
|
||||
httplib2==0.18.1
|
||||
httpx==0.27.2
|
||||
hyper==0.7.0
|
||||
hyperframe==3.2.0
|
||||
idna==2.10
|
||||
image==1.5.33
|
||||
iniconfig==1.1.1
|
||||
|
@ -81,9 +75,9 @@ pluggy==0.13.1
|
|||
psutil==5.7.3
|
||||
psycopg2==2.9.2
|
||||
py==1.10.0
|
||||
pyapns-client==2.0.6
|
||||
pyasn1==0.4.8
|
||||
pycparser==2.20
|
||||
PyJWT==1.7.1
|
||||
pymongo>=3,<4
|
||||
PyMySQL==0.10.1
|
||||
pynliner==0.8.0
|
||||
|
|
|
@ -61,6 +61,17 @@ class AppDirectoriesFinder(PipelineAppDirectoriesFinder):
|
|||
"*LICENSE*",
|
||||
"*README*",
|
||||
]
|
||||
|
||||
def find_files(self, storage, path=None, all=False):
|
||||
"""
|
||||
Override to properly handle wildcard patterns like 'underscore-*.js'
|
||||
"""
|
||||
path = path or ''
|
||||
for pattern in self.find_pattern_matches(path):
|
||||
for path in storage.listdir(pattern[0])[1]:
|
||||
if self.is_ignored(path, pattern[0]):
|
||||
continue
|
||||
yield path, storage
|
||||
|
||||
|
||||
class FileSystemFinder(PipelineFileSystemFinder):
|
||||
|
@ -115,3 +126,14 @@ class FileSystemFinder(PipelineFileSystemFinder):
|
|||
# 'Gemfile*',
|
||||
"node_modules",
|
||||
]
|
||||
|
||||
def find_files(self, storage, path=None, all=False):
|
||||
"""
|
||||
Override to properly handle wildcard patterns like 'underscore-*.js'
|
||||
"""
|
||||
path = path or ''
|
||||
for pattern in self.find_pattern_matches(path):
|
||||
for path in storage.listdir(pattern[0])[1]:
|
||||
if self.is_ignored(path, pattern[0]):
|
||||
continue
|
||||
yield path, storage
|
||||
|
|
Loading…
Add table
Reference in a new issue