app: lists on meta tables, schema-qualified SQL, typed columns

- lists read/write real lists.* tables via meta.tables/meta.columns and the
  SECURITY DEFINER meta functions; typed extra columns in the item forms
- old numeric list URLs 308-redirect to the slug
- include_recurring is boolean, osu accuracy/pp/rate numeric
- raw SQL qualified with the new schemas
This commit is contained in:
2026-10-23 22:03:42 +00:00
parent 64fb8df262
commit 4cfe6bad6c
25 changed files with 529 additions and 218 deletions

View File

@@ -28,7 +28,7 @@ export const load: PageServerLoad = async (event) => {
sightingAcceptedAt: sightings.acceptedAt,
sightingDeniedAt: sightings.deniedAt,
photoUrl: sql<string>`(
SELECT url FROM photos
SELECT url FROM animaldex.sighting_photos
WHERE sighting_id = ${sightings.id}
ORDER BY sort_order ASC
LIMIT 1
@@ -40,7 +40,7 @@ export const load: PageServerLoad = async (event) => {
and(
eq(sightings.animalId, animals.id),
sql`${sightings.id} = (
SELECT id FROM sightings
SELECT id FROM animaldex.sightings
WHERE animal_id = ${animals.id}
ORDER BY submitted_at ASC
LIMIT 1

View File

@@ -45,7 +45,7 @@ export const GET: RequestHandler = async ({ url }) => {
lat: sightings.lat,
lng: sightings.lng,
photoUrl: sql<string>`(
SELECT url FROM photos
SELECT url FROM animaldex.sighting_photos
WHERE sighting_id = ${sightings.id}
ORDER BY sort_order ASC
LIMIT 1

View File

@@ -5,13 +5,13 @@ import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async () => {
const [stats, dimensions, markets] = await Promise.all([
db.execute(sql`SELECT
(SELECT count(*)::int FROM cs_market_items) AS items,
(SELECT greatest(reltuples::bigint, 0) FROM pg_class WHERE oid = 'cs_price_observations'::regclass) AS observations,
(SELECT min(observed_at) FROM cs_price_observations) AS earliest,
(SELECT max(observed_at) FROM cs_price_observations) AS latest`),
(SELECT count(*)::int FROM cs.items) AS items,
(SELECT greatest(reltuples::bigint, 0) FROM pg_class WHERE oid = 'cs.price_observations'::regclass) AS observations,
(SELECT min(observed_at) FROM cs.price_observations) AS earliest,
(SELECT max(observed_at) FROM cs.price_observations) AS latest`),
db.execute(sql`SELECT key, count(DISTINCT value)::int AS values
FROM cs_item_dimensions GROUP BY key ORDER BY key`),
db.execute(sql`SELECT source, currency, kind FROM cs_price_streams ORDER BY 1, 2, 3`)
FROM cs.item_dimensions GROUP BY key ORDER BY key`),
db.execute(sql`SELECT source, currency, kind FROM cs.price_streams ORDER BY 1, 2, 3`)
]);
return {
stats: stats[0] as {

View File

@@ -10,14 +10,14 @@ export const GET: RequestHandler = async ({ url }) => {
const pattern = `%${q.replaceAll('\\', '\\\\').replaceAll('%', '\\%').replaceAll('_', '\\_')}%`;
if (dimension === 'item') {
const rows = await db.execute(sql`SELECT id, name, market_hash_name, icon_url
FROM cs_market_items WHERE name ILIKE ${pattern} ESCAPE '\'
FROM cs.items WHERE name ILIKE ${pattern} ESCAPE '\'
ORDER BY name, id LIMIT ${limit} OFFSET ${offset}`);
return Response.json({ results: rows });
}
if (!/^[a-z][a-z0-9_]*$/.test(dimension))
return Response.json({ error: 'Invalid dimension' }, { status: 400 });
const rows = await db.execute(sql`SELECT value, count(*)::int AS items
FROM cs_item_dimensions WHERE key = ${dimension} AND value ILIKE ${pattern} ESCAPE '\'
FROM cs.item_dimensions WHERE key = ${dimension} AND value ILIKE ${pattern} ESCAPE '\'
GROUP BY value ORDER BY value LIMIT ${limit} OFFSET ${offset}`);
return Response.json({ results: rows });
};

View File

@@ -34,11 +34,11 @@ export const POST: RequestHandler = async ({ request }) => {
const seconds = Math.max(60, Math.ceil((+to - +from) / 1000 / 240 / 60) * 60);
const selection =
dimension === 'item'
? sql`JOIN cs_market_items i ON i.id = p.item_id AND i.id IN (${sql.join(
? sql`JOIN cs.items i ON i.id = p.item_id AND i.id IN (${sql.join(
ids.map((id) => sql`${id}`),
sql`,`
)})`
: sql`JOIN cs_item_dimensions d ON d.item_id = p.item_id AND d.key = ${dimension}
: sql`JOIN cs.item_dimensions d ON d.item_id = p.item_id AND d.key = ${dimension}
AND d.value IN (${sql.join(
values.map((value) => sql`${value}`),
sql`,`
@@ -53,7 +53,7 @@ export const POST: RequestHandler = async ({ request }) => {
sum(p.volume) AS volume,
min(p.price_cents) AS min_cents,
max(p.price_cents) AS max_cents
FROM cs_price_observations p ${selection}
FROM cs.price_observations p ${selection}
WHERE p.source = ${source} AND p.currency = ${currency} AND p.kind = ${kind}
AND p.observed_at >= ${from.toISOString()}::timestamptz
AND p.observed_at <= ${to.toISOString()}::timestamptz

View File

@@ -82,9 +82,9 @@ export const actions = {
duration,
combo,
misses,
accuracy: t('accuracy') || null,
pp: t('pp') || null,
rate: t('rate'),
accuracy: t('accuracy') ? Number(t('accuracy')) : null,
pp: t('pp') ? Number(t('pp')) : null,
rate: Number(t('rate')),
mods: t('mods') || 'NM',
beatmapUrl: t('beatmapUrl') || null,
scoreUrl: t('scoreUrl') || null,

View File

@@ -91,7 +91,7 @@ export const load: PageServerLoad = async (event) => {
.limit(400),
db.execute(sql`SELECT city_slug AS slug, min(city) AS name,
count(*) FILTER (WHERE removed_at IS NULL)::int AS active
FROM house_listings WHERE offer = ${offer} AND city_slug IS NOT NULL
FROM house.listings WHERE offer = ${offer} AND city_slug IS NOT NULL
GROUP BY city_slug ORDER BY active DESC, slug LIMIT 200`),
db.execute(sql`SELECT coalesce(min(city), city_slug) AS city,
count(*) FILTER (WHERE removed_at IS NULL)::int AS active,
@@ -103,18 +103,18 @@ export const load: PageServerLoad = async (event) => {
percentile_cont(0.5) WITHIN GROUP (
ORDER BY extract(epoch FROM removed_at - coalesce(offered_at, first_seen_at)) / 86400)
FILTER (WHERE removed_at > now() - interval '90 days') AS median_days_listed
FROM house_listings WHERE offer = ${offer} AND city_slug IS NOT NULL
FROM house.listings WHERE offer = ${offer} AND city_slug IS NOT NULL
GROUP BY city_slug ORDER BY active DESC LIMIT 30`),
db.select().from(houseAlertRules).orderBy(asc(houseAlertRules.id)),
db.select().from(houseWorkerRuns).orderBy(desc(houseWorkerRuns.createdAt)).limit(25),
db.execute(sql`SELECT
(SELECT max(created_at) FROM house_worker_runs WHERE status = 'ok') AS last_ok,
(SELECT max(created_at) FROM house_worker_runs WHERE status = 'challenged') AS last_challenge,
(SELECT count(*)::int FROM house_listings) AS total,
(SELECT count(*)::int FROM house_listings WHERE alerted_at > now() - interval '7 days') AS alerts_week,
(SELECT max(created_at) FROM house.worker_runs WHERE status = 'ok') AS last_ok,
(SELECT max(created_at) FROM house.worker_runs WHERE status = 'challenged') AS last_challenge,
(SELECT count(*)::int FROM house.listings) AS total,
(SELECT count(*)::int FROM house.listings WHERE alerted_at > now() - interval '7 days') AS alerts_week,
(SELECT percentile_cont(0.5) WITHIN GROUP (
ORDER BY extract(epoch FROM first_seen_at - offered_at) / 60)
FROM house_listings WHERE first_seen_lane = 'fast' AND offered_at IS NOT NULL
FROM house.listings WHERE first_seen_lane = 'fast' AND offered_at IS NOT NULL
AND first_seen_at >= offered_at AND first_seen_at - offered_at < interval '3 days'
AND first_seen_at > now() - interval '30 days')
AS median_detect_minutes`)

View File

@@ -300,7 +300,7 @@ export const actions: Actions = {
fixedMonthlyCents,
annualReturnPercent,
contributionMode,
includeRecurring: form.get('includeRecurring') === 'on' ? 1 : 0,
includeRecurring: form.get('includeRecurring') === 'on',
endDate
};
await db

View File

@@ -324,7 +324,7 @@
</div>
<p class="muted projection-note">
Includes future dated transactions and estimates{data.projectionSettings
?.includeRecurring !== 0
?.includeRecurring !== false
? ', plus recurring monthly equivalents from next month'
: ''}. {data.projectionSettings?.investmentAccountId
? `The selected investment account and new contributions use a hypothetical ${data.projectionSettings.annualReturnPercent}% annual return.`
@@ -991,8 +991,11 @@
></label
>
<label class="field check-field"
><Checkbox name="includeRecurring" value="on" checked={active.includeRecurring !== 0} /> Include
recurring items from next month</label
><Checkbox
name="includeRecurring"
value="on"
checked={active.includeRecurring !== false}
/> Include recurring items from next month</label
>
{:else if mode === 'settings'}
<input type="hidden" name="kind" value={active.kind ?? selectedUtility} />