← back to AsSeenInMovies
asseeninmovies: GET /sitemap.xml + /robots.txt — XML sitemap with 6 static pages + top-10k movies (popularity-ranked) + top-10k people + all verified SPOTTED anchors; 10-min in-memory cache; robots disallows /spotted/queue + /api/. Closes the asim-vs-sod sitemap parity gap.
bbaae4b5d2af9c01caa185f3785320db8e090ed0 · 2026-05-12 18:14:32 -0700 · SteveStudio2
Files touched
Diff
commit bbaae4b5d2af9c01caa185f3785320db8e090ed0
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date: Tue May 12 18:14:32 2026 -0700
asseeninmovies: GET /sitemap.xml + /robots.txt — XML sitemap with 6 static pages + top-10k movies (popularity-ranked) + top-10k people + all verified SPOTTED anchors; 10-min in-memory cache; robots disallows /spotted/queue + /api/. Closes the asim-vs-sod sitemap parity gap.
---
server.js | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 59 insertions(+)
diff --git a/server.js b/server.js
index 5fa4af1..151f175 100644
--- a/server.js
+++ b/server.js
@@ -1491,6 +1491,65 @@ app.get('/', async (_req, res) => {
</body></html>`);
});
+// ────────────────────────────────────────────────────────────────────
+// /sitemap.xml + /robots.txt — SEO surface. asseeninmovies has 290k movies
+// and 61k people; we surface the static pages, then a popularity-capped
+// slice of detail pages (top ~10k each by popularity / credit count) plus
+// all verified SPOTTED pages. Sitemap protocol max is 50,000 URLs per
+// file — we sit well under. Cached in-memory for 10 min so 290k count(*)
+// doesn't hit on every crawler probe.
+// ────────────────────────────────────────────────────────────────────
+let _sitemapCache = { at: 0, xml: null };
+const SITEMAP_TTL_MS = 10 * 60 * 1000;
+app.get('/sitemap.xml', async (_req, res) => {
+ const now = Date.now();
+ if (_sitemapCache.xml && (now - _sitemapCache.at) < SITEMAP_TTL_MS) {
+ return res.type('application/xml').send(_sitemapCache.xml);
+ }
+ try {
+ const base = process.env.ASIM_PUBLIC_URL || 'https://asseeninmovies.com';
+ const today = new Date().toISOString().slice(0, 10);
+ const STATIC = [
+ ['/', '1.0', 'weekly'],
+ ['/movies', '0.9', 'weekly'],
+ ['/people', '0.9', 'weekly'],
+ ['/spotted', '0.9', 'daily'],
+ ['/spotted/submit', '0.4', 'monthly'],
+ ['/search', '0.5', 'weekly'],
+ ];
+ const [moviesR, peopleR, spottedR] = await Promise.all([
+ pool.query(`SELECT id FROM asim_movies WHERE poster_url IS NOT NULL ORDER BY popularity DESC NULLS LAST, id DESC LIMIT 10000`),
+ pool.query(`SELECT id FROM asim_people WHERE headshot_url IS NOT NULL ORDER BY id LIMIT 10000`),
+ pool.query(`SELECT id, movie_id FROM asim_spotted WHERE verified_at IS NOT NULL AND (verified_by IS NULL OR verified_by NOT LIKE 'rejected%')`)
+ ]);
+ const urls = [];
+ for (const [path, pri, cf] of STATIC) {
+ urls.push(`<url><loc>${base}${path}</loc><lastmod>${today}</lastmod><changefreq>${cf}</changefreq><priority>${pri}</priority></url>`);
+ }
+ for (const m of moviesR.rows) {
+ urls.push(`<url><loc>${base}/movie/${m.id}</loc><lastmod>${today}</lastmod><changefreq>monthly</changefreq><priority>0.6</priority></url>`);
+ }
+ for (const p of peopleR.rows) {
+ urls.push(`<url><loc>${base}/person/${p.id}</loc><lastmod>${today}</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>`);
+ }
+ // Each verified spotted lives under its movie's page — surface the movie URL
+ // anchored to #spotted-<id> so crawlers can discover them individually.
+ for (const s of spottedR.rows) {
+ urls.push(`<url><loc>${base}/movie/${s.movie_id}#spotted-${s.id}</loc><lastmod>${today}</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url>`);
+ }
+ const xml = `<?xml version="1.0" encoding="UTF-8"?>\n<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">\n ${urls.join('\n ')}\n</urlset>`;
+ _sitemapCache = { at: now, xml };
+ res.type('application/xml').send(xml);
+ } catch (e) {
+ res.status(500).type('text/plain').send(`error: ${e.message}`);
+ }
+});
+
+app.get('/robots.txt', (_req, res) => {
+ const base = process.env.ASIM_PUBLIC_URL || 'https://asseeninmovies.com';
+ res.type('text/plain').send(`User-agent: *\nAllow: /\nDisallow: /spotted/queue\nDisallow: /api/\nSitemap: ${base}/sitemap.xml\n`);
+});
+
app.listen(PORT, '127.0.0.1', async () => {
await ensureSchema();
console.log(`asseeninmovies → http://127.0.0.1:${PORT}`);
← 08671ef asseeninmovies: /movie/:id + /person/:id — parallelize credi
·
back to AsSeenInMovies
·
asseeninmovies: SEO surface — inline SVG favicon (data: URI, b59fa66 →