176 lines
5.4 KiB
TypeScript
176 lines
5.4 KiB
TypeScript
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2.39.3';
|
|
|
|
const corsHeaders = {
|
|
'Access-Control-Allow-Origin': '*',
|
|
'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',
|
|
};
|
|
|
|
Deno.serve(async (req) => {
|
|
if (req.method === 'OPTIONS') {
|
|
return new Response(null, { headers: corsHeaders });
|
|
}
|
|
|
|
try {
|
|
const supabaseUrl = Deno.env.get('SUPABASE_URL')!;
|
|
const supabaseKey = Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!;
|
|
const supabase = createClient(supabaseUrl, supabaseKey);
|
|
|
|
console.log('Starting price check...');
|
|
|
|
// Get all enabled alert profiles
|
|
const { data: alertProfiles, error: profilesError } = await supabase
|
|
.from('alert_profiles')
|
|
.select('*')
|
|
.eq('enabled', true);
|
|
|
|
if (profilesError) {
|
|
throw profilesError;
|
|
}
|
|
|
|
console.log(`Found ${alertProfiles?.length || 0} active alert profiles`);
|
|
|
|
// Get all products
|
|
const { data: products, error: productsError } = await supabase
|
|
.from('products')
|
|
.select('*');
|
|
|
|
if (productsError) {
|
|
throw productsError;
|
|
}
|
|
|
|
console.log(`Found ${products?.length || 0} products`);
|
|
|
|
const alerts = [];
|
|
|
|
// Check each alert profile against products
|
|
for (const profile of alertProfiles || []) {
|
|
for (const product of products || []) {
|
|
// Match category
|
|
if (product.category !== profile.category) continue;
|
|
|
|
// Check if product meets specification requirements
|
|
const specsFilter = profile.specs_filter;
|
|
const productSpecs = product.specs;
|
|
|
|
let meetsRequirements = true;
|
|
|
|
// Check minimum size (for harddrives/ram)
|
|
if (specsFilter.min_size) {
|
|
const minSizeValue = parseFloat(specsFilter.min_size);
|
|
const productSizeValue = parseFloat(productSpecs.size || '0');
|
|
if (productSizeValue < minSizeValue) {
|
|
meetsRequirements = false;
|
|
}
|
|
}
|
|
|
|
// Check type (SSD/HDD/etc)
|
|
if (specsFilter.type && productSpecs.type !== specsFilter.type) {
|
|
meetsRequirements = false;
|
|
}
|
|
|
|
// Check if price is below target
|
|
if (!meetsRequirements || product.current_price > profile.target_price) {
|
|
continue;
|
|
}
|
|
|
|
// Found a match! Log the alert
|
|
const message = `🎯 Alert: ${profile.name}\n` +
|
|
`Product: ${product.name}\n` +
|
|
`Price: ${product.current_price} ${product.currency}\n` +
|
|
`Target: ${profile.target_price} ${product.currency}\n` +
|
|
`Store: ${product.store}\n` +
|
|
`URL: ${product.url}`;
|
|
|
|
const { error: logError } = await supabase
|
|
.from('alerts_log')
|
|
.insert({
|
|
alert_profile_id: profile.id,
|
|
product_id: product.id,
|
|
triggered_price: product.current_price,
|
|
message,
|
|
sent_to_discord: false
|
|
});
|
|
|
|
if (logError) {
|
|
console.error('Error logging alert:', logError);
|
|
}
|
|
|
|
alerts.push({
|
|
profile,
|
|
product,
|
|
message
|
|
});
|
|
|
|
// Send Discord notification if webhook URL is set
|
|
if (profile.discord_webhook_url) {
|
|
try {
|
|
await fetch(profile.discord_webhook_url, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
content: message,
|
|
embeds: [{
|
|
title: '💰 Price Alert!',
|
|
description: message,
|
|
color: 0x00ff00,
|
|
fields: [
|
|
{
|
|
name: 'Current Price',
|
|
value: `${product.current_price} ${product.currency}`,
|
|
inline: true
|
|
},
|
|
{
|
|
name: 'Target Price',
|
|
value: `${profile.target_price} ${product.currency}`,
|
|
inline: true
|
|
},
|
|
{
|
|
name: 'Savings',
|
|
value: `${(profile.target_price - product.current_price).toFixed(2)} ${product.currency}`,
|
|
inline: true
|
|
}
|
|
],
|
|
timestamp: new Date().toISOString()
|
|
}]
|
|
})
|
|
});
|
|
|
|
// Update log to mark as sent
|
|
await supabase
|
|
.from('alerts_log')
|
|
.update({ sent_to_discord: true })
|
|
.eq('alert_profile_id', profile.id)
|
|
.eq('product_id', product.id);
|
|
|
|
console.log(`Sent Discord notification for ${product.name}`);
|
|
} catch (discordError) {
|
|
console.error('Error sending Discord notification:', discordError);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
console.log(`Price check complete. Found ${alerts.length} matching alerts`);
|
|
|
|
return new Response(
|
|
JSON.stringify({
|
|
success: true,
|
|
alertsTriggered: alerts.length,
|
|
alerts
|
|
}),
|
|
{ headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
|
|
);
|
|
|
|
} catch (error) {
|
|
console.error('Error in check-prices function:', error);
|
|
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
|
|
return new Response(
|
|
JSON.stringify({ error: errorMessage }),
|
|
{
|
|
status: 500,
|
|
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
|
|
}
|
|
);
|
|
}
|
|
});
|