Refactor documentation and add new features

- Updated index.md to simplify structure and focus on task-oriented guides.
- Revised notifications.md to streamline setup instructions and improve clarity.
- Added report-error.js for structured error reporting to Discord.
- Introduced account-creation.md and configuration.md for detailed guidance on account setup and bot configuration.
- Created dashboard.md, docker.md, error-reporting.md, modes.md, running.md, scheduling.md, setup.md, troubleshooting.md, and update.md for comprehensive documentation on respective features and usage.
- Updated package-lock.json and package.json to include peer dependencies.
This commit is contained in:
2025-12-06 13:13:20 +01:00
parent a149c54013
commit 64352fa306
22 changed files with 377 additions and 3942 deletions

119
api/report-error.js Normal file
View File

@@ -0,0 +1,119 @@
const MAX_BODY_SIZE = 10000
const MAX_TEXT = 900
const MAX_FIELD = 120
function isPlainObject(value) {
return Boolean(value) && typeof value === 'object' && !Array.isArray(value)
}
function trimAndLimit(value, limit) {
if (typeof value !== 'string') {
return ''
}
const trimmed = value.trim()
return trimmed.length > limit ? `${trimmed.slice(0, limit)}` : trimmed
}
function formatMetadata(metadata) {
if (!isPlainObject(metadata)) {
return 'Not provided'
}
const entries = Object.entries(metadata).filter(([key, val]) => typeof key === 'string' && (typeof val === 'string' || typeof val === 'number' || typeof val === 'boolean'))
if (entries.length === 0) {
return 'Not provided'
}
const limited = entries.slice(0, 6)
const lines = limited.map(([key, val]) => {
const valueText = trimAndLimit(String(val), MAX_FIELD)
return `${trimAndLimit(key, MAX_FIELD)}: ${valueText}`
})
return lines.join('\n')
}
async function readJsonBody(req) {
if (req.body) {
return req.body
}
let data = ''
for await (const chunk of req) {
data += chunk
if (data.length > MAX_BODY_SIZE) {
throw new Error('Payload too large')
}
}
if (!data) {
return {}
}
return JSON.parse(data)
}
module.exports = async function handler(req, res) {
res.setHeader('Content-Type', 'application/json')
if (req.method !== 'POST') {
res.setHeader('Allow', 'POST')
res.status(405).json({ error: 'Method not allowed' })
return
}
const webhookUrl = process.env.DISCORD_WEBHOOK_URL
if (!webhookUrl) {
res.status(500).json({ error: 'Webhook not configured' })
return
}
let body
try {
body = await readJsonBody(req)
} catch (error) {
res.status(400).json({ error: 'Invalid JSON body' })
return
}
const errorText = trimAndLimit(body.error, MAX_TEXT)
if (!errorText) {
res.status(400).json({ error: 'Field \'error\' is required' })
return
}
const summary = trimAndLimit(body.summary || body.message || '', 140)
const errorType = trimAndLimit(body.type || 'unspecified', 80)
const environment = trimAndLimit((body.environment && (body.environment.name || body.environment)) || process.env.VERCEL_ENV || process.env.NODE_ENV || 'unspecified', 80)
const metadata = formatMetadata(body.metadata)
const embed = {
title: 'Error Report',
description: summary || 'Automated error report received',
color: 0xef4444,
fields: [
{ name: 'Error', value: errorText, inline: false },
{ name: 'Type', value: errorType, inline: true },
{ name: 'Environment', value: environment, inline: true }
],
footer: { text: 'Microsoft Rewards Bot' },
timestamp: new Date().toISOString()
}
if (metadata && metadata !== 'Not provided') {
embed.fields.push({ name: 'Metadata', value: metadata, inline: false })
}
const payload = { embeds: [embed] }
try {
const response = await fetch(webhookUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
})
if (!response.ok) {
res.status(502).json({ error: 'Failed to deliver report' })
return
}
res.status(200).json({ status: 'reported' })
} catch (error) {
res.status(502).json({ error: 'Failed to deliver report' })
}
}