Files
homefeed/backend/src/storage/db/settings.ts
T
Claude 6d5d74b9bb Add PoE2 sidebar module: currency exchange watchlist
Tracks Path of Exile 2 currency values via poe.ninja's public economy
API, mirroring the existing Weather/Stocks sidebar modules. Always
follows the current challenge league (auto-detected, no admin picker).
Admin browses and picks currencies from a live list rather than typing
symbols, since currency ids are opaque. Change % is a 7-day window,
labeled accordingly to avoid the same interval ambiguity Stocks had.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014c1L8ghNBFjfiH64UMViP8
2026-07-25 01:54:55 +00:00

114 lines
4.3 KiB
TypeScript

import { db } from './index.js';
import type { GlobalSettings } from './types.js';
function rowToSettings(row: any): GlobalSettings {
return {
mergeStrictness: row.merge_strictness,
defaultPollIntervalMinutes: row.default_poll_interval_minutes,
holdBeforePublishMinutes: row.hold_before_publish_minutes,
tagDedupThreshold: row.tag_dedup_threshold,
tagExpiryDays: row.tag_expiry_days,
followUpMinHoursSinceLast: row.follow_up_min_hours_since_last,
followUpMinNewSources: row.follow_up_min_new_sources,
aiServiceHost: row.ai_service_host,
aiServicePort: row.ai_service_port,
selectedModels: JSON.parse(row.selected_models),
nitterMediaMode: row.nitter_media_mode,
fxtwitterBaseUrl: row.fxtwitter_base_url,
telegramMediaMode: row.telegram_media_mode,
retention: {
publishedArticleMaxAgeDays: row.published_article_max_age_days,
rawItemMaxAgeDays: row.raw_item_max_age_days,
storageCapEnabled: !!row.storage_cap_enabled,
storageCapValue: row.storage_cap_value,
storageCapUnit: row.storage_cap_unit
},
weather: {
locationName: row.weather_location_name,
latitude: row.weather_latitude,
longitude: row.weather_longitude,
unit: row.weather_unit,
windUnit: row.weather_wind_unit,
pressureUnit: row.weather_pressure_unit,
// Unlike retention, this is genuinely absent pre-first-poll (and pre-location-config) — null-safe parse.
current: row.weather_current ? JSON.parse(row.weather_current) : null,
hourly: JSON.parse(row.weather_hourly),
daily: JSON.parse(row.weather_daily),
alerts: JSON.parse(row.weather_alerts),
updatedAt: row.weather_updated_at
},
poe2: {
leagueId: row.poe2_league_id,
leagueName: row.poe2_league_name,
primaryCurrencyName: row.poe2_primary_currency_name,
updatedAt: row.poe2_updated_at
}
};
}
export function getSettings(): GlobalSettings {
const row = db.prepare('SELECT * FROM global_settings WHERE id = 1').get();
return rowToSettings(row);
}
export function updateSettings(patch: Partial<GlobalSettings>): GlobalSettings {
const current = getSettings();
const merged: GlobalSettings = {
...current,
...patch,
retention: { ...current.retention, ...(patch.retention ?? {}) },
selectedModels: { ...current.selectedModels, ...(patch.selectedModels ?? {}) },
weather: { ...current.weather, ...(patch.weather ?? {}) },
poe2: { ...current.poe2, ...(patch.poe2 ?? {}) }
};
db.prepare(
`UPDATE global_settings SET
merge_strictness=?, default_poll_interval_minutes=?, hold_before_publish_minutes=?,
tag_dedup_threshold=?, tag_expiry_days=?, follow_up_min_hours_since_last=?, follow_up_min_new_sources=?,
ai_service_host=?, ai_service_port=?, selected_models=?,
nitter_media_mode=?, fxtwitter_base_url=?, telegram_media_mode=?,
published_article_max_age_days=?, raw_item_max_age_days=?,
storage_cap_enabled=?, storage_cap_value=?, storage_cap_unit=?,
weather_location_name=?, weather_latitude=?, weather_longitude=?, weather_unit=?,
weather_wind_unit=?, weather_pressure_unit=?,
weather_current=?, weather_hourly=?, weather_daily=?, weather_alerts=?, weather_updated_at=?,
poe2_league_id=?, poe2_league_name=?, poe2_primary_currency_name=?, poe2_updated_at=?
WHERE id = 1`
).run(
merged.mergeStrictness,
merged.defaultPollIntervalMinutes,
merged.holdBeforePublishMinutes,
merged.tagDedupThreshold,
merged.tagExpiryDays,
merged.followUpMinHoursSinceLast,
merged.followUpMinNewSources,
merged.aiServiceHost,
merged.aiServicePort,
JSON.stringify(merged.selectedModels),
merged.nitterMediaMode,
merged.fxtwitterBaseUrl,
merged.telegramMediaMode,
merged.retention.publishedArticleMaxAgeDays,
merged.retention.rawItemMaxAgeDays,
merged.retention.storageCapEnabled ? 1 : 0,
merged.retention.storageCapValue,
merged.retention.storageCapUnit,
merged.weather.locationName,
merged.weather.latitude,
merged.weather.longitude,
merged.weather.unit,
merged.weather.windUnit,
merged.weather.pressureUnit,
merged.weather.current ? JSON.stringify(merged.weather.current) : null,
JSON.stringify(merged.weather.hourly),
JSON.stringify(merged.weather.daily),
JSON.stringify(merged.weather.alerts),
merged.weather.updatedAt,
merged.poe2.leagueId,
merged.poe2.leagueName,
merged.poe2.primaryCurrencyName,
merged.poe2.updatedAt
);
return getSettings();
}