diff --git a/backend/routes/photos.js b/backend/routes/photos.js index e54f8af..6494191 100644 --- a/backend/routes/photos.js +++ b/backend/routes/photos.js @@ -22,7 +22,109 @@ const storage = multer.memoryStorage(); const upload = multer({ storage }); -// ... (OAuth endpoints remain the same) ... +// OAuth Endpoints +router.get("/auth/url", (req, res) => { + const url = oauth2Client.generateAuthUrl({ + access_type: "offline", + scope: PHOTOS_SCOPE, + prompt: "consent", + }); + res.json({ url }); +}); + +router.get("/auth/callback", async (req, res) => { + const { code } = req.query; + try { + const { tokens } = await oauth2Client.getToken(code); + await GoogleConfig.findOneAndUpdate( + { key: "photos_auth" }, + { tokens }, + { upsert: true } + ); + res.send("
You can close this window and return to the application.
"); + } catch (error) { + console.error("Auth error:", error); + res.status(500).send("Authentication failed"); + } +}); + +router.get("/status", async (req, res) => { + try { + const config = await GoogleConfig.findOne({ key: "photos_auth" }); + res.json({ connected: !!config && !!config.tokens }); + } catch (error) { + res.status(500).json({ message: "Failed to check status" }); + } +}); + +router.get("/disconnect", async (req, res) => { + try { + await GoogleConfig.deleteOne({ key: "photos_auth" }); + res.json({ ok: true }); + } catch (error) { + res.status(500).json({ message: "Failed to disconnect" }); + } +}); + +// Fetching Routes +router.get("/", async (req, res) => { + try { + const filter = {}; + if (req.query.active === "true") { + filter.active = true; + } + + // Get local photos + const localPhotos = await Photo.find(filter).sort({ createdAt: -1 }); + + // Check if Google Photos is connected + const config = await GoogleConfig.findOne({ key: "photos_auth" }); + + if (config && config.tokens) { + try { + oauth2Client.setCredentials(config.tokens); + + // Refresh token if needed + if (config.tokens.expiry_date <= Date.now()) { + const { tokens } = await oauth2Client.refreshAccessToken(); + config.tokens = tokens; + await config.save(); + oauth2Client.setCredentials(tokens); + } + + const tokensInfo = await oauth2Client.getAccessToken(); + const accessToken = tokensInfo.token; + + const response = await axios.get( + "https://photoslibrary.googleapis.com/v1/mediaItems?pageSize=100", + { + headers: { Authorization: `Bearer ${accessToken}` }, + } + ); + + const googlePhotos = (response.data.mediaItems || []).map((item) => ({ + id: item.id, + url: `${item.baseUrl}=w2048-h1024`, + caption: item.description || "Google Photo", + active: true, + source: "google" + })); + + // Combine local and google photos + return res.json([...localPhotos, ...googlePhotos]); + } catch (gError) { + console.error("Error fetching Google Photos:", gError.message); + // Fallback to local photos only if Google fails + return res.json(localPhotos); + } + } + + res.json(localPhotos); + } catch (error) { + console.error("Fetch error:", error); + res.status(500).json({ message: "Failed to fetch photos" }); + } +}); router.post("/upload", upload.single("file"), async (req, res) => { try {