mirror of
https://github.com/jcreek/LivingDexTracker.git
synced 2026-07-13 02:53:45 +00:00
feat(#64): Improve the about page
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
-- Create stats_cache table to store cached statistics
|
||||
-- This table will be updated once per day to improve performance
|
||||
|
||||
CREATE TABLE stats_cache (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
pokemon_caught BIGINT NOT NULL DEFAULT 0,
|
||||
total_users BIGINT NOT NULL DEFAULT 0,
|
||||
completed_pokedexes BIGINT NOT NULL DEFAULT 0,
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Create index for faster lookups
|
||||
CREATE INDEX idx_stats_cache_updated_at ON stats_cache(updated_at DESC);
|
||||
|
||||
-- Enable RLS
|
||||
ALTER TABLE stats_cache ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
-- RLS policies: Only allow reads (stats are public)
|
||||
CREATE POLICY "Anyone can view stats cache" ON stats_cache
|
||||
FOR SELECT USING (true);
|
||||
|
||||
-- No insert/update/delete policies - only database functions can modify this table
|
||||
@@ -0,0 +1,51 @@
|
||||
-- Create function to calculate and update stats in the cache table
|
||||
-- This function runs the expensive queries and stores the results
|
||||
|
||||
CREATE OR REPLACE FUNCTION update_and_get_stats()
|
||||
RETURNS TABLE (
|
||||
pokemon_caught BIGINT,
|
||||
total_users BIGINT,
|
||||
completed_pokedexes BIGINT,
|
||||
updated_at TIMESTAMPTZ
|
||||
)
|
||||
SECURITY DEFINER
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
DECLARE
|
||||
v_pokemon_caught BIGINT;
|
||||
v_total_users BIGINT;
|
||||
v_completed_pokedexes BIGINT;
|
||||
BEGIN
|
||||
-- Calculate current stats: count of caught Pokémon
|
||||
SELECT COUNT(*) INTO v_pokemon_caught
|
||||
FROM catch_records
|
||||
WHERE caught = true;
|
||||
|
||||
-- Calculate current stats: count of total users
|
||||
SELECT COUNT(*) INTO v_total_users
|
||||
FROM auth.users;
|
||||
|
||||
-- Calculate current stats: count of completed pokedexes
|
||||
-- A pokedex is completed when all its entries have caught = true
|
||||
SELECT COUNT(*) INTO v_completed_pokedexes
|
||||
FROM (
|
||||
SELECT pokedexId
|
||||
FROM catch_records
|
||||
GROUP BY pokedexId
|
||||
HAVING COUNT(*) = COUNT(*) FILTER (WHERE caught = true)
|
||||
) completed;
|
||||
|
||||
-- Update cache using UPSERT pattern
|
||||
INSERT INTO stats_cache (pokemon_caught, total_users, completed_pokedexes, updated_at)
|
||||
VALUES (v_pokemon_caught, v_total_users, v_completed_pokedexes, NOW())
|
||||
ON CONFLICT (id) DO UPDATE
|
||||
SET pokemon_caught = EXCLUDED.pokemon_caught,
|
||||
total_users = EXCLUDED.total_users,
|
||||
completed_pokedexes = EXCLUDED.completed_pokedexes,
|
||||
updated_at = NOW();
|
||||
|
||||
-- Return the stats
|
||||
RETURN QUERY
|
||||
SELECT v_pokemon_caught, v_total_users, v_completed_pokedexes, NOW() AS updated_at;
|
||||
END;
|
||||
$$;
|
||||
@@ -0,0 +1,36 @@
|
||||
-- Create function to get public stats with caching
|
||||
-- Returns cached stats if fresh (within 24 hours), otherwise refreshes cache
|
||||
|
||||
CREATE OR REPLACE FUNCTION get_public_stats()
|
||||
RETURNS TABLE (
|
||||
pokemon_caught BIGINT,
|
||||
total_users BIGINT,
|
||||
completed_pokedexes BIGINT,
|
||||
updated_at TIMESTAMPTZ
|
||||
)
|
||||
SECURITY DEFINER
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
DECLARE
|
||||
v_cache_exists BOOLEAN;
|
||||
BEGIN
|
||||
-- Check if cache exists and is fresh (within 24 hours)
|
||||
SELECT EXISTS(
|
||||
SELECT 1 FROM stats_cache
|
||||
WHERE stats_cache.updated_at > NOW() - INTERVAL '24 hours'
|
||||
) INTO v_cache_exists;
|
||||
|
||||
IF v_cache_exists THEN
|
||||
-- Return cached stats
|
||||
RETURN QUERY
|
||||
SELECT sc.pokemon_caught, sc.total_users, sc.completed_pokedexes, sc.updated_at
|
||||
FROM stats_cache sc
|
||||
ORDER BY sc.updated_at DESC
|
||||
LIMIT 1;
|
||||
ELSE
|
||||
-- Cache is stale or doesn't exist, update it
|
||||
RETURN QUERY
|
||||
SELECT * FROM update_and_get_stats();
|
||||
END IF;
|
||||
END;
|
||||
$$;
|
||||
@@ -0,0 +1,89 @@
|
||||
-- Fix ambiguous column reference errors in stats functions.
|
||||
--
|
||||
-- If earlier migrations have already been applied, editing them will not update
|
||||
-- the deployed database. This migration re-defines the functions using
|
||||
-- CREATE OR REPLACE so the corrected definitions take effect.
|
||||
|
||||
CREATE OR REPLACE FUNCTION update_and_get_stats()
|
||||
RETURNS TABLE (
|
||||
pokemon_caught BIGINT,
|
||||
total_users BIGINT,
|
||||
completed_pokedexes BIGINT,
|
||||
updated_at TIMESTAMPTZ
|
||||
)
|
||||
SECURITY DEFINER
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
DECLARE
|
||||
v_pokemon_caught BIGINT;
|
||||
v_total_users BIGINT;
|
||||
v_completed_pokedexes BIGINT;
|
||||
BEGIN
|
||||
-- Calculate current stats: count of caught Pokémon
|
||||
SELECT COUNT(*) INTO v_pokemon_caught
|
||||
FROM catch_records
|
||||
WHERE caught = true;
|
||||
|
||||
-- Calculate current stats: count of total users
|
||||
SELECT COUNT(*) INTO v_total_users
|
||||
FROM auth.users;
|
||||
|
||||
-- Calculate current stats: count of completed pokedexes
|
||||
-- A pokedex is completed when all its entries have caught = true
|
||||
SELECT COUNT(*) INTO v_completed_pokedexes
|
||||
FROM (
|
||||
SELECT "pokedexId"
|
||||
FROM catch_records
|
||||
GROUP BY "pokedexId"
|
||||
HAVING COUNT(*) = COUNT(*) FILTER (WHERE caught = true)
|
||||
) completed;
|
||||
|
||||
-- Update cache using UPSERT pattern
|
||||
INSERT INTO stats_cache (pokemon_caught, total_users, completed_pokedexes, updated_at)
|
||||
VALUES (v_pokemon_caught, v_total_users, v_completed_pokedexes, NOW())
|
||||
ON CONFLICT (id) DO UPDATE
|
||||
SET pokemon_caught = EXCLUDED.pokemon_caught,
|
||||
total_users = EXCLUDED.total_users,
|
||||
completed_pokedexes = EXCLUDED.completed_pokedexes,
|
||||
updated_at = NOW();
|
||||
|
||||
-- Return stats
|
||||
RETURN QUERY
|
||||
SELECT v_pokemon_caught, v_total_users, v_completed_pokedexes, NOW() AS updated_at;
|
||||
END;
|
||||
$$;
|
||||
|
||||
|
||||
CREATE OR REPLACE FUNCTION get_public_stats()
|
||||
RETURNS TABLE (
|
||||
pokemon_caught BIGINT,
|
||||
total_users BIGINT,
|
||||
completed_pokedexes BIGINT,
|
||||
updated_at TIMESTAMPTZ
|
||||
)
|
||||
SECURITY DEFINER
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
DECLARE
|
||||
v_cache_exists BOOLEAN;
|
||||
BEGIN
|
||||
-- Check if cache exists and is fresh (within 24 hours)
|
||||
SELECT EXISTS(
|
||||
SELECT 1 FROM stats_cache
|
||||
WHERE stats_cache.updated_at > NOW() - INTERVAL '24 hours'
|
||||
) INTO v_cache_exists;
|
||||
|
||||
IF v_cache_exists THEN
|
||||
-- Return cached stats (qualify updated_at to avoid ambiguity with output parameter)
|
||||
RETURN QUERY
|
||||
SELECT sc.pokemon_caught, sc.total_users, sc.completed_pokedexes, sc.updated_at
|
||||
FROM stats_cache sc
|
||||
ORDER BY sc.updated_at DESC
|
||||
LIMIT 1;
|
||||
ELSE
|
||||
-- Cache is stale or doesn't exist, update it
|
||||
RETURN QUERY
|
||||
SELECT * FROM update_and_get_stats();
|
||||
END IF;
|
||||
END;
|
||||
$$;
|
||||
Reference in New Issue
Block a user