-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathapi_backend.py
More file actions
263 lines (213 loc) · 7.86 KB
/
Copy pathapi_backend.py
File metadata and controls
263 lines (213 loc) · 7.86 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
#!/usr/bin/env python3
"""
iPhone Availability Tracker - Backend API
Converts the original Python script into a REST API for iOS app consumption
"""
from fastapi import FastAPI, HTTPException, BackgroundTasks
from pydantic import BaseModel
from typing import List, Optional, Dict
import asyncio
import httpx
from datetime import datetime
import json
import re
import os
# Import our existing iPhone checker logic
from iphone_checker import iPhoneChecker
app = FastAPI(
title="iPhone Availability Tracker API",
description="Backend API for iPhone availability monitoring with push notifications",
version="1.0.0"
)
# Pydantic models for API requests/responses
class UserPreferences(BaseModel):
user_id: str
target_model: str = "iPhone 17 Pro Max"
target_color: str = "Orange"
target_storage: str = "256GB"
monitored_stores: List[str] = [] # Store IDs to monitor
notification_enabled: bool = True
push_token: Optional[str] = None # APNs device token
class AvailabilityResult(BaseModel):
status: str
available_stores: List[str]
store_results: Dict
message: str
timestamp: str
user_id: Optional[str] = None
class NotificationPayload(BaseModel):
title: str
body: str
badge: int = 1
sound: str = "default"
data: Optional[Dict] = None
# In-memory storage (replace with database in production)
user_preferences = {}
availability_cache = {}
@app.get("/")
async def root():
"""Health check endpoint"""
return {
"message": "iPhone Availability Tracker API",
"status": "running",
"version": "1.0.0"
}
@app.post("/users/{user_id}/preferences")
async def update_user_preferences(user_id: str, preferences: UserPreferences):
"""Update user notification preferences"""
preferences.user_id = user_id
user_preferences[user_id] = preferences
return {
"message": "Preferences updated successfully",
"user_id": user_id,
"preferences": preferences.dict()
}
@app.get("/users/{user_id}/preferences")
async def get_user_preferences(user_id: str):
"""Get user preferences"""
if user_id not in user_preferences:
raise HTTPException(status_code=404, detail="User preferences not found")
return user_preferences[user_id].dict()
@app.get("/availability/{user_id}", response_model=AvailabilityResult)
async def check_user_availability(user_id: str, background_tasks: BackgroundTasks):
"""Check iPhone availability for specific user preferences"""
# Get user preferences
if user_id not in user_preferences:
# Use default preferences if none exist
prefs = UserPreferences(user_id=user_id)
user_preferences[user_id] = prefs
else:
prefs = user_preferences[user_id]
# Run availability check using our existing checker
checker = iPhoneChecker()
# Override target specs from user preferences
checker.target_model = prefs.target_model
checker.target_color = prefs.target_color
checker.target_storage = prefs.target_storage
# Filter stores if user specified specific ones
if prefs.monitored_stores:
filtered_stores = {
store_id: store_info
for store_id, store_info in checker.houston_stores.items()
if store_id in prefs.monitored_stores
}
checker.houston_stores = filtered_stores
# Check availability
result = checker.check_availability()
result["user_id"] = user_id
# Cache the result
availability_cache[user_id] = result
# If device is available and notifications enabled, send push notification
if result["status"] == "available" and prefs.notification_enabled and prefs.push_token:
background_tasks.add_task(
send_push_notification,
prefs.push_token,
result["available_stores"]
)
return AvailabilityResult(**result)
@app.get("/availability")
async def check_general_availability():
"""Check availability without user-specific preferences (public endpoint)"""
checker = iPhoneChecker()
result = checker.check_availability()
return AvailabilityResult(**result)
@app.get("/stores")
async def get_all_stores():
"""Get list of all monitored Apple Store locations"""
checker = iPhoneChecker()
stores = []
for store_id, store_info in checker.houston_stores.items():
stores.append({
"store_id": store_id,
"name": store_info["name"],
"address": store_info["address"],
"city": "Houston",
"state": "TX"
})
return {"stores": stores}
@app.post("/notifications/test/{user_id}")
async def test_notification(user_id: str):
"""Test push notification for a user"""
if user_id not in user_preferences:
raise HTTPException(status_code=404, detail="User not found")
prefs = user_preferences[user_id]
if not prefs.push_token:
raise HTTPException(status_code=400, detail="Push token not configured")
# Send test notification
await send_push_notification(
prefs.push_token,
["Highland Village"],
test_mode=True
)
return {"message": "Test notification sent"}
async def send_push_notification(device_token: str, available_stores: List[str], test_mode: bool = False):
"""Send Apple Push Notification"""
try:
# This would use APNs in production
# For demo purposes, we'll just log it
if test_mode:
message = "📱 Test notification from iPhone Availability Tracker!"
else:
stores_text = ", ".join(available_stores)
message = f"🍎 iPhone 17 Pro Max Orange 256GB available at: {stores_text}"
notification = NotificationPayload(
title="iPhone Available!",
body=message,
badge=1,
data={
"available_stores": available_stores,
"timestamp": datetime.now().isoformat()
}
)
print(f"📱 PUSH NOTIFICATION SENT:")
print(f" Device Token: {device_token[:20]}...")
print(f" Title: {notification.title}")
print(f" Body: {notification.body}")
print(f" Stores: {available_stores}")
# In production, use APNs:
# from apns2.client import APNsClient
# from apns2.payload import Payload
#
# client = APNsClient(credentials_path, use_sandbox=False)
# payload = Payload(alert=notification.body, badge=notification.badge)
# client.send_notification(device_token, payload)
return True
except Exception as e:
print(f"❌ Failed to send push notification: {str(e)}")
return False
@app.post("/schedule/start/{user_id}")
async def start_monitoring(user_id: str, interval_minutes: int = 30):
"""Start continuous monitoring for a user"""
# This would integrate with a job scheduler like Celery or APScheduler
# For demo, we'll just return the configuration
return {
"message": "Monitoring started",
"user_id": user_id,
"interval_minutes": interval_minutes,
"next_check": datetime.now().isoformat()
}
@app.delete("/schedule/stop/{user_id}")
async def stop_monitoring(user_id: str):
"""Stop continuous monitoring for a user"""
return {
"message": "Monitoring stopped",
"user_id": user_id
}
# CORS middleware for iOS app development
from fastapi.middleware.cors import CORSMiddleware
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # Configure properly for production
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
if __name__ == "__main__":
import uvicorn
uvicorn.run(
"api_backend:app",
host="0.0.0.0",
port=8000,
reload=True,
log_level="info"
)