mirror of
https://github.com/DocNR/POWR.git
synced 2025-04-22 16:51:33 +00:00
84 lines
2.7 KiB
TypeScript
84 lines
2.7 KiB
TypeScript
// utils/validation.ts
|
|
import { NostrEvent, NostrEventKind, NostrTag, getTagValue, getTagValues } from '@/types/events';
|
|
|
|
function validateBasicEventStructure(event: NostrEvent): boolean {
|
|
return !!(event.id &&
|
|
event.pubkey &&
|
|
event.kind &&
|
|
event.created_at &&
|
|
Array.isArray(event.tags));
|
|
}
|
|
|
|
function validateExerciseTag(tag: NostrTag): boolean {
|
|
// Exercise tag format: ['exercise', id, type, category, setsData]
|
|
if (tag.length < 5) return false;
|
|
|
|
try {
|
|
const setsData = JSON.parse(tag[4]);
|
|
return Array.isArray(setsData) && setsData.every(set =>
|
|
typeof set === 'object' &&
|
|
('weight' in set || 'reps' in set || 'type' in set)
|
|
);
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
export function validateNostrExerciseEvent(event: NostrEvent): boolean {
|
|
if (!validateBasicEventStructure(event)) return false;
|
|
if (event.kind !== NostrEventKind.EXERCISE_TEMPLATE) return false;
|
|
|
|
const requiredTags = ['d', 'name', 'type', 'category'];
|
|
const tagMap = new Set(event.tags.map(tag => tag[0]));
|
|
|
|
if (!requiredTags.every(tag => tagMap.has(tag))) return false;
|
|
|
|
const format = getTagValue(event.tags, 'format');
|
|
if (format) {
|
|
try {
|
|
const formatObj = JSON.parse(format);
|
|
if (typeof formatObj !== 'object') return false;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
export function validateNostrTemplateEvent(event: NostrEvent): boolean {
|
|
if (!validateBasicEventStructure(event)) return false;
|
|
if (event.kind !== NostrEventKind.WORKOUT_TEMPLATE) return false;
|
|
|
|
const requiredTags = ['d', 'title', 'type', 'category'];
|
|
const tagMap = new Set(event.tags.map(tag => tag[0]));
|
|
|
|
if (!requiredTags.every(tag => tagMap.has(tag))) return false;
|
|
|
|
// Validate exercise tags
|
|
const exerciseTags = event.tags.filter(tag => tag[0] === 'exercise');
|
|
if (exerciseTags.length === 0) return false;
|
|
|
|
return exerciseTags.every(validateExerciseTag);
|
|
}
|
|
|
|
export function validateNostrWorkoutEvent(event: NostrEvent): boolean {
|
|
if (!validateBasicEventStructure(event)) return false;
|
|
if (event.kind !== NostrEventKind.WORKOUT_RECORD) return false;
|
|
|
|
const requiredTags = ['d', 'title', 'type', 'start', 'end', 'completed'];
|
|
const tagMap = new Set(event.tags.map(tag => tag[0]));
|
|
|
|
if (!requiredTags.every(tag => tagMap.has(tag))) return false;
|
|
|
|
// Validate timestamps
|
|
const start = parseInt(getTagValue(event.tags, 'start') || '');
|
|
const end = parseInt(getTagValue(event.tags, 'end') || '');
|
|
if (isNaN(start) || isNaN(end) || start > end) return false;
|
|
|
|
// Validate exercise tags
|
|
const exerciseTags = event.tags.filter(tag => tag[0] === 'exercise');
|
|
if (exerciseTags.length === 0) return false;
|
|
|
|
return exerciseTags.every(validateExerciseTag);
|
|
} |