// MIT License
//
// Copyright (c) 2024 HowMuchRadiation.com
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// Additional Conditions:
// 1. Attribution: Users are encouraged to credit the original authors when using or redistributing this software.
// 2. Modification Notices: Any modifications made to the original software should be documented.
// 3. Documentation: Clear usage instructions should be provided with any distribution.
// 4. Limitations of Liability: The authors are not liable for any damages resulting from the use of this software.
// 5. Compliance with Laws: Users must ensure compliance with applicable laws when using this software.
// 6. Contribution Guidelines: Contributions are welcome; please follow the provided guidelines.
// 7. Contact Information: For questions or support, please contact HowMuchRadiation.com
//
// THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
// Note: This widget is not affiliated with GMCmap and operates independently.
export default {
// Rate limiting variables
rlWindow: 60 * 1000, // 1 minute in milliseconds
maxReq: 30,
reqCounts: {},
async fetch(req) {
const clientIP = req.headers.get("cf-connecting-ip") || "unknown";
const now = Date.now();
// Initialize or reset rate limit tracking for the client
if (!this.reqCounts[clientIP] || now - this.reqCounts[clientIP].start > this.rlWindow) {
this.reqCounts[clientIP] = { count: 1, start: now };
} else {
this.reqCounts[clientIP].count++;
}
// Check if the client exceeds the rate limit
if (this.reqCounts[clientIP].count > this.maxReq) {
return new Response("Too Many Requests: Please try again later.", {
status: 429,
headers: { "Retry-After": "60" }, // Suggests a retry after 60 seconds
});
}
const url = new URL(req.url);
const searchParams = new URLSearchParams();
// Normalize query parameters to lowercase
for (const [key, value] of url.searchParams) {
searchParams.append(key.toLowerCase(), value);
}
// Check if "param_id" is missing
if (!searchParams.has("param_id")) {
return this.helpScreen();
}
const paramId = searchParams.get("param_id");
const timezone = parseFloat(searchParams.get("timezone")) || 0;
const bgColor = searchParams.get("bg_color") || "#f5deb3";
const footer = searchParams.get("footer") || "HowMuchRadiation.com";
const showBorder = searchParams.get("show_border") !== "false";
// Customizable color variables with defaults
const colors = {
radiationSymbol: searchParams.get("radiation_symbol_color") || "black",
headline: searchParams.get("headline_color") || "black",
reading: searchParams.get("reading_color") || "red",
dateTime: searchParams.get("date_time_color") || "black",
};
try {
const jsonData = await this.getGMCData(paramId);
return this.radiationScreen(
jsonData,
timezone,
bgColor,
showBorder,
footer,
colors
);
} catch (error) {
return this.errorScreen(bgColor, showBorder, footer);
}
},
// Fetch data from GMCmap API
async getGMCData(paramId) {
const jsonUrl = `https://www.gmcmap.com/historyData-plain.asp?Param_ID=${paramId}&n=1`;
const response = await fetch(jsonUrl);
if (!response.ok) {
throw new Error(`Failed to fetch JSON data: ${response.status} ${response.statusText}`);
}
const jsonData = await response.json();
if (!jsonData || jsonData.length === 0) {
throw new Error("No data available from GMCmap.");
}
return jsonData[0]; // Only need the most recent record
},
// Generate Help Screen
helpScreen() {
const helpText = `
`;
return new Response(helpText, {
headers: { "Content-Type": "image/svg+xml", "Cache-Control": "no-store" },
});
},
// Generate Error Screen
errorScreen(bgColor, showBorder, footer) {
const errorSvg = `
`;
return new Response(errorSvg, {
headers: { "Content-Type": "image/svg+xml", "Cache-Control": "no-store" },
});
},
// Generate Radiation Screen
radiationScreen(data, timezone, bgColor, showBorder, footer, colors) {
const { uSv, time, CPM, ACPM } = data;
const formattedTime = this.formatTimestamp(time, timezone);
const svgContent = `
`;
return new Response(svgContent, {
headers: { "Content-Type": "image/svg+xml", "Cache-Control": "no-store" },
});
},
formatTimestamp(time, timezone) {
const utcDate = new Date();
const localDate = new Date(utcDate.getTime() + timezone * 60 * 60 * 1000);
return localDate.toLocaleString();
},
};