13 });
14
15 // Fetch weather data for Madrid using Open-Meteo API
16 const weatherResponse = await fetch(
17 'https://api.open-meteo.com/v1/forecast?latitude=40.4168&longitude=-3.7038¤t=temperature_2m,weather_code,wind_speed_10m&timezone=Europe%2FMadrid'
18 );
19
20 if (!weatherResponse.ok) {
21 throw new Error('Failed to fetch weather data');
22 }
23
191<body style="font-family: sans-serif; text-align: center; padding: 50px; background: #f0f0f0;">
192 <h1>🌍 Weather Temporarily Unavailable</h1>
193 <p>Unable to fetch current weather data for Madrid. Please try again later.</p>
194 <p><em>Error: ${error instanceof Error ? error.message : 'Unknown error'}</em></p>
195</body>
22 // Use custom hooks for all stateful logic
23 const { userEmail, isAuthorized } = useAuth();
24 const { data, loading, error, refetch } = useGlimpseData(
25 glimpseId,
26 initialData,
107 <div className="flex gap-3">
108 <button
109 onClick={refetch}
110 className="bg-red-600 text-white px-4 py-2 rounded hover:bg-red-700 transition-colors"
111 >
71
72 // ---- Exchange JWT for access token ----
73 const tokenRes = await fetch("https://oauth2.googleapis.com/token", {
74 method: "POST",
75 headers: { "Content-Type": "application/x-www-form-urlencoded" },
89
90 // ---- Query Firestore publicLenses ----
91 const pubDocRes = await fetch(
92 `https://firestore.googleapis.com/v1/projects/${projectId}/databases/(default)/documents/publicLenses/${id}`,
93 { headers: { Authorization: `Bearer ${access_token}` } },
105
106 // ---- Query Firestore private user doc ----
107 const privDocRes = await fetch(
108 `https://firestore.googleapis.com/v1/projects/${projectId}/databases/(default)/documents/users/${owner}/lenses/${id}`,
109 { headers: { Authorization: `Bearer ${access_token}` } },
441 "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
442 },
443 "node-fetch": {
444 "version": "2.6.1",
445 "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
446 "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw=="
447 },
623 "requires": {
624 "lodash": "4.17.13",
625 "node-fetch": "^2.6.0",
626 "request": "2.88.0"
627 }
21 "knockout": "^3.5.1",
22 "morgan": "~1.10.0",
23 "node-fetch": "^3.2.10",
24 "rand-token": "^1.0.1",
25 "randomstring": "^1.2.1",
489 }
490 },
491 "node_modules/fetch-blob": {
492 "version": "3.2.0",
493 "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
494 "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
495 "funding": [
568 "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
569 "dependencies": {
570 "fetch-blob": "^3.1.2"
571 },
572 "engines": {
924 }
925 },
926 "node_modules/node-fetch": {
927 "version": "3.3.2",
928 "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz",
929 "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==",
930 "dependencies": {
931 "data-uri-to-buffer": "^4.0.0",
932 "fetch-blob": "^3.1.4",
933 "formdata-polyfill": "^4.0.10"
934 },
938 "funding": {
939 "type": "opencollective",
940 "url": "https://opencollective.com/node-fetch"
941 }
942 },
1158 "dependencies": {
1159 "camelcase": "6.2.0",
1160 "node-fetch": "^2.6.7"
1161 }
1162 },
1163 "node_modules/st-schema/node_modules/node-fetch": {
1164 "version": "2.6.12",
1165 "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz",
1166 "integrity": "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==",
1167 "dependencies": {
21 "knockout": "^3.5.1",
22 "morgan": "~1.10.0",
23 "node-fetch": "^3.2.10",
24 "rand-token": "^1.0.1",
25 "randomstring": "^1.2.1",
489 }
490 },
491 "node_modules/fetch-blob": {
492 "version": "3.2.0",
493 "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
494 "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
495 "funding": [
568 "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
569 "dependencies": {
570 "fetch-blob": "^3.1.2"
571 },
572 "engines": {
924 }
925 },
926 "node_modules/node-fetch": {
927 "version": "3.3.2",
928 "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz",
929 "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==",
930 "dependencies": {
931 "data-uri-to-buffer": "^4.0.0",
932 "fetch-blob": "^3.1.4",
933 "formdata-polyfill": "^4.0.10"
934 },
938 "funding": {
939 "type": "opencollective",
940 "url": "https://opencollective.com/node-fetch"
941 }
942 },
1158 "dependencies": {
1159 "camelcase": "6.2.0",
1160 "node-fetch": "^2.6.7"
1161 }
1162 },
1163 "node_modules/st-schema/node_modules/node-fetch": {
1164 "version": "2.6.12",
1165 "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz",
1166 "integrity": "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==",
1167 "dependencies": {
441 "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
442 },
443 "node-fetch": {
444 "version": "2.6.1",
445 "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
446 "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw=="
447 },
623 "requires": {
624 "lodash": "4.17.13",
625 "node-fetch": "^2.6.0",
626 "request": "2.88.0"
627 }
441 "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
442 },
443 "node-fetch": {
444 "version": "2.6.1",
445 "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
446 "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw=="
447 },
623 "requires": {
624 "lodash": "4.17.13",
625 "node-fetch": "^2.6.0",
626 "request": "2.88.0"
627 }
356 try {
357 // Pass password as query parameter for authentication
358 const response = await fetch(`/api/posts/${encodeURIComponent(slug)}?password=${encodeURIComponent(password)}`);
359
360 if (!response.ok) {
518
519 try {
520 const response = await fetch(`/api/posts/${encodeURIComponent(currentPost.slug)}`, {
521 method: 'PUT',
522 headers: {
564
565 try {
566 const response = await fetch(`/api/posts/${encodeURIComponent(currentPost.slug)}?password=${encodeURIComponent(currentPassword)}`, {
567 method: 'DELETE'
568 });
625
626 try {
627 const response = await fetch(`/api/posts/${encodeURIComponent(currentPost.slug)}/images`);
628
629 if (!response.ok) {
711 }
712
713 const response = await fetch('/api/images/upload', {
714 method: 'POST',
715 body: formData
57// Parse RSS feed using Feedsmith
58async function parseRSSFeed(url: string): Promise<RSSFeed> {
59 const response = await fetch(url);
60 if (!response.ok) {
61 throw new Error(`Failed to fetch RSS feed: ${response.status} ${response.statusText}`);
62 }
63
162 };
163
164 const response = await fetch(DISCORD_WEBHOOK_URL, {
165 method: "POST",
166 headers: {