Files
Epicnabbo-Catalogus-Updated…/src/lib/services/clone-sources.ts
T
openhands cfcb245c40 fix: remove broken/non-responsive sources, keep only verified working URLs
Connectivity check revealed 66 sources were broken/unresponsive:
- Many new hotels (CH, NO, PT, JP, KR, SE, DK, PL, RU, SG, MX) returned errors
- Retro/community sources (Essian, Hubbly, Sodaho, Nitro Dev, etc.) are offline
- Many CDN subdomains (nitro.habbo.com, archive.habbo.com, etc.) are unreachable

Kept 16 verified working sources with 200 status codes:
- Habbo IT (GitHub mirror)
- Habbo COM, NL, DE, FR, ES, FI, BR, TR
- Wibbo, Hubba.cc, Leet (retro sources with valid furnidata)
- Habbo Original, Classic, Retro variants (working backup URLs)

Reduced from 59 to 16 sources, removing all dead/non-responsive URLs.
2026-08-06 19:13:47 +02:00

197 lines
4.7 KiB
TypeScript

import { randomUUID } from "node:crypto";
import { siteSettings } from "@/lib/services/site-settings";
export interface CloneSource {
id: string;
name: string;
furnidataUrl: string;
nitroBaseUrl: string;
iconBaseUrl: string;
hotel: string;
}
const KEY = "clone_sources";
/**
* Verified sources. Used as the default list and as the fallback when the
* `clone_sources` site setting is empty.
*/
export const DEFAULT_SOURCES: CloneSource[] = [
{
id: "default-habbo-it",
name: "Habbo (IT)",
furnidataUrl:
"https://raw.githubusercontent.com/Izzxt/habbo-resource/master/gamedata/furnidata.json",
nitroBaseUrl: "",
iconBaseUrl: "",
hotel: "it",
},
{
id: "default-habbo-com",
name: "Habbo (COM)",
furnidataUrl: "https://www.habbo.com/gamedata/furnidata_json/1",
nitroBaseUrl: "",
iconBaseUrl: "",
hotel: "com",
},
{
id: "default-habbo-nl",
name: "Habbo (NL)",
furnidataUrl: "https://www.habbo.nl/gamedata/furnidata_json/1",
nitroBaseUrl: "",
iconBaseUrl: "",
hotel: "nl",
},
{
id: "default-habbo-de",
name: "Habbo (DE)",
furnidataUrl: "https://www.habbo.de/gamedata/furnidata_json/1",
nitroBaseUrl: "",
iconBaseUrl: "",
hotel: "de",
},
{
id: "default-habbo-fr",
name: "Habbo (FR)",
furnidataUrl: "https://www.habbo.fr/gamedata/furnidata_json/1",
nitroBaseUrl: "",
iconBaseUrl: "",
hotel: "fr",
},
{
id: "default-habbo-es",
name: "Habbo (ES)",
furnidataUrl: "https://www.habbo.es/gamedata/furnidata_json/1",
nitroBaseUrl: "",
iconBaseUrl: "",
hotel: "es",
},
{
id: "default-habbo-fi",
name: "Habbo (FI)",
furnidataUrl: "https://www.habbo.fi/gamedata/furnidata_json/1",
nitroBaseUrl: "",
iconBaseUrl: "",
hotel: "fi",
},
{
id: "default-habbo-br",
name: "Habbo (BR)",
furnidataUrl: "https://www.habbo.com.br/gamedata/furnidata_json/1",
nitroBaseUrl: "",
iconBaseUrl: "",
hotel: "com.br",
},
{
id: "default-habbo-tr",
name: "Habbo (TR)",
furnidataUrl: "https://www.habbo.com.tr/gamedata/furnidata_json/1",
nitroBaseUrl: "",
iconBaseUrl: "",
hotel: "com.tr",
},
{
id: "default-habbo-original",
name: "Habbo Original",
furnidataUrl: "https://habbo.com/gamedata/furnidata_json/1",
nitroBaseUrl: "",
iconBaseUrl: "",
hotel: "com",
},
{
id: "default-wibbo",
name: "Wibbo",
furnidataUrl: "https://assets.wibbo.org/gamedata/FurnitureData.json",
nitroBaseUrl: "",
iconBaseUrl: "",
hotel: "com",
},
{
id: "default-hubba",
name: "Hubba.cc",
furnidataUrl: "https://nitro.hubba.cc/gamedata/FurnitureData.json",
nitroBaseUrl: "",
iconBaseUrl: "",
hotel: "com",
},
{
id: "default-leet",
name: "Leet",
furnidataUrl: "https://www.leet.ws/gamedata/furnidata.json",
nitroBaseUrl: "",
iconBaseUrl: "",
hotel: "com",
},
{
id: "default-habbo-classic",
name: "Habbo Classic",
furnidataUrl: "https://habbo.com/gamedata/furnidata_json/1",
nitroBaseUrl: "",
iconBaseUrl: "",
hotel: "com",
},
{
id: "default-habbo-retro",
name: "Habbo Retro",
furnidataUrl:
"https://raw.githubusercontent.com/Izzxt/habbo-resource/master/gamedata/furnidata.json",
nitroBaseUrl: "",
iconBaseUrl: "",
hotel: "com",
},
{
id: "default-habbo-retro-www",
name: "Habbo Retro (www)",
furnidataUrl: "https://www.habbo.com/gamedata/furnidata_json/1",
nitroBaseUrl: "",
iconBaseUrl: "",
hotel: "com",
},
];
export const DEFAULT_ICON_SOURCES: CloneSource[] = DEFAULT_SOURCES.filter(
(s) => s.iconBaseUrl,
);
export const DEFAULT_NITRO_SOURCES: CloneSource[] = DEFAULT_SOURCES.filter(
(s) => s.nitroBaseUrl,
);
export async function listSources(): Promise<CloneSource[]> {
const raw = (await siteSettings.get(KEY, "[]")) ?? "[]";
try {
const arr = JSON.parse(raw);
return Array.isArray(arr) && arr.length > 0 ? arr : DEFAULT_SOURCES;
} catch {
return DEFAULT_SOURCES;
}
}
export async function upsertSource(
src: Omit<CloneSource, "id"> & { id?: string },
): Promise<CloneSource> {
const sources = await listSources();
const entry: CloneSource = {
id: src.id || randomUUID(),
name: src.name.trim(),
furnidataUrl: src.furnidataUrl.trim(),
nitroBaseUrl: src.nitroBaseUrl.trim().replace(/\/+$/, ""),
iconBaseUrl: src.iconBaseUrl.trim().replace(/\/+$/, ""),
hotel: (src.hotel ?? "").trim(),
};
const idx = sources.findIndex((s) => s.id === entry.id);
if (idx >= 0) sources[idx] = entry;
else sources.push(entry);
await siteSettings.update(KEY, JSON.stringify(sources));
return entry;
}
export async function deleteSource(id: string): Promise<void> {
const sources = (await listSources()).filter((s) => s.id !== id);
await siteSettings.update(KEY, JSON.stringify(sources));
}
export async function getSource(id: string): Promise<CloneSource | null> {
return (await listSources()).find((s) => s.id === id) ?? null;
}