Files
bus-reminder/src/pages/api/cron.ts

104 lines
2.6 KiB
TypeScript

import { getMatchingTime } from '@/timing';
import type { NextApiRequest, NextApiResponse } from 'next';
import { getDistance } from '@/location';
import { env } from '@/env/server.mjs';
import monitorAsync from '@/monitor';
import { sendNotification } from '@/notify';
import {
checkIdentifier,
fetchConfiguration,
getKey,
markIdentifier
} from '@/db';
import { localNow } from '@/utils/timezone';
import logger from '@/logger';
import { parseBoolean, unauthorized } from '@/utils/helpers';
type ResponseData = {
diff: number;
inRange: boolean;
};
type StatusData = {
status: ResponseStatus;
identifier?: { name: string; key?: string };
};
type ResponseStatus =
| 'unauthorized'
| 'out-of-range'
| 'no-matching-time'
| 'already-notified'
| 'notified'
| 'error';
const center = {
latitude: env.CENTER_LATITUDE,
longitude: env.CENTER_LONGITUDE
};
export default async function handler(
req: NextApiRequest,
res: NextApiResponse<(ResponseData & StatusData) | StatusData>
) {
if (unauthorized(req, res)) return;
async function innerFunction(): Promise<{
status: ResponseStatus;
identifier?: { name: string; key?: string };
}> {
const now = localNow();
const config = await fetchConfiguration();
const matching = await getMatchingTime(config, now);
// No matching time - no notification to send.
if (matching == null) return { status: 'no-matching-time' };
// Get the key for this notification (name + time)
const key = getKey(matching.name, now);
// Check if I am in range of the center
const distanceToCenter = await getDistance();
// TODO: Properly draw from environment MAX_DISTANCE
if (distanceToCenter > 280)
return {
status: 'out-of-range',
identifier: { name: matching.name, key }
};
// Check if I have already been notified
const marked = await checkIdentifier(key);
if (marked)
return {
status: 'already-notified',
identifier: {
name: matching.name,
key
}
};
// Send notification, mark
await sendNotification(`${matching.message} (${matching.name})`);
await markIdentifier(matching.name, true, 60 * 60 * 24 * 31, now);
return { status: 'notified', identifier: { name: matching.name, key } };
}
try {
let result;
if (process.env.NODE_ENV === 'production' && parseBoolean(req.query.report))
result = await monitorAsync(innerFunction);
else result = await innerFunction();
logger.info('Cron evaluated.', { result });
res
.status(200)
.json({ status: result.status, identifier: result.identifier });
} catch (e) {
logger.error(e);
res.status(500).json({ status: 'error' });
}
}