|
1 | | -const { deviceLogin, hasValidToken, refreshToken } = require('./lib/auth') |
2 | | -const { getNote } = require('./lib/onenote') |
3 | | -const notify = require('./lib/notify') |
4 | | -const localStorage = require('./lib/store') |
5 | | -const { promises: fs } = require('fs') |
6 | | -const db = require('./db/persist') |
| 1 | +const { hasValidToken, refreshToken, deviceLogin } = require("./lib/auth"); |
| 2 | +const { getNote } = require("./lib/onenote"); |
| 3 | +const notify = require("./lib/notify"); |
| 4 | +const localStorage = require("./lib/store"); |
| 5 | +const { promises: fs } = require("fs"); |
| 6 | +const db = require("./db/persist"); |
7 | 7 | const { snakeCase } = require("snake-case"); |
8 | 8 |
|
9 | 9 | /** |
10 | 10 | * Lambda functions have ephemeral storage on the server in /tmp. |
11 | 11 | * Seed the MSAL Key Cache and localStorage with the latest from the database |
12 | 12 | */ |
13 | 13 | async function initCache(sectionHandle) { |
14 | | - // populate cache with db contents |
15 | | - const data = await db.getItem('cache') |
16 | | - await fs |
17 | | - .writeFile(process.env.CACHE_PATH, data) |
18 | | - .then(console.log('Restore Cache')) |
| 14 | + try { |
| 15 | + // populate cache with db contents |
| 16 | + let cacheData; |
| 17 | + try { |
| 18 | + cacheData = await db.getItem("cache"); |
| 19 | + } catch (error) { |
| 20 | + // Cache not found in DB, will start fresh |
| 21 | + cacheData = null; |
| 22 | + } |
| 23 | + |
| 24 | + // Handle corrupted cache data (object instead of string) |
| 25 | + if ( |
| 26 | + cacheData && |
| 27 | + typeof cacheData === "object" && |
| 28 | + Object.keys(cacheData).length === 0 |
| 29 | + ) { |
| 30 | + await db.setItem("cache", null); // Clear the corrupted cache |
| 31 | + cacheData = null; |
| 32 | + } |
| 33 | + |
| 34 | + if (cacheData && typeof cacheData === "string" && cacheData.trim()) { |
| 35 | + // Validate that we have proper MSAL cache structure |
| 36 | + try { |
| 37 | + const parsed = JSON.parse(cacheData); |
| 38 | + if (parsed.Account && parsed.RefreshToken && parsed.AccessToken) { |
| 39 | + const path = require("path"); |
| 40 | + const cachePath = path.resolve(process.env.CACHE_PATH); |
| 41 | + await fs.writeFile(cachePath, cacheData); |
| 42 | + } |
| 43 | + } catch (parseError) { |
| 44 | + // Cache data is not valid JSON, will start fresh |
| 45 | + } |
| 46 | + } |
19 | 47 |
|
20 | | - // populate local storage with login contents |
21 | | - // coerced to json |
22 | | - localStorage.initStore(); |
23 | | - const onenote = await db.getItem('onenote', true) |
24 | | - localStorage.setItem('onenote', onenote) |
| 48 | + // populate local storage with login contents |
| 49 | + // coerced to json |
| 50 | + localStorage.initStore(); |
25 | 51 |
|
26 | | - const count = await db.getItem(`${sectionHandle}_section_count`) |
27 | | - localStorage.setItem(`${sectionHandle}_section_count`, count) |
| 52 | + try { |
| 53 | + const onenote = await db.getItem("onenote", true); |
| 54 | + if (onenote) { |
| 55 | + localStorage.setItem("onenote", onenote); |
| 56 | + } |
| 57 | + } catch (error) { |
| 58 | + // OneNote data not found or corrupted, will be recreated on next auth |
| 59 | + } |
28 | 60 |
|
29 | | - const lastPage = await db.getItem(`${sectionHandle}_last_page`) |
30 | | - localStorage.setItem(`${sectionHandle}_last_page`, lastPage) |
| 61 | + try { |
| 62 | + const count = await db.getItem(`${sectionHandle}_section_count`); |
| 63 | + localStorage.setItem(`${sectionHandle}_section_count`, count); |
| 64 | + } catch (error) { |
| 65 | + // Section count not found, will start from 0 |
| 66 | + } |
31 | 67 |
|
32 | | - const recent = (await db.getItem(`recent_${sectionHandle}`, true)) || [] |
33 | | - localStorage.setItem(`recent_${sectionHandle}`, recent) |
| 68 | + try { |
| 69 | + const lastPage = await db.getItem(`${sectionHandle}_last_page`); |
| 70 | + localStorage.setItem(`${sectionHandle}_last_page`, lastPage); |
| 71 | + } catch (error) { |
| 72 | + // Last page not found, will start fresh |
| 73 | + } |
34 | 74 |
|
35 | | - console.log('Restore localStorage') |
| 75 | + try { |
| 76 | + const recent = (await db.getItem(`recent_${sectionHandle}`, true)) || []; |
| 77 | + // Ensure recent is always an array |
| 78 | + const recentArray = Array.isArray(recent) ? recent : []; |
| 79 | + localStorage.setItem(`recent_${sectionHandle}`, recentArray); |
| 80 | + } catch (error) { |
| 81 | + // Recent data not found, will start with empty array |
| 82 | + localStorage.setItem(`recent_${sectionHandle}`, []); |
| 83 | + } |
| 84 | + } catch (err) { |
| 85 | + console.error("Error initializing cache", err); |
| 86 | + throw err; |
| 87 | + } |
36 | 88 | } |
37 | 89 |
|
38 | | -const app = async (event, context) => { |
39 | | - let { onenoteSettings, messageSettings } = event |
| 90 | +const app = async (event) => { |
| 91 | + let { onenoteSettings, messageSettings } = event; |
40 | 92 |
|
41 | 93 | onenoteSettings = { |
42 | 94 | sectionHandle: snakeCase(onenoteSettings.sectionName), |
43 | 95 | isSequential: false, |
44 | | - ...onenoteSettings |
45 | | - } |
| 96 | + ...onenoteSettings, |
| 97 | + }; |
46 | 98 |
|
47 | 99 | const resp = await initCache(onenoteSettings.sectionHandle) |
48 | | - .then(() => refreshToken()) |
49 | | - .then(tokenResponse => { |
50 | | - if (!tokenResponse || !hasValidToken()) { |
51 | | - console.log('Token still invalid after refresh, initiating device login'); |
52 | | - return deviceLogin(); |
53 | | - } |
54 | | - return tokenResponse; |
55 | | - }) |
56 | | - .then(() => getNote(onenoteSettings)) |
57 | | - .then(note => { |
58 | | - if (typeof note === 'undefined') { |
59 | | - throw new Error('Note is undefined'); |
60 | | - } |
61 | | - return notify.withTelegram(note, messageSettings); |
62 | | - }) |
63 | | - .catch(err => { |
64 | | - console.log( |
65 | | - 'Ooops!', |
66 | | - `Can't seem to find any notes here. Please check if you created a section called '${onenoteSettings.sectionName}', add some notes.` |
67 | | - ); |
68 | | - console.error('App: Check Logs', err); |
69 | | - return { |
70 | | - status: 400, |
71 | | - title: 'Error', |
72 | | - body: err |
73 | | - }; |
74 | | - }); |
| 100 | + .then(() => refreshToken()) |
| 101 | + .then((tokenResponse) => { |
| 102 | + if (!tokenResponse || !hasValidToken()) { |
| 103 | + throw new Error("Token refresh failed - device login required"); |
| 104 | + } |
| 105 | + return tokenResponse; |
| 106 | + }) |
| 107 | + .then(() => getNote(onenoteSettings)) |
| 108 | + .then((note) => { |
| 109 | + if (typeof note === "undefined") { |
| 110 | + throw new Error("Note is undefined"); |
| 111 | + } |
| 112 | + return notify.withTelegram(note, messageSettings); |
| 113 | + }) |
| 114 | + .catch(async (err) => { |
| 115 | + console.error("App: Check Logs", err); |
| 116 | + const errorMessage = err.errorMessage || err.message || String(err); |
75 | 117 |
|
| 118 | + if (err.message === "Token refresh failed - device login required") { |
| 119 | + try { |
| 120 | + await deviceLogin(); |
| 121 | + } catch (loginErr) { |
| 122 | + const loginErrorMsg = |
| 123 | + loginErr.errorMessage || loginErr.message || String(loginErr); |
| 124 | + await notify.sendNoteToTelegram( |
| 125 | + `Device login failed: ${loginErrorMsg}`, |
| 126 | + process.env.ADMIN_TELEGRAM_CHANNEL, |
| 127 | + null, |
| 128 | + true |
| 129 | + ); |
| 130 | + } |
| 131 | + } else { |
| 132 | + await notify.sendNoteToTelegram( |
| 133 | + errorMessage, |
| 134 | + process.env.ADMIN_TELEGRAM_CHANNEL, |
| 135 | + null, |
| 136 | + true |
| 137 | + ); |
| 138 | + } |
76 | 139 |
|
| 140 | + return { |
| 141 | + status: 400, |
| 142 | + title: "Error", |
| 143 | + body: errorMessage, |
| 144 | + }; |
| 145 | + }); |
77 | 146 |
|
78 | 147 | return { |
79 | 148 | status: resp.status, |
80 | 149 | title: resp.title, |
81 | | - body: resp.body |
82 | | - } |
83 | | -} |
| 150 | + body: resp.body, |
| 151 | + }; |
| 152 | +}; |
84 | 153 |
|
85 | 154 | module.exports = { |
86 | | - app |
87 | | -} |
| 155 | + app, |
| 156 | +}; |
0 commit comments